All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.prestosql.jdbc.$internal.jackson.datatype.guava.ser.MultimapSerializer Maven / Gradle / Ivy

There is a newer version: 350
Show newest version
package io.prestosql.jdbc.$internal.jackson.datatype.guava.ser;

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

import io.prestosql.jdbc.$internal.jackson.annotation.JsonFormat;
import io.prestosql.jdbc.$internal.jackson.annotation.JsonIgnoreProperties;
import io.prestosql.jdbc.$internal.jackson.core.*;
import io.prestosql.jdbc.$internal.jackson.core.type.WritableTypeId;
import io.prestosql.jdbc.$internal.jackson.databind.*;
import io.prestosql.jdbc.$internal.jackson.databind.introspect.AnnotatedMember;
import io.prestosql.jdbc.$internal.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
import io.prestosql.jdbc.$internal.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
import io.prestosql.jdbc.$internal.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
import io.prestosql.jdbc.$internal.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import io.prestosql.jdbc.$internal.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor;
import io.prestosql.jdbc.$internal.jackson.databind.jsontype.TypeSerializer;
import io.prestosql.jdbc.$internal.jackson.databind.ser.ContainerSerializer;
import io.prestosql.jdbc.$internal.jackson.databind.ser.ContextualSerializer;
import io.prestosql.jdbc.$internal.jackson.databind.ser.PropertyFilter;
import io.prestosql.jdbc.$internal.jackson.databind.ser.impl.PropertySerializerMap;
import io.prestosql.jdbc.$internal.jackson.databind.ser.std.MapProperty;
import io.prestosql.jdbc.$internal.jackson.databind.type.MapLikeType;
import io.prestosql.jdbc.$internal.guava.collect.Multimap;

/**
 * Serializer for Guava's {@link Multimap} values. Output format encloses all
 * value sets in JSON Array, regardless of number of values; this to reduce
 * complexity (and inaccuracy) of trying to handle cases where values themselves
 * would be serialized as arrays (in which cases determining whether given array
 * is a wrapper or value gets complicated and unreliable).
 *

* Missing features, compared to standard Java Maps: *

    *
  • Inclusion checks for content entries (non-null, non-empty) *
  • *
  • Sorting of entries *
  • *
*/ public class MultimapSerializer extends ContainerSerializer> implements ContextualSerializer { private static final long serialVersionUID = 1L; private final MapLikeType _type; private final BeanProperty _property; private final JsonSerializer _keySerializer; private final TypeSerializer _valueTypeSerializer; private final JsonSerializer _valueSerializer; /** * Set of entries to omit during serialization, if any * * @since 2.5 */ protected final Set _ignoredEntries; /** * If value type can not be statically determined, mapping from * runtime value types to serializers are stored in this object. * * @since 2.5 */ protected PropertySerializerMap _dynamicValueSerializers; /** * Id of the property filter to use, if any; null if none. * * @since 2.5 */ protected final Object _filterId; /** * Flag set if output is forced to be sorted by keys (usually due * to annotation). *

* NOTE: not yet used. * * @since 2.5 */ protected final boolean _sortKeys; public MultimapSerializer(MapLikeType type, BeanDescription beanDesc, JsonSerializer keySerializer, TypeSerializer vts, JsonSerializer valueSerializer, Set ignoredEntries, Object filterId) { super(type.getRawClass(), false); _type = type; _property = null; _keySerializer = keySerializer; _valueTypeSerializer = vts; _valueSerializer = valueSerializer; _ignoredEntries = ignoredEntries; _filterId = filterId; _sortKeys = false; _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); } /** * @since 2.5 */ @SuppressWarnings("unchecked") protected MultimapSerializer(MultimapSerializer src, BeanProperty property, JsonSerializer keySerializer, TypeSerializer vts, JsonSerializer valueSerializer, Set ignoredEntries, Object filterId, boolean sortKeys) { super(src); _type = src._type; _property = property; _keySerializer = (JsonSerializer) keySerializer; _valueTypeSerializer = vts; _valueSerializer = (JsonSerializer) valueSerializer; _dynamicValueSerializers = src._dynamicValueSerializers; _ignoredEntries = ignoredEntries; _filterId = filterId; _sortKeys = sortKeys; } protected MultimapSerializer withResolved(BeanProperty property, JsonSerializer keySer, TypeSerializer vts, JsonSerializer valueSer, Set ignored, Object filterId, boolean sortKeys) { return new MultimapSerializer(this, property, keySer, vts, valueSer, ignored, filterId, sortKeys); } @Override protected ContainerSerializer _withValueTypeSerializer(TypeSerializer typeSer) { return new MultimapSerializer(this, _property, _keySerializer, typeSer, _valueSerializer, _ignoredEntries, _filterId, _sortKeys); } /* /********************************************************** /* Post-processing (contextualization) /********************************************************** */ @Override public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException { JsonSerializer valueSer = _valueSerializer; if (valueSer == null) { // if type is final, can actually resolve: JavaType valueType = _type.getContentType(); if (valueType.isFinal()) { valueSer = provider.findValueSerializer(valueType, property); } } else if (valueSer instanceof ContextualSerializer) { valueSer = ((ContextualSerializer) valueSer).createContextual(provider, property); } final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember(); JsonSerializer keySer = null; Object filterId = _filterId; // First: if we have a property, may have property-annotation overrides if (propertyAcc != null && intr != null) { Object serDef = intr.findKeySerializer(propertyAcc); if (serDef != null) { keySer = provider.serializerInstance(propertyAcc, serDef); } serDef = intr.findContentSerializer(propertyAcc); if (serDef != null) { valueSer = provider.serializerInstance(propertyAcc, serDef); } filterId = intr.findFilterId(propertyAcc); } if (valueSer == null) { valueSer = _valueSerializer; } // [datatype-guava#124]: May have a content converter valueSer = findContextualConvertingSerializer(provider, property, valueSer); if (valueSer == null) { // One more thing -- if explicit content type is annotated, // we can consider it a static case as well. JavaType valueType = _type.getContentType(); if (valueType.useStaticType()) { valueSer = provider.findValueSerializer(valueType, property); } } else { valueSer = provider.handleSecondaryContextualization(valueSer, property); } if (keySer == null) { keySer = _keySerializer; } if (keySer == null) { keySer = provider.findKeySerializer(_type.getKeyType(), property); } else { keySer = provider.handleSecondaryContextualization(keySer, property); } // finally, TypeSerializers may need contextualization as well TypeSerializer typeSer = _valueTypeSerializer; if (typeSer != null) { typeSer = typeSer.forProperty(property); } Set ignored = _ignoredEntries; boolean sortKeys = false; if (intr != null && propertyAcc != null) { JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(propertyAcc); if (ignorals != null) { Set newIgnored = ignorals.findIgnoredForSerialization(); if ((newIgnored != null) && !newIgnored.isEmpty()) { ignored = (ignored == null) ? new HashSet() : new HashSet<>(ignored); for (String str : newIgnored) { ignored.add(str); } } } Boolean b = intr.findSerializationSortAlphabetically(propertyAcc); sortKeys = (b != null) && b.booleanValue(); } // 19-May-2016, tatu: Also check per-property format features, even if // this isn't yet used (as per [guava#7]) JsonFormat.Value format = findFormatOverrides(provider, property, handledType()); if (format != null) { Boolean B = format.getFeature(JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES); if (B != null) { sortKeys = B.booleanValue(); } } return withResolved(property, keySer, typeSer, valueSer, ignored, filterId, sortKeys); } /* /********************************************************** /* Accessors for ContainerSerializer /********************************************************** */ @Override public JsonSerializer getContentSerializer() { return _valueSerializer; } @Override public JavaType getContentType() { return _type.getContentType(); } @Override public boolean hasSingleElement(Multimap map) { return map.size() == 1; } @Override public boolean isEmpty(SerializerProvider prov, Multimap value) { return value.isEmpty(); } /* /********************************************************** /* Post-processing (contextualization) /********************************************************** */ @Override public void serialize(Multimap value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); // [databind#631]: Assign current value, to be accessible by custom serializers gen.setCurrentValue(value); if (!value.isEmpty()) { // 20-Mar-2017, tatu: And this is where [datatypes-collections#7] would be // plugged in... // if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) { // value = _orderEntries(value, gen, provider); // } if (_filterId != null) { serializeFilteredFields(value, gen, provider); } else { serializeFields(value, gen, provider); } } gen.writeEndObject(); } @Override public void serializeWithType(Multimap value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { gen.setCurrentValue(value); WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.START_OBJECT)); if (!value.isEmpty()) { // 20-Mar-2017, tatu: And this is where [datatypes-collections#7] would be // plugged in... // if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) { // value = _orderEntries(value, gen, provider); // } if (_filterId != null) { serializeFilteredFields(value, gen, provider); } else { serializeFields(value, gen, provider); } } typeSer.writeTypeSuffix(gen, typeIdDef); } private final void serializeFields(Multimap mmap, JsonGenerator gen, SerializerProvider provider) throws IOException { final Set ignored = _ignoredEntries; PropertySerializerMap serializers = _dynamicValueSerializers; for (Entry> entry : mmap.asMap().entrySet()) { // First, serialize key Object key = entry.getKey(); if ((ignored != null) && ignored.contains(key)) { continue; } if (key == null) { provider.findNullKeySerializer(_type.getKeyType(), _property) .serialize(null, gen, provider); } else { _keySerializer.serialize(key, gen, provider); } // note: value is a List, but generic type is for contents... so: gen.writeStartArray(); for (Object vv : entry.getValue()) { if (vv == null) { provider.defaultSerializeNull(gen); continue; } JsonSerializer valueSer = _valueSerializer; if (valueSer == null) { Class cc = vv.getClass(); valueSer = serializers.serializerFor(cc); if (valueSer == null) { valueSer = _findAndAddDynamic(serializers, cc, provider); serializers = _dynamicValueSerializers; } } if (_valueTypeSerializer == null) { valueSer.serialize(vv, gen, provider); } else { valueSer.serializeWithType(vv, gen, provider, _valueTypeSerializer); } } gen.writeEndArray(); } } private final void serializeFilteredFields(Multimap mmap, JsonGenerator gen, SerializerProvider provider) throws IOException { final Set ignored = _ignoredEntries; PropertyFilter filter = findPropertyFilter(provider, _filterId, mmap); final MapProperty prop = new MapProperty(_valueTypeSerializer, _property); for (Entry> entry : mmap.asMap().entrySet()) { // First, serialize key Object key = entry.getKey(); if ((ignored != null) && ignored.contains(key)) { continue; } Collection value = entry.getValue(); JsonSerializer valueSer; if (value == null) { // !!! TODO: null suppression? valueSer = provider.getDefaultNullValueSerializer(); } else { valueSer = _valueSerializer; } prop.reset(key, value, _keySerializer, valueSer); try { filter.serializeAsField(mmap, gen, provider, prop); } catch (Exception e) { String keyDesc = ""+key; wrapAndThrow(provider, e, value, keyDesc); } } } /* /********************************************************** /* Schema related functionality /********************************************************** */ @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { JsonMapFormatVisitor v2 = (visitor == null) ? null : visitor.expectMapFormat(typeHint); if (v2 != null) { v2.keyFormat(_keySerializer, _type.getKeyType()); JsonSerializer valueSer = _valueSerializer; final JavaType vt = _type.getContentType(); final SerializerProvider prov = visitor.getProvider(); if (valueSer == null) { valueSer = _findAndAddDynamic(_dynamicValueSerializers, vt, prov); } final JsonSerializer valueSer2 = valueSer; v2.valueFormat(new JsonFormatVisitable() { final JavaType arrayType = prov.getTypeFactory().constructArrayType(vt); @Override public void acceptJsonFormatVisitor( JsonFormatVisitorWrapper v3, JavaType hint3) throws JsonMappingException { JsonArrayFormatVisitor v4 = v3.expectArrayFormat(arrayType); if (v4 != null) { v4.itemsFormat(valueSer2, vt); } } }, vt); } } /* /********************************************************** /* Internal helper methods /********************************************************** */ protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, Class type, SerializerProvider provider) throws JsonMappingException { PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); // did we get a new map of serializers? If so, start using it if (map != result.map) { _dynamicValueSerializers = result.map; } return result.serializer; } protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, JavaType type, SerializerProvider provider) throws JsonMappingException { PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); if (map != result.map) { _dynamicValueSerializers = result.map; } return result.serializer; } }