package com.fasterxml.jackson.databind.ser.std;

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
import com.fasterxml.jackson.databind.type.ReferenceType;
import com.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.fasterxml.jackson.databind.util.NameTransformer;

Base implementation for values of ReferenceType. Implements most of functionality, only leaving couple of abstract methods for sub-classes to implement
Since:2.8
/** * Base implementation for values of {@link ReferenceType}. * Implements most of functionality, only leaving couple of abstract * methods for sub-classes to implement * * @since 2.8 */
public abstract class ReferenceTypeSerializer<T> extends StdSerializer<T> implements ContextualSerializer { private static final long serialVersionUID = 1L;
Since:2.9
/** * @since 2.9 */
public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
Value type
/** * Value type */
protected final JavaType _referredType; protected final BeanProperty _property;
Type serializer used for values, if any.
/** * Type serializer used for values, if any. */
protected final TypeSerializer _valueTypeSerializer;
Serializer for content values, if statically known.
/** * Serializer for content values, if statically known. */
protected final JsonSerializer<Object> _valueSerializer;
In case of unwrapping, need name transformer.
/** * In case of unwrapping, need name transformer. */
protected final NameTransformer _unwrapper;
If element type cannot be statically determined, mapping from runtime type to serializer is handled using this object
/** * If element type cannot be statically determined, mapping from * runtime type to serializer is handled using this object */
protected transient PropertySerializerMap _dynamicSerializers; /* /********************************************************** /* Config settings, filtering /********************************************************** */
Value that indicates suppression mechanism to use for values contained; either "filter" (of which equals() is called), or marker value of ReferenceTypeSerializer<T>.MARKER_FOR_EMPTY, or null to indicate no filtering for non-null values. Note that inclusion value for Map instance itself is handled by caller (POJO property that refers to the Map value).
Since:2.9
/** * Value that indicates suppression mechanism to use for <b>values contained</b>; * either "filter" (of which <code>equals()</code> is called), or marker * value of {@link #MARKER_FOR_EMPTY}, or null to indicate no filtering for * non-null values. * Note that inclusion value for Map instance itself is handled by caller (POJO * property that refers to the Map value). * * @since 2.9 */
protected final Object _suppressableValue;
Flag that indicates what to do with `null` values, distinct from handling of ReferenceTypeSerializer<T>._suppressableValue
Since:2.9
/** * Flag that indicates what to do with `null` values, distinct from * handling of {@link #_suppressableValue} * * @since 2.9 */
protected final boolean _suppressNulls; /* /********************************************************** /* Constructors, factory methods /********************************************************** */ public ReferenceTypeSerializer(ReferenceType fullType, boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> ser) { super(fullType); _referredType = fullType.getReferencedType(); _property = null; _valueTypeSerializer = vts; _valueSerializer = ser; _unwrapper = null; _suppressableValue = null; _suppressNulls = false; _dynamicSerializers = PropertySerializerMap.emptyForProperties(); } @SuppressWarnings("unchecked") protected ReferenceTypeSerializer(ReferenceTypeSerializer<?> base, BeanProperty property, TypeSerializer vts, JsonSerializer<?> valueSer, NameTransformer unwrapper, Object suppressableValue, boolean suppressNulls) { super(base); _referredType = base._referredType; // [databind#2181]: may not be safe to reuse, start from empty _dynamicSerializers = PropertySerializerMap.emptyForProperties(); _property = property; _valueTypeSerializer = vts; _valueSerializer = (JsonSerializer<Object>) valueSer; _unwrapper = unwrapper; _suppressableValue = suppressableValue; _suppressNulls = suppressNulls; } @Override public JsonSerializer<T> unwrappingSerializer(NameTransformer transformer) { JsonSerializer<Object> valueSer = _valueSerializer; if (valueSer != null) { valueSer = valueSer.unwrappingSerializer(transformer); } NameTransformer unwrapper = (_unwrapper == null) ? transformer : NameTransformer.chainedTransformer(transformer, _unwrapper); if ((_valueSerializer == valueSer) && (_unwrapper == unwrapper)) { return this; } return withResolved(_property, _valueTypeSerializer, valueSer, unwrapper); } /* /********************************************************** /* Abstract methods to implement /********************************************************** */
Mutant factory method called when changes are needed; should construct newly configured instance with new values as indicated.

NOTE: caller has verified that there are changes, so implementations need NOT check if a new instance is needed.

Since:2.9
/** * Mutant factory method called when changes are needed; should construct * newly configured instance with new values as indicated. *<p> * NOTE: caller has verified that there are changes, so implementations * need NOT check if a new instance is needed. * * @since 2.9 */
protected abstract ReferenceTypeSerializer<T> withResolved(BeanProperty prop, TypeSerializer vts, JsonSerializer<?> valueSer, NameTransformer unwrapper);
Mutant factory method called to create a differently constructed instance, specifically with different exclusion rules for contained value.

NOTE: caller has verified that there are changes, so implementations need NOT check if a new instance is needed.

Since:2.9
/** * Mutant factory method called to create a differently constructed instance, * specifically with different exclusion rules for contained value. *<p> * NOTE: caller has verified that there are changes, so implementations * need NOT check if a new instance is needed. * * @since 2.9 */
public abstract ReferenceTypeSerializer<T> withContentInclusion(Object suppressableValue, boolean suppressNulls);
Method called to see if there is a value present or not. Note that value itself may still be `null`, even if present, if referential type allows three states (absent, present-null, present-non-null); some only allow two (absent, present-non-null).
/** * Method called to see if there is a value present or not. * Note that value itself may still be `null`, even if present, * if referential type allows three states (absent, present-null, * present-non-null); some only allow two (absent, present-non-null). */
protected abstract boolean _isValuePresent(T value); protected abstract Object _getReferenced(T value); protected abstract Object _getReferencedIfPresent(T value); /* /********************************************************** /* Contextualization (support for property annotations) /********************************************************** */ @Override public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException { TypeSerializer typeSer = _valueTypeSerializer; if (typeSer != null) { typeSer = typeSer.forProperty(property); } // First: do we have an annotation override from property? JsonSerializer<?> ser = findAnnotatedContentSerializer(provider, property); if (ser == null) { // If not, use whatever was configured by type ser = _valueSerializer; if (ser == null) { // A few conditions needed to be able to fetch serializer here: if (_useStatic(provider, property, _referredType)) { ser = _findSerializer(provider, _referredType, property); } } else { ser = provider.handlePrimaryContextualization(ser, property); } } // First, resolve wrt property, resolved serializers ReferenceTypeSerializer<?> refSer; if ((_property == property) && (_valueTypeSerializer == typeSer) && (_valueSerializer == ser)) { refSer = this; } else { refSer = withResolved(property, typeSer, ser, _unwrapper); } // and then see if we have property-inclusion overrides if (property != null) { JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), handledType()); if (inclV != null) { JsonInclude.Include incl = inclV.getContentInclusion(); if (incl != JsonInclude.Include.USE_DEFAULTS) { Object valueToSuppress; boolean suppressNulls; switch (incl) { case NON_DEFAULT: valueToSuppress = BeanUtil.getDefaultValue(_referredType); suppressNulls = true; if (valueToSuppress != null) { if (valueToSuppress.getClass().isArray()) { valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress); } } break; case NON_ABSENT: suppressNulls = true; valueToSuppress = _referredType.isReferenceType() ? MARKER_FOR_EMPTY : null; break; case NON_EMPTY: suppressNulls = true; valueToSuppress = MARKER_FOR_EMPTY; break; case CUSTOM: valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter()); if (valueToSuppress == null) { // is this legal? suppressNulls = true; } else { suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress); } break; case NON_NULL: valueToSuppress = null; suppressNulls = true; break; case ALWAYS: // default default: valueToSuppress = null; suppressNulls = false; break; } if ((_suppressableValue != valueToSuppress) || (_suppressNulls != suppressNulls)) { refSer = refSer.withContentInclusion(valueToSuppress, suppressNulls); } } } } return refSer; } protected boolean _useStatic(SerializerProvider provider, BeanProperty property, JavaType referredType) { // First: no serializer for `Object.class`, must be dynamic if (referredType.isJavaLangObject()) { return false; } // but if type is final, might as well fetch if (referredType.isFinal()) { // or should we allow annotation override? (only if requested...) return true; } // also: if indicated by typing, should be considered static if (referredType.useStaticType()) { return true; } // if neither, maybe explicit annotation? AnnotationIntrospector intr = provider.getAnnotationIntrospector(); if ((intr != null) && (property != null)) { Annotated ann = property.getMember(); if (ann != null) { JsonSerialize.Typing t = intr.findSerializationTyping(property.getMember()); if (t == JsonSerialize.Typing.STATIC) { return true; } if (t == JsonSerialize.Typing.DYNAMIC) { return false; } } } // and finally, may be forced by global static typing (unlikely...) return provider.isEnabled(MapperFeature.USE_STATIC_TYPING); } /* /********************************************************** /* Accessors /********************************************************** */ @Override public boolean isEmpty(SerializerProvider provider, T value) { // First, absent value (note: null check is just sanity check here) if (!_isValuePresent(value)) { return true; } Object contents = _getReferenced(value); if (contents == null) { // possible for explicitly contained `null` return _suppressNulls; } if (_suppressableValue == null) { return false; } JsonSerializer<Object> ser = _valueSerializer; if (ser == null) { try { ser = _findCachedSerializer(provider, contents.getClass()); } catch (JsonMappingException e) { // nasty but necessary throw new RuntimeJsonMappingException(e); } } if (_suppressableValue == MARKER_FOR_EMPTY) { return ser.isEmpty(provider, contents); } return _suppressableValue.equals(contents); } @Override public boolean isUnwrappingSerializer() { return (_unwrapper != null); }
Since:2.9
/** * @since 2.9 */
public JavaType getReferredType() { return _referredType; } /* /********************************************************** /* Serialization methods /********************************************************** */ @Override public void serialize(T ref, JsonGenerator g, SerializerProvider provider) throws IOException { Object value = _getReferencedIfPresent(ref); if (value == null) { if (_unwrapper == null) { provider.defaultSerializeNull(g); } return; } JsonSerializer<Object> ser = _valueSerializer; if (ser == null) { ser = _findCachedSerializer(provider, value.getClass()); } if (_valueTypeSerializer != null) { ser.serializeWithType(value, g, provider, _valueTypeSerializer); } else { ser.serialize(value, g, provider); } } @Override public void serializeWithType(T ref, JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer) throws IOException { Object value = _getReferencedIfPresent(ref); if (value == null) { if (_unwrapper == null) { provider.defaultSerializeNull(g); } return; } // 19-Apr-2016, tatu: In order to basically "skip" the whole wrapper level // (which is what non-polymorphic serialization does too), we will need // to simply delegate call, I think, and NOT try to use it here. // Otherwise apply type-prefix/suffix, then std serialize: /* typeSer.writeTypePrefixForScalar(ref, g); serialize(ref, g, provider); typeSer.writeTypeSuffixForScalar(ref, g); */ JsonSerializer<Object> ser = _valueSerializer; if (ser == null) { ser = _findCachedSerializer(provider, value.getClass()); } ser.serializeWithType(value, g, provider, typeSer); } /* /********************************************************** /* Introspection support /********************************************************** */ @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { JsonSerializer<?> ser = _valueSerializer; if (ser == null) { ser = _findSerializer(visitor.getProvider(), _referredType, _property); if (_unwrapper != null) { ser = ser.unwrappingSerializer(_unwrapper); } } ser.acceptJsonFormatVisitor(visitor, _referredType); } /* /********************************************************** /* Helper methods /********************************************************** */
Helper method that encapsulates logic of retrieving and caching required serializer.
/** * Helper method that encapsulates logic of retrieving and caching required * serializer. */
private final JsonSerializer<Object> _findCachedSerializer(SerializerProvider provider, Class<?> rawType) throws JsonMappingException { JsonSerializer<Object> ser = _dynamicSerializers.serializerFor(rawType); if (ser == null) { // NOTE: call this instead of `map._findAndAddDynamic(...)` (which in turn calls // `findAndAddSecondarySerializer`) since we may need to apply unwrapper // too, before caching. But calls made are the same if (_referredType.hasGenericTypes()) { // [databind#1673] Must ensure we will resolve all available type information // so as not to miss generic declaration of, say, `List<GenericPojo>`... JavaType fullType = provider.constructSpecializedType(_referredType, rawType); ser = provider.findValueSerializer(fullType, _property); } else { ser = provider.findValueSerializer(rawType, _property); } if (_unwrapper != null) { ser = ser.unwrappingSerializer(_unwrapper); } _dynamicSerializers = _dynamicSerializers.newWith(rawType, ser); } return ser; } private final JsonSerializer<Object> _findSerializer(SerializerProvider provider, JavaType type, BeanProperty prop) throws JsonMappingException { // 13-Mar-2017, tatu: Used to call `findTypeValueSerializer()`, but contextualization // not working for that case for some reason // 15-Jan-2017, tatu: ... possibly because we need to access "secondary" serializer, // not primary (primary being one for Reference type itself, not value) // return provider.findTypedValueSerializer(type, true, prop); return provider.findValueSerializer(type, prop); } }