PairSerializer.java

package top.infra.jackson2.ser;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
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.ser.std.StdSerializer;

import org.apache.commons.lang3.tuple.Pair;

import java.io.IOException;
import java.util.Map;

/**
 * see: {@link com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer}.
 */
public class PairSerializer extends StdSerializer<Pair<?, ?>> implements ContextualSerializer {

    /**
     * property being serialized with this instance
     */
    protected final BeanProperty _property;

    /**
     * Whether static types should be used for serialization of values
     * or not (if not, dynamic runtime type is used)
     */
    protected final boolean _staticTyping;

    protected final JavaType _entryType, _keyType, _valueType;
    /**
     * keyTypeSerializer, Type identifier serializer used for keys, if any.
     */
    protected final TypeSerializer _kts;
    /**
     * valueTypeSerializer, Type identifier serializer used for values, if any.
     */
    protected final TypeSerializer _vts;
    /**
     * Key serializer to use, if it can be statically determined
     */
    protected JsonSerializer<Object> _ks;
    /**
     * Value serializer to use, if it can be statically determined
     */
    protected JsonSerializer<Object> _vs;
    /**
     * dynamicKeySerializers, If key type can not be statically determined, mapping from
     * runtime key types to serializers are stored in this object.
     */
    protected PropertySerializerMap _dks;
    /**
     * dynamicValueSerializers, If value type can not be statically determined, mapping from
     * runtime value types to serializers are stored in this object.
     */
    protected PropertySerializerMap _dvs;

    public PairSerializer(
        final JavaType type, final JavaType keyType, final JavaType valueType,
        final boolean staticTyping,
        final TypeSerializer kts, final TypeSerializer vts,
        final BeanProperty property
    ) {
        super(type);
        this._entryType = type;
        this._keyType = keyType;
        this._valueType = valueType;
        this._staticTyping = staticTyping;
        this._kts = kts;
        this._vts = vts;
        this._dks = PropertySerializerMap.emptyForProperties();
        this._dvs = PropertySerializerMap.emptyForProperties();
        this._property = property;
    }

    @SuppressWarnings("unchecked")
    protected PairSerializer(
        final PairSerializer src,
        final BeanProperty property,
        final TypeSerializer vts,
        final JsonSerializer<?> keySer,
        final JsonSerializer<?> valueSer
    ) {
        super(Map.class, false);
        this._entryType = src._entryType;
        this._keyType = src._keyType;
        this._valueType = src._valueType;
        this._staticTyping = src._staticTyping;
        this._kts = src._kts;
        this._vts = src._vts;
        this._ks = (JsonSerializer<Object>) keySer;
        this._vs = (JsonSerializer<Object>) valueSer;
        this._dks = src._dks;
        this._dvs = src._dvs;
        this._property = src._property;
    }

    @Override
    public JsonSerializer<?> createContextual(final SerializerProvider provider, final BeanProperty property) throws JsonMappingException {
        JsonSerializer<?> vs = null;
        JsonSerializer<?> ks = null;

        final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
        final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();

        // First: if we have a property, may have property-annotation overrides
        if (propertyAcc != null && intr != null) {
            Object serDef = intr.findKeySerializer(propertyAcc);
            if (serDef != null) {
                ks = provider.serializerInstance(propertyAcc, serDef);
            }
            serDef = intr.findContentSerializer(propertyAcc);
            if (serDef != null) {
                vs = provider.serializerInstance(propertyAcc, serDef);
            }
        }

        if (vs == null) {
            vs = this._vs;
        }
        // [databind#124]: May have a content converter
        vs = findConvertingContentSerializer(provider, property, vs);
        if (vs == null) {
            // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
            //   we can consider it a static case as well.
            // 20-Aug-2013, tatu: Need to avoid trying to access serializer for java.lang.Object tho
            if (this._staticTyping && !this._valueType.isJavaLangObject()) {
                vs = provider.findValueSerializer(this._valueType, property);
            }
        } else {
            vs = provider.handleSecondaryContextualization(vs, property);
        }

        if (ks == null) {
            ks = this._ks;
        }
        ks = findConvertingContentSerializer(provider, property, ks);
        if (ks == null) {
            if (this._staticTyping && !this._valueType.isJavaLangObject()) {
                ks = provider.findValueSerializer(this._valueType, property);
            }
        } else {
            ks = provider.handleSecondaryContextualization(ks, property);
        }

        PairSerializer mser = withResolved(property, ks, vs);
        // but note: no filtering, ignored entries or sorting (unlike Maps)
        return mser;
    }

    public PairSerializer withResolved(BeanProperty property, JsonSerializer<?> ks, JsonSerializer<?> vs) {
        return new PairSerializer(this, property, this._vts, ks, vs);
    }

    @Override
    public void serialize(
        final Pair<?, ?> value, final JsonGenerator gen, final SerializerProvider provider
    ) throws IOException {
        gen.writeStartObject(value);
        //if (this._vs != null) {
        //    serializeUsing(value, gen, provider, _vs);
        //} else {
        serializeDynamic(value, gen, provider);
        //}
        gen.writeEndObject();
    }

    @Override
    public void serializeWithType(
        final Pair<?, ?> value, final JsonGenerator gen, final SerializerProvider provider, final TypeSerializer typeSer
    ) throws IOException {
        typeSer.writeTypePrefixForObject(value, gen);
        // [databind#631]: Assign current value, to be accessible by custom serializers
        gen.setCurrentValue(value);
        //if (this._vs != null) {
        //    serializeUsing(value, gen, provider, _vs);
        //} else {
        serializeDynamic(value, gen, provider);
        //}
        typeSer.writeTypeSuffixForObject(value, gen);
    }

    protected void serializeDynamic(
        final Pair<?, ?> value, final JsonGenerator jgen, final SerializerProvider provider
    ) throws IOException {
        //final JsonSerializer<Object> keySerializer = this._ks;
        //final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
        final TypeSerializer vts = this._vts;
        final TypeSerializer kts = this._kts;

        PropertySerializerMap dvs = this._dvs;
        PropertySerializerMap dks = this._dks;

        Object valueElem = value.getValue();
        Object keyElem = value.getKey();

        jgen.writeFieldName("left");
        if (keyElem == null) {
            provider.defaultSerializeNull(jgen);
        } else {
            Class<?> cc = keyElem.getClass();
            JsonSerializer<Object> ser = dks.serializerFor(cc);
            if (ser == null) {
                if (this._valueType.hasGenericTypes()) {
                    ser = _findAndAddDynamicKeySerializer(dks, provider.constructSpecializedType(this._keyType, cc), provider);
                } else {
                    ser = _findAndAddDynamicKeySerializer(dks, cc, provider);
                }
                dks = this._dks;
            }
            try {
                if (kts == null) {
                    ser.serialize(keyElem, jgen, provider);
                } else {
                    ser.serializeWithType(keyElem, jgen, provider, kts);
                }
            } catch (final Exception e) {
                // [JACKSON-55] Need to add reference information
                String keyDesc = "" + keyElem;
                wrapAndThrow(provider, e, value, keyDesc);
            }
        }

        // And then value
        jgen.writeFieldName("right");
        if (valueElem == null) {
            provider.defaultSerializeNull(jgen);
        } else {
            Class<?> cc = valueElem.getClass();
            JsonSerializer<Object> ser = dvs.serializerFor(cc);
            if (ser == null) {
                if (this._valueType.hasGenericTypes()) {
                    ser = _findAndAddDynamicValueSerializer(dvs, provider.constructSpecializedType(this._valueType, cc), provider);
                } else {
                    ser = _findAndAddDynamicValueSerializer(dvs, cc, provider);
                }
                dvs = this._dvs;
            }
            try {
                if (vts == null) {
                    ser.serialize(valueElem, jgen, provider);
                } else {
                    ser.serializeWithType(valueElem, jgen, provider, vts);
                }
            } catch (final Exception e) {
                // [JACKSON-55] Need to add reference information
                String keyDesc = "" + keyElem;
                wrapAndThrow(provider, e, value, keyDesc);
            }
        }
    }


    protected final JsonSerializer<Object> _findAndAddDynamicKeySerializer(
        final PropertySerializerMap map,
        final Class<?> type,
        final SerializerProvider provider
    ) throws JsonMappingException {
        final PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, this._property);
        if (map != result.map) {
            this._dks = result.map;
        }
        return result.serializer;
    }

    protected final JsonSerializer<Object> _findAndAddDynamicKeySerializer(
        final PropertySerializerMap map,
        final JavaType type,
        final SerializerProvider provider
    ) throws JsonMappingException {
        final PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, this._property);
        if (map != result.map) {
            this._dks = result.map;
        }
        return result.serializer;
    }

    protected final JsonSerializer<Object> _findAndAddDynamicValueSerializer(
        final PropertySerializerMap map,
        final Class<?> type,
        final SerializerProvider provider
    ) throws JsonMappingException {
        final PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, this._property);
        if (map != result.map) {
            this._dvs = result.map;
        }
        return result.serializer;
    }

    protected final JsonSerializer<Object> _findAndAddDynamicValueSerializer(
        final PropertySerializerMap map,
        final JavaType type,
        final SerializerProvider provider
    ) throws JsonMappingException {
        final PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, this._property);
        if (map != result.map) {
            this._dvs = result.map;
        }
        return result.serializer;
    }
}