Skip to content

Commit

Permalink
Fix #1889
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jan 13, 2018
1 parent c52b0d1 commit 8d3d3b7
Show file tree
Hide file tree
Showing 56 changed files with 132 additions and 277 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ Versions: 3.x (for earlier see VERSION-2.x)
#1790: Add `createParser` methods in `ObjectMapper`, `ObjectReader`
#1888: Merge `ResolvableSerializer` into `JsonSerializer`, `ResolvableDeserializer`
into `JsonDeserializer`
#1889: Merge `ContextualSerializer` into `JsonSerializer`, `ContextualDeserializer`
into `JsonDeserializer`
- Remove `MappingJsonFactory`
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@
*<p>
* Instances are not typically passed when constructing serializers
* and deserializers, but rather only passed when context
* is known when
* {@link com.fasterxml.jackson.databind.ser.ContextualSerializer} and
* {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}
* resolution occurs (<code>createContextual(...)</code> method is called).
* is known and
* {@link JsonSerializer#createContextual} and
* {@link JsonDeserializer#createContextual} are called.
* References may (need to) be retained by serializers and deserializers,
* especially when further resolving dependent handlers like value
* serializers/deserializers or structured types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,11 @@ public abstract class DeserializationContext
protected transient ContextAttributes _attributes;

/**
* Type of {@link JsonDeserializer} (or, more specifically,
* {@link ContextualDeserializer}) that is being
* contextualized currently.
* Type of {@link JsonDeserializer} on which {@link JsonDeserializer#createContextual}
* is being called currently.
*/
protected LinkedNode<JavaType> _currentType;

/*
/**********************************************************
/* Life-cycle
Expand Down Expand Up @@ -316,13 +315,13 @@ public DeserializationContext setAttribute(Object key, Object value)

/**
* Accessor to {@link JavaType} of currently contextualized
* {@link ContextualDeserializer}, if any.
* {@link JsonDeserializer}, if any.
* This is sometimes useful for generic {@link JsonDeserializer}s that
* do not get passed (or do not retain) type information when being
* constructed: happens for example for deserializers constructed
* from annotations.
*
* @return Type of {@link ContextualDeserializer} being contextualized,
* @return Type of {@link JsonDeserializer} being contextualized,
* if process is on-going; null if not.
*/
public JavaType getContextualType() {
Expand Down Expand Up @@ -755,21 +754,19 @@ public abstract KeyDeserializer keyDeserializerInstance(Annotated annotated,
/**
* Method called for primary property deserializers (ones
* directly created to deserialize values of a POJO property),
* to handle details of resolving
* {@link ContextualDeserializer} with given property context.
* to handle details of calling
* {@link JsonDeserializer#createContextual} with given property context.
*
* @param prop Property for which the given primary deserializer is used; never null.
*
* @since 2.5
*/
public JsonDeserializer<?> handlePrimaryContextualization(JsonDeserializer<?> deser,
BeanProperty prop, JavaType type)
throws JsonMappingException
{
if (deser instanceof ContextualDeserializer) {
if (deser != null) {
_currentType = new LinkedNode<JavaType>(type, _currentType);
try {
deser = ((ContextualDeserializer) deser).createContextual(this, prop);
deser = deser.createContextual(this, prop);
} finally {
_currentType = _currentType.next();
}
Expand All @@ -783,24 +780,22 @@ public JsonDeserializer<?> handlePrimaryContextualization(JsonDeserializer<?> de
* but instead created as a component -- such as value deserializers
* for structured types, or deserializers for root values)
* to handle details of resolving
* {@link ContextualDeserializer} with given property context.
* {@link JsonDeserializer#createContextual} with given property context.
* Given that these deserializers are not directly related to given property
* (or, in case of root value property, to any property), annotations
* accessible may or may not be relevant.
*
* @param prop Property for which deserializer is used, if any; null
* when deserializing root values
*
* @since 2.5
*/
public JsonDeserializer<?> handleSecondaryContextualization(JsonDeserializer<?> deser,
BeanProperty prop, JavaType type)
throws JsonMappingException
{
if (deser instanceof ContextualDeserializer) {
if (deser != null) {
_currentType = new LinkedNode<JavaType>(type, _currentType);
try {
deser = ((ContextualDeserializer) deser).createContextual(this, prop);
deser =deser.createContextual(this, prop);
} finally {
_currentType = _currentType.next();
}
Expand Down
37 changes: 31 additions & 6 deletions src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@
*<p>
* In addition, to support per-property annotations (to configure aspects
* of deserialization on per-property basis), deserializers may want
* to implement
* {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer},
* which allows specialization of deserializers: call to
* {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual}
* is passed information on property, and can create a newly configured
* to override
* {@link #createContextual} which allows specialization of deserializers:
* it is passed information on property, and can create a newly configured
* deserializer for handling that particular property.
*<br />
* Resolution of deserializers occurs before contextualization.
Expand All @@ -44,7 +42,8 @@ public abstract class JsonDeserializer<T>
{
/*
/**********************************************************
/* Initialization, with former `ResolvableDeserializer`
/* Initialization, with former `ResolvableDeserializer`,
/* `ContextualDeserializer`
/**********************************************************
*/

Expand All @@ -62,6 +61,32 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException {
// Default implementation does nothing
}

/**
* Method called to see if a different (or differently configured) deserializer
* is needed to deserialize values of specified property.
* Note that instance that this method is called on is typically shared one and
* as a result method should <b>NOT</b> modify this instance but rather construct
* and return a new instance. This instance should only be returned as-is, in case
* it is already suitable for use.
*
* @param ctxt Deserialization context to access configuration, additional
* deserializers that may be needed by this deserializer
* @param property Method, field or constructor parameter that represents the property
* (and is used to assign deserialized value).
* Should be available; but there may be cases where caller cannot provide it and
* null is passed instead (in which case impls usually pass 'this' deserializer as is)
*
* @return Deserializer to use for deserializing values of specified property;
* may be this instance or a new instance.
*
* @throws JsonMappingException
*/
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException {
// default implementation returns instance unmodified
return this;
}

/*
/**********************************************************
/* Main deserialization methods
Expand Down
73 changes: 53 additions & 20 deletions src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,9 @@
* Abstract class that defines API used by {@link ObjectMapper} (and
* other chained {@link JsonSerializer}s too) to serialize Objects of
* arbitrary types into JSON, using provided {@link JsonGenerator}.
* {@link com.fasterxml.jackson.databind.ser.std.StdSerializer} instead
* of this class, since it will implement many of optional
* methods of this class.
*<p>
* NOTE: various <code>serialize</code> methods are never (to be) called
* with null values -- caller <b>must</b> handle null values, usually
* by calling {@link SerializerProvider#findNullValueSerializer} to obtain
* serializer to use.
* This also means that custom serializers cannot be directly used to change
* the output to produce when serializing null values.
* Note that although API is defined here, custom serializer implementations
* should almost always be based on {@link com.fasterxml.jackson.databind.ser.std.StdSerializer}
* since it will implement many of optional methods of this class.
*<p>
* If serializer is an aggregate one -- meaning it delegates handling of some
* of its contents by using other serializer(s) -- it typically also needs
Expand All @@ -35,23 +28,34 @@
* resolution of secondary serializers (which may have cyclic link back
* to serializer itself, directly or indirectly).
*<p>
* In addition, to support per-property annotations (to configure aspects
* of serialization on per-property basis), serializers may want
* to implement
* {@link com.fasterxml.jackson.databind.ser.ContextualSerializer},
* which allows specialization of serializers: call to
* {@link com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual}
* is passed information on property, and can create a newly configured
* serializer for handling that particular property.
* Initialization of serializers is handled by two main methods:
*<ol>
* <li>{@link #resolve}: called after instance is configured to be used for specific type,
* but without yet knowing property it will be used for (or, in case of root values, without property).
* Method needs to be implemented for serializers that may work on cyclic types, and specifically
* is implemented by standard POJO serializer ({@code BeanSerializer}). It is usually not needed for
* container types as their type definitions are not cyclic, unlike some POJO types.
* <li>{@link #createContextual}: called on resolved instance (whether newly created, or found via cache),
* when serializer is to be used for specific property, or as root value serializer (no referring property).
* It is used to apply annotations from property accessors (getter, field), and may also be used for resolving
* nested types for container serializers (such as ones for {@link java.util.Collection}s).
* </ol>
* Caching of serializers occurs after {@link #resolve} is called: cached instances are not contextual.
*<p>
* Resolution of serializers occurs before contextualization.
* NOTE: various <code>serialize</code> methods are never (to be) called
* with null values -- caller <b>must</b> handle null values, usually
* by calling {@link SerializerProvider#findNullValueSerializer} to obtain
* serializer to use.
* This also means that custom serializers cannot be directly used to change
* the output to produce when serializing null values.
*/
public abstract class JsonSerializer<T>
implements JsonFormatVisitable
{
/*
/**********************************************************
/* Initialization, with former `ResolvableSerializer`
/* Initialization, with former `ResolvableSerializer`,
/* `ContextualSerializer`.
/**********************************************************
*/

Expand All @@ -71,6 +75,35 @@ public void resolve(SerializerProvider provider) throws JsonMappingException {
// Default implementation does nothing
}

/**
* Method called to see if a different (or differently configured) serializer
* is needed to serialize values of specified property (or, for root values, in which
* case `null` is passed).
* Note that instance that this method is called on is typically shared one and
* as a result method should <b>NOT</b> modify this instance but rather construct
* and return a new instance. This instance should only be returned as-is, in case
* it is already suitable for use.
*<p>
* Note that method is only called once per POJO property, and for the first usage as root
* value serializer; it is not called for every serialization, as doing that would have
* significant performance impact; most serializers cache contextual instances for future
* use.
*
* @param prov Serializer provider to use for accessing config, other serializers
* @param property Property (defined by one or more accessors - field or method - used
* for accessing logical property value) for which serializer is used to be used;
* or, `null` for root value (or in cases where caller does not have this information,
* which is handled as root value case).
*
* @return Serializer to use for serializing values of specified property;
* may be this instance or a new instance.
*/
public JsonSerializer<?> createContextual(SerializerProvider prov,
BeanProperty property) throws JsonMappingException {
// default implementation returns instance unmodified
return this;
}

/*
/**********************************************************
/* Fluent factory methods for constructing decorated versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.cfg.GeneratorSettings;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
import com.fasterxml.jackson.databind.introspect.Annotated;
Expand Down Expand Up @@ -1021,9 +1020,8 @@ public boolean isUnknownTypeSerializer(JsonSerializer<?> ser) {
* Method that can be called to construct and configure serializer instance,
* either given a {@link Class} to instantiate (with default constructor),
* or an uninitialized serializer instance.
* Either way, serialize will be properly resolved
* (via {@link com.fasterxml.jackson.databind.JsonSerializer#resolve}) and/or contextualized
* (via {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}) as necessary.
* Either way, serializer will be properly resolved
* (via {@link com.fasterxml.jackson.databind.JsonSerializer#resolve}).
*
* @param annotated Annotated entity that contained definition
* @param serDef Serializer definition: either an instance or class
Expand All @@ -1039,8 +1037,6 @@ public abstract JsonSerializer<Object> serializerInstance(Annotated annotated,
*
* @param forProperty (optional) If filter is created for a property, that property;
* `null` if filter created via defaulting, global or per-type.
*
* @since 2.9
*/
public abstract Object includeFilterInstance(BeanPropertyDefinition forProperty,
Class<?> filterClass)
Expand All @@ -1049,8 +1045,6 @@ public abstract Object includeFilterInstance(BeanPropertyDefinition forProperty,
/**
* Follow-up method that may be called after calling {@link #includeFilterInstance},
* to check handling of `null` values by the filter.
*
* @since 2.9
*/
public abstract boolean includeFilterSuppressNulls(Object filter)
throws JsonMappingException;
Expand All @@ -1064,8 +1058,8 @@ public abstract boolean includeFilterSuppressNulls(Object filter)
/**
* Method called for primary property serializers (ones
* directly created to serialize values of a POJO property),
* to handle details of resolving
* {@link ContextualSerializer} with given property context.
* to handle details of contextualization, calling
* {@link JsonSerializer#createContextual(SerializerProvider, BeanProperty)} with given property context.
*
* @param property Property for which the given primary serializer is used; never null.
*/
Expand All @@ -1074,9 +1068,7 @@ public JsonSerializer<?> handlePrimaryContextualization(JsonSerializer<?> ser,
throws JsonMappingException
{
if (ser != null) {
if (ser instanceof ContextualSerializer) {
ser = ((ContextualSerializer) ser).createContextual(this, property);
}
ser = ser.createContextual(this, property);
}
return ser;
}
Expand All @@ -1086,29 +1078,25 @@ public JsonSerializer<?> handlePrimaryContextualization(JsonSerializer<?> ser,
* NOT directly created to serialize values of a POJO property
* but instead created as a dependant serializer -- such as value serializers
* for structured types, or serializers for root values)
* to handle details of resolving
* {@link ContextualDeserializer} with given property context.
* to handle details of contextualization, calling
* {@link JsonSerializer#createContextual(SerializerProvider, BeanProperty)} with given property context.
* Given that these serializers are not directly related to given property
* (or, in case of root value property, to any property), annotations
* accessible may or may not be relevant.
*
* @param property Property for which serializer is used, if any; null
* when deserializing root values
*
* @since 2.3
*/
public JsonSerializer<?> handleSecondaryContextualization(JsonSerializer<?> ser,
BeanProperty property)
throws JsonMappingException
{
if (ser != null) {
if (ser instanceof ContextualSerializer) {
ser = ((ContextualSerializer) ser).createContextual(this, property);
}
ser = ser.createContextual(this, property);
}
return ser;
}

/*
/********************************************************
/* Convenience methods for serializing using default methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
*/
public class AbstractDeserializer
extends JsonDeserializer<Object>
implements ContextualDeserializer, // since 2.9
java.io.Serializable
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
*/
public abstract class BeanDeserializerBase
extends StdDeserializer<Object>
implements ContextualDeserializer,
ValueInstantiator.Gettable,
implements ValueInstantiator.Gettable,
java.io.Serializable
{
private static final long serialVersionUID = 1;
Expand Down
Loading

0 comments on commit 8d3d3b7

Please sign in to comment.