1 package com.fasterxml.jackson.databind.ser.std;
2
3 import java.io.IOException;
4 import java.lang.reflect.Type;
5 import java.util.*;
6
7 import com.fasterxml.jackson.annotation.JsonFormat;
8 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
9 import com.fasterxml.jackson.annotation.JsonInclude;
10 import com.fasterxml.jackson.core.*;
11 import com.fasterxml.jackson.core.type.WritableTypeId;
12 import com.fasterxml.jackson.databind.*;
13 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
14 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
15 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
16 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor;
17 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
18 import com.fasterxml.jackson.databind.ser.ContainerSerializer;
19 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
20 import com.fasterxml.jackson.databind.ser.PropertyFilter;
21 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
22 import com.fasterxml.jackson.databind.type.TypeFactory;
23 import com.fasterxml.jackson.databind.util.ArrayBuilders;
24 import com.fasterxml.jackson.databind.util.BeanUtil;
25 import com.fasterxml.jackson.databind.util.ClassUtil;
26
27 /**
28  * Standard serializer implementation for serializing {link java.util.Map} types.
29  *<p>
30  * Note: about the only configurable setting currently is ability to filter out
31  * entries with specified names.
32  */

33 @JacksonStdImpl
34 public class MapSerializer
35     extends ContainerSerializer<Map<?,?>>
36     implements ContextualSerializer
37 {
38     private static final long serialVersionUID = 1L;
39
40     protected final static JavaType UNSPECIFIED_TYPE = TypeFactory.unknownType();
41
42     /**
43      * @since 2.9
44      */

45     public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
46
47     /*
48     /**********************************************************
49     /* Basic information about referring property, type
50     /**********************************************************
51      */

52     
53     /**
54      * Map-valued property being serialized with this instance
55      */

56     protected final BeanProperty _property;
57
58     /**
59      * Whether static types should be used for serialization of values
60      * or not (if not, dynamic runtime type is used)
61      */

62     protected final boolean _valueTypeIsStatic;
63
64     /**
65      * Declared type of keys
66      */

67     protected final JavaType _keyType;
68
69     /**
70      * Declared type of contained values
71      */

72     protected final JavaType _valueType;
73
74     /*
75     /**********************************************************
76     /* Serializers used
77     /**********************************************************
78      */

79     
80     /**
81      * Key serializer to use, if it can be statically determined
82      */

83     protected JsonSerializer<Object> _keySerializer;
84
85     /**
86      * Value serializer to use, if it can be statically determined
87      */

88     protected JsonSerializer<Object> _valueSerializer;
89
90     /**
91      * Type identifier serializer used for values, if any.
92      */

93     protected final TypeSerializer _valueTypeSerializer;
94
95     /**
96      * If value type cannot be statically determined, mapping from
97      * runtime value types to serializers are stored in this object.
98      */

99     protected PropertySerializerMap _dynamicValueSerializers;
100
101     /*
102     /**********************************************************
103     /* Config settings, filtering
104     /**********************************************************
105      */

106     
107     /**
108      * Set of entries to omit during serialization, if any
109      */

110     protected final Set<String> _ignoredEntries;
111
112     /**
113      * Id of the property filter to use, if any; null if none.
114      *
115      * @since 2.3
116      */

117     protected final Object _filterId;
118
119     /**
120      * Value that indicates suppression mechanism to use for <b>values contained</b>;
121      * either "filter" (of which <code>equals()</code> is called), or marker
122      * value of {@link #MARKER_FOR_EMPTY}, or null to indicate no filtering for
123      * non-null values.
124      * Note that inclusion value for Map instance itself is handled by caller (POJO
125      * property that refers to the Map value).
126      * 
127      * @since 2.5
128      */

129     protected final Object _suppressableValue;
130
131     /**
132      * Flag that indicates what to do with `null` values, distinct from
133      * handling of {@link #_suppressableValue}
134      *
135      * @since 2.9
136      */

137     protected final boolean _suppressNulls;
138
139     /*
140     /**********************************************************
141     /* Config settings, other
142     /**********************************************************
143      */

144
145     /**
146      * Flag set if output is forced to be sorted by keys (usually due
147      * to annotation).
148      * 
149      * @since 2.4
150      */

151     protected final boolean _sortKeys;
152
153     /*
154     /**********************************************************
155     /* Life-cycle
156     /**********************************************************
157      */

158     
159     /**
160      * @since 2.5
161      */

162     @SuppressWarnings("unchecked")
163     protected MapSerializer(Set<String> ignoredEntries,
164             JavaType keyType, JavaType valueType, boolean valueTypeIsStatic,
165             TypeSerializer vts,
166             JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer)
167     {
168         super(Map.classfalse);
169         _ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty())
170                 ? null : ignoredEntries;
171         _keyType = keyType;
172         _valueType = valueType;
173         _valueTypeIsStatic = valueTypeIsStatic;
174         _valueTypeSerializer = vts;
175         _keySerializer = (JsonSerializer<Object>) keySerializer;
176         _valueSerializer = (JsonSerializer<Object>) valueSerializer;
177         _dynamicValueSerializers = PropertySerializerMap.emptyForProperties();
178         _property = null;
179         _filterId = null;
180         _sortKeys = false;
181         _suppressableValue = null;
182         _suppressNulls = false;
183     }
184
185     @SuppressWarnings("unchecked")
186     protected MapSerializer(MapSerializer src, BeanProperty property,
187             JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
188             Set<String> ignoredEntries)
189     {
190         super(Map.classfalse);
191         _ignoredEntries = ((ignoredEntries == null) || ignoredEntries.isEmpty())
192                 ? null : ignoredEntries;
193         _keyType = src._keyType;
194         _valueType = src._valueType;
195         _valueTypeIsStatic = src._valueTypeIsStatic;
196         _valueTypeSerializer = src._valueTypeSerializer;
197         _keySerializer = (JsonSerializer<Object>) keySerializer;
198         _valueSerializer = (JsonSerializer<Object>) valueSerializer;
199         // [databind#2181]: may not be safe to reuse, start from empty
200         _dynamicValueSerializers = PropertySerializerMap.emptyForProperties();
201         _property = property;
202         _filterId = src._filterId;
203         _sortKeys = src._sortKeys;
204         _suppressableValue = src._suppressableValue;
205         _suppressNulls = src._suppressNulls;
206     }
207
208     /**
209      * @since 2.9
210      */

211     protected MapSerializer(MapSerializer src, TypeSerializer vts,
212             Object suppressableValue, boolean suppressNulls)
213     {
214         super(Map.classfalse);
215         _ignoredEntries = src._ignoredEntries;
216         _keyType = src._keyType;
217         _valueType = src._valueType;
218         _valueTypeIsStatic = src._valueTypeIsStatic;
219         _valueTypeSerializer = vts;
220         _keySerializer = src._keySerializer;
221         _valueSerializer = src._valueSerializer;
222         // 22-Nov-2018, tatu: probably safe (even with [databind#2181]) since it's just
223         //   inclusion, type serializer but NOT serializer
224         _dynamicValueSerializers = src._dynamicValueSerializers;
225         _property = src._property;
226         _filterId = src._filterId;
227         _sortKeys = src._sortKeys;
228         _suppressableValue = suppressableValue;
229         _suppressNulls = suppressNulls;
230     }
231
232     protected MapSerializer(MapSerializer src, Object filterId, boolean sortKeys)
233     {
234         super(Map.classfalse);
235         _ignoredEntries = src._ignoredEntries;
236         _keyType = src._keyType;
237         _valueType = src._valueType;
238         _valueTypeIsStatic = src._valueTypeIsStatic;
239         _valueTypeSerializer = src._valueTypeSerializer;
240         _keySerializer = src._keySerializer;
241         _valueSerializer = src._valueSerializer;
242         // [databind#2181]: may not be safe to reuse, start from empty
243         _dynamicValueSerializers = PropertySerializerMap.emptyForProperties();
244         _property = src._property;
245         _filterId = filterId;
246         _sortKeys = sortKeys;
247         _suppressableValue = src._suppressableValue;
248         _suppressNulls = src._suppressNulls;
249     }
250
251     @Override
252     public MapSerializer _withValueTypeSerializer(TypeSerializer vts) {
253         if (_valueTypeSerializer == vts) {
254             return this;
255         }
256         _ensureOverride("_withValueTypeSerializer");
257         return new MapSerializer(this, vts, _suppressableValue, _suppressNulls);
258     }
259
260     /**
261      * @since 2.4
262      */

263     public MapSerializer withResolved(BeanProperty property,
264             JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
265             Set<String> ignored, boolean sortKeys)
266     {
267         _ensureOverride("withResolved");
268         MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
269         if (sortKeys != ser._sortKeys) {
270             ser = new MapSerializer(ser, _filterId, sortKeys);
271         }
272         return ser;
273     }
274
275     @Override
276     public MapSerializer withFilterId(Object filterId) {
277         if (_filterId == filterId) {
278             return this;
279         }
280         _ensureOverride("withFilterId");
281         return new MapSerializer(this, filterId, _sortKeys);
282     }
283
284     /**
285      * Mutant factory for constructing an instance with different inclusion strategy
286      * for content (Map values).
287      * 
288      * @since 2.9
289      */

290     public MapSerializer withContentInclusion(Object suppressableValue, boolean suppressNulls) {
291         if ((suppressableValue == _suppressableValue) && (suppressNulls == _suppressNulls)) {
292             return this;
293         }
294         _ensureOverride("withContentInclusion");
295         return new MapSerializer(this, _valueTypeSerializer, suppressableValue, suppressNulls);
296     }
297
298     /**
299      * @since 2.8
300      */

301     public static MapSerializer construct(Set<String> ignoredEntries, JavaType mapType,
302             boolean staticValueType, TypeSerializer vts,
303             JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
304             Object filterId)
305     {
306         JavaType keyType, valueType;
307
308         if (mapType == null) {
309             keyType = valueType = UNSPECIFIED_TYPE;
310         } else { 
311             keyType = mapType.getKeyType();
312             if (mapType.hasRawClass(java.util.Properties.class)) {
313                 // 25-Mar-2020, tatu: [databind#2657] Since non-standard Properties may actually
314                 //     contain non-Strings, demote value type to raw `Object`
315                 valueType = TypeFactory.unknownType();
316             } else {
317                 valueType = mapType.getContentType();
318             }
319         }
320         // If value type is final, it's same as forcing static value typing:
321         if (!staticValueType) {
322             staticValueType = (valueType != null && valueType.isFinal());
323         } else {
324             // also: Object.class cannot be handled as static, ever
325             if (valueType.getRawClass() == Object.class) {
326                 staticValueType = false;
327             }
328         }
329         MapSerializer ser = new MapSerializer(ignoredEntries, keyType, valueType, staticValueType, vts,
330                 keySerializer, valueSerializer);
331         if (filterId != null) {
332             ser = ser.withFilterId(filterId);
333         }
334         return ser;
335     }
336
337     /**
338      * @since 2.9
339      */

340     protected void _ensureOverride(String method) {
341         ClassUtil.verifyMustOverride(MapSerializer.classthis, method);
342     }
343
344     /**
345      * @since 2.5
346      */

347     @Deprecated // since 2.9
348     protected void _ensureOverride() {
349         _ensureOverride("N/A");
350     }
351
352     /*
353     /**********************************************************
354     /* Deprecated creators
355     /**********************************************************
356      */

357
358     /**
359      * @since 2.5
360      * @deprecated // since 2.9
361      */

362     @Deprecated // since 2.9
363     protected MapSerializer(MapSerializer src, TypeSerializer vts,
364             Object suppressableValue)
365     {
366         this(src, vts, suppressableValue, false);
367     }
368
369     /**
370      * @deprecated since 2.9
371      */

372     @Deprecated // since 2.9
373     public MapSerializer withContentInclusion(Object suppressableValue) {
374         return new MapSerializer(this, _valueTypeSerializer, suppressableValue, _suppressNulls);
375     }                
376
377     /**
378      * @since 2.3
379      *
380      * @deprecated Since 2.8 use the other overload
381      */

382     @Deprecated // since 2.8
383     public static MapSerializer construct(String[] ignoredList, JavaType mapType,
384             boolean staticValueType, TypeSerializer vts,
385             JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
386             Object filterId)
387     {
388         Set<String> ignoredEntries = ArrayBuilders.arrayToSet(ignoredList);
389         return construct(ignoredEntries, mapType, staticValueType, vts,
390                 keySerializer, valueSerializer, filterId);
391     }
392
393     /*
394     /**********************************************************
395     /* Post-processing (contextualization)
396     /**********************************************************
397      */

398
399     @Override
400     public JsonSerializer<?> createContextual(SerializerProvider provider,
401             BeanProperty property)
402         throws JsonMappingException
403     {
404         JsonSerializer<?> ser = null;
405         JsonSerializer<?> keySer = null;
406         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
407         final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();
408
409         // First: if we have a property, may have property-annotation overrides
410         if (_neitherNull(propertyAcc, intr)) {
411             Object serDef = intr.findKeySerializer(propertyAcc);
412             if (serDef != null) {
413                 keySer = provider.serializerInstance(propertyAcc, serDef);
414             }
415             serDef = intr.findContentSerializer(propertyAcc);
416             if (serDef != null) {
417                 ser = provider.serializerInstance(propertyAcc, serDef);
418             }
419         }
420         if (ser == null) {
421             ser = _valueSerializer;
422         }
423         // [databind#124]: May have a content converter
424         ser = findContextualConvertingSerializer(provider, property, ser);
425         if (ser == null) {
426             // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
427             //   we can consider it a static case as well.
428             // 20-Aug-2013, tatu: Need to avoid trying to access serializer for java.lang.Object tho
429             if (_valueTypeIsStatic && !_valueType.isJavaLangObject()) {
430                 ser = provider.findContentValueSerializer(_valueType, property);
431             }
432         }
433         if (keySer == null) {
434             keySer = _keySerializer;
435         }
436         if (keySer == null) {
437             keySer = provider.findKeySerializer(_keyType, property);
438         } else {
439             keySer = provider.handleSecondaryContextualization(keySer, property);
440         }
441         Set<String> ignored = _ignoredEntries;
442         boolean sortKeys = false;
443         if (_neitherNull(propertyAcc, intr)) {
444             JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(propertyAcc);
445             if (ignorals != null){
446                 Set<String> newIgnored = ignorals.findIgnoredForSerialization();
447                 if (_nonEmpty(newIgnored)) {
448                     ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
449                     for (String str : newIgnored) {
450                         ignored.add(str);
451                     }
452                 }
453             }
454             Boolean b = intr.findSerializationSortAlphabetically(propertyAcc);
455             sortKeys = Boolean.TRUE.equals(b);
456         }
457         JsonFormat.Value format = findFormatOverrides(provider, property, Map.class);
458         if (format != null) {
459             Boolean B = format.getFeature(JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES);
460             if (B != null) {
461                 sortKeys = B.booleanValue();
462             }
463         }
464         MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys);
465
466         // [databind#307]: allow filtering
467         if (property != null) {
468             AnnotatedMember m = property.getMember();
469             if (m != null) {
470                 Object filterId = intr.findFilterId(m);
471                 if (filterId != null) {
472                     mser = mser.withFilterId(filterId);
473                 }
474             }
475         }
476         JsonInclude.Value inclV = findIncludeOverrides(provider, property, Map.class);
477         if (inclV != null) {
478             JsonInclude.Include incl = inclV.getContentInclusion();
479
480             if (incl != JsonInclude.Include.USE_DEFAULTS) {
481                 Object valueToSuppress;
482                 boolean suppressNulls;
483                 switch (incl) {
484                 case NON_DEFAULT:
485                     valueToSuppress = BeanUtil.getDefaultValue(_valueType);
486                     suppressNulls = true;
487                     if (valueToSuppress != null) {
488                         if (valueToSuppress.getClass().isArray()) {
489                             valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
490                         }
491                     }
492                     break;
493                 case NON_ABSENT:
494                     suppressNulls = true;
495                     valueToSuppress = _valueType.isReferenceType() ? MARKER_FOR_EMPTY : null;
496                     break;
497                 case NON_EMPTY:
498                     suppressNulls = true;
499                     valueToSuppress = MARKER_FOR_EMPTY;
500                     break;
501                 case CUSTOM:
502                     valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter());
503                     if (valueToSuppress == null) { // is this legal?
504                         suppressNulls = true;
505                     } else {
506                         suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress);
507                     }
508                     break;
509                 case NON_NULL:
510                     valueToSuppress = null;
511                     suppressNulls = true;
512                     break;
513                 case ALWAYS: // default
514                 default:
515                     valueToSuppress = null;
516                     // 30-Sep-2016, tatu: Should not need to check global flags here,
517                     //   if inclusion forced to be ALWAYS
518                     suppressNulls = false;
519                     break;
520                 }
521                 mser = mser.withContentInclusion(valueToSuppress, suppressNulls);
522             }
523         }
524         return mser;
525     }
526
527     /*
528     /**********************************************************
529     /* Accessors
530     /**********************************************************
531      */

532
533     @Override
534     public JavaType getContentType() {
535         return _valueType;
536     }
537
538     @Override
539     public JsonSerializer<?> getContentSerializer() {
540         return _valueSerializer;
541     }
542
543     @Override
544     public boolean isEmpty(SerializerProvider prov, Map<?,?> value)
545     {
546         if (value.isEmpty()) {
547             return true;
548         }
549         
550         // 05-Nove-2015, tatu: Simple cases are cheap, but for recursive
551         //   emptiness checking we actually need to see if values are empty as well.
552         Object supp = _suppressableValue;
553         if ((supp == null) && !_suppressNulls) {
554             return false;
555         }
556         JsonSerializer<Object> valueSer = _valueSerializer;
557         final boolean checkEmpty = (MARKER_FOR_EMPTY == supp);
558         if (valueSer != null) {
559             for (Object elemValue : value.values()) {
560                 if (elemValue == null) {
561                     if (_suppressNulls) {
562                         continue;
563                     }
564                     return false;
565                 }
566                 if (checkEmpty) {
567                     if (!valueSer.isEmpty(prov, elemValue)) {
568                         return false;
569                     }
570                 } else if ((supp == null) || !supp.equals(value)) {
571                     return false;
572                 }
573             }
574             return true;
575         }
576         // But if not statically known, try this:
577         for (Object elemValue : value.values()) {
578             if (elemValue == null) {
579                 if (_suppressNulls) {
580                     continue;
581                 }
582                 return false;
583             }
584             try {
585                 valueSer = _findSerializer(prov, elemValue);
586             } catch (JsonMappingException e) { // Ugh... cannot just throw as-is, so...
587                 // 05-Nov-2015, tatu: For now, probably best not to assume empty then
588                 return false;
589             }
590             if (checkEmpty) {
591                 if (!valueSer.isEmpty(prov, elemValue)) {
592                     return false;
593                 }
594             } else if ((supp == null) || !supp.equals(value)) {
595                 return false;
596             }
597         }
598         return true;
599     }
600
601     @Override
602     public boolean hasSingleElement(Map<?,?> value) {
603         return (value.size() == 1);
604     }
605
606     /*
607     /**********************************************************
608     /* Extended API
609     /**********************************************************
610      */

611
612     /**
613      * Accessor for currently assigned key serializer. Note that
614      * this may return null during construction of <code>MapSerializer</code>:
615      * depedencies are resolved during {@link #createContextual} method
616      * (which can be overridden by custom implementations), but for some
617      * dynamic types, it is possible that serializer is only resolved
618      * during actual serialization.
619      * 
620      * @since 2.0
621      */

622     public JsonSerializer<?> getKeySerializer() {
623         return _keySerializer;
624     }
625     
626     /*
627     /**********************************************************
628     /* JsonSerializer implementation
629     /**********************************************************
630      */

631
632     @Override
633     public void serialize(Map<?,?> value, JsonGenerator gen, SerializerProvider provider)
634         throws IOException
635     {
636         gen.writeStartObject(value);
637         serializeWithoutTypeInfo(value, gen, provider);
638         gen.writeEndObject();
639     }
640
641     @Override
642     public void serializeWithType(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
643             TypeSerializer typeSer)
644         throws IOException
645     {
646         // [databind#631]: Assign current value, to be accessible by custom serializers
647         gen.setCurrentValue(value);
648         WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen,
649                 typeSer.typeId(value, JsonToken.START_OBJECT));
650         serializeWithoutTypeInfo(value, gen, provider);
651         typeSer.writeTypeSuffix(gen, typeIdDef);
652     }
653
654     /*
655     /**********************************************************
656     /* Secondary serialization methods
657     /**********************************************************
658      */

659
660     /**
661      * General-purpose serialization for contents without writing object type. Will suppress, filter and
662      * use custom serializers.
663      *<p>
664      * Public since it also is called by {@code AnyGetterWriter}.
665      *
666      * @since 2.11
667      */

668     public void serializeWithoutTypeInfo(Map<?, ?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
669         if (!value.isEmpty()) {
670             if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
671                 value = _orderEntries(value, gen, provider);
672             }
673             PropertyFilter pf;
674             if ((_filterId != null) && (pf = findPropertyFilter(provider, _filterId, value)) != null) {
675                 serializeFilteredFields(value, gen, provider, pf, _suppressableValue);
676             } else if ((_suppressableValue != null) || _suppressNulls) {
677                 serializeOptionalFields(value, gen, provider, _suppressableValue);
678             } else if (_valueSerializer != null) {
679                 serializeFieldsUsing(value, gen, provider, _valueSerializer);
680             } else {
681                 serializeFields(value, gen, provider);
682             }
683         }
684     }
685
686     /**
687      * General-purpose serialization for contents, where we do not necessarily know
688      * the value serialization, but 
689      * we do know that no value suppression is needed (which simplifies processing a bit)
690      */

691     public void serializeFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider)
692         throws IOException
693     {
694         // If value type needs polymorphic type handling, some more work needed:
695         if (_valueTypeSerializer != null) {
696             serializeTypedFields(value, gen, provider, null);
697             return;
698         }
699         final JsonSerializer<Object> keySerializer = _keySerializer;
700         final Set<String> ignored = _ignoredEntries;
701         Object keyElem = null;
702
703         try {
704             for (Map.Entry<?,?> entry : value.entrySet()) {
705                 Object valueElem = entry.getValue();
706                 // First, serialize key
707                 keyElem = entry.getKey();
708                 if (keyElem == null) {
709                     provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider);
710                 } else {
711                     // One twist: is entry ignorable? If so, skip
712                     if ((ignored != null) && ignored.contains(keyElem)) {
713                         continue;
714                     }
715                     keySerializer.serialize(keyElem, gen, provider);
716                 }
717                 // And then value
718                 if (valueElem == null) {
719                     provider.defaultSerializeNull(gen);
720                     continue;
721                 }
722                 JsonSerializer<Object> serializer = _valueSerializer;
723                 if (serializer == null) {
724                     serializer = _findSerializer(provider, valueElem);
725                 }
726                 serializer.serialize(valueElem, gen, provider);
727             }
728         } catch (Exception e) { // Add reference information
729             wrapAndThrow(provider, e, value, String.valueOf(keyElem));
730         }
731     }
732
733     /**
734      * Serialization method called when exclusion filtering needs to be applied.
735      */

736     public void serializeOptionalFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
737             Object suppressableValue)
738         throws IOException
739     {
740         // If value type needs polymorphic type handling, some more work needed:
741         if (_valueTypeSerializer != null) {
742             serializeTypedFields(value, gen, provider, suppressableValue);
743             return;
744         }
745         final Set<String> ignored = _ignoredEntries;
746         final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
747
748         for (Map.Entry<?,?> entry : value.entrySet()) {
749             // First find key serializer
750             final Object keyElem = entry.getKey();
751             JsonSerializer<Object> keySerializer;
752             if (keyElem == null) {
753                 keySerializer = provider.findNullKeySerializer(_keyType, _property);
754             } else {
755                 if (ignored != null && ignored.contains(keyElem)) continue;
756                 keySerializer = _keySerializer;
757             }
758
759             // Then value serializer
760             final Object valueElem = entry.getValue();
761             JsonSerializer<Object> valueSer;
762             if (valueElem == null) {
763                 if (_suppressNulls) { // all suppressions include null-suppression
764                     continue;
765                 }
766                 valueSer = provider.getDefaultNullValueSerializer();
767             } else {
768                 valueSer = _valueSerializer;
769                 if (valueSer == null) {
770                     valueSer = _findSerializer(provider, valueElem);
771                 }
772                 // also may need to skip non-empty values:
773                 if (checkEmpty) {
774                     if (valueSer.isEmpty(provider, valueElem)) {
775                         continue;
776                     }
777                 } else if (suppressableValue != null) {
778                     if (suppressableValue.equals(valueElem)) {
779                         continue;
780                     }
781                 }
782             }
783             // and then serialize, if all went well
784             try {
785                 keySerializer.serialize(keyElem, gen, provider);
786                 valueSer.serialize(valueElem, gen, provider);
787             } catch (Exception e) {
788                 wrapAndThrow(provider, e, value, String.valueOf(keyElem));
789             }
790         }
791     }
792     
793     /**
794      * Method called to serialize fields, when the value type is statically known,
795      * so that value serializer is passed and does not need to be fetched from
796      * provider.
797      */

798     public void serializeFieldsUsing(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
799             JsonSerializer<Object> ser)
800         throws IOException
801     {
802         final JsonSerializer<Object> keySerializer = _keySerializer;
803         final Set<String> ignored = _ignoredEntries;
804         final TypeSerializer typeSer = _valueTypeSerializer;
805
806         for (Map.Entry<?,?> entry : value.entrySet()) {
807             Object keyElem = entry.getKey();
808             if (ignored != null && ignored.contains(keyElem)) continue;
809
810             if (keyElem == null) {
811                 provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider);
812             } else {
813                 keySerializer.serialize(keyElem, gen, provider);
814             }
815             final Object valueElem = entry.getValue();
816             if (valueElem == null) {
817                 provider.defaultSerializeNull(gen);
818             } else {
819                 try {
820                     if (typeSer == null) {
821                         ser.serialize(valueElem, gen, provider);
822                     } else {
823                         ser.serializeWithType(valueElem, gen, provider, typeSer);
824                     }
825                 } catch (Exception e) {
826                     wrapAndThrow(provider, e, value, String.valueOf(keyElem));
827                 }
828             }
829         }
830     }
831
832     /**
833      * Helper method used when we have a JSON Filter to use for potentially
834      * filtering out Map entries.
835      * 
836      * @since 2.5
837      */

838     public void serializeFilteredFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
839             PropertyFilter filter,
840             Object suppressableValue) // since 2.5
841         throws IOException
842     {
843         final Set<String> ignored = _ignoredEntries;
844         final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
845         final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
846
847         for (Map.Entry<?,?> entry : value.entrySet()) {
848             // First, serialize key; unless ignorable by key
849             final Object keyElem = entry.getKey();
850             if (ignored != null && ignored.contains(keyElem)) continue;
851
852             JsonSerializer<Object> keySerializer;
853             if (keyElem == null) {
854                 keySerializer = provider.findNullKeySerializer(_keyType, _property);
855             } else {
856                 keySerializer = _keySerializer;
857             }
858             // or by value; nulls often suppressed
859             final Object valueElem = entry.getValue();
860
861             JsonSerializer<Object> valueSer;
862             // And then value
863             if (valueElem == null) {
864                 if (_suppressNulls) {
865                     continue;
866                 }
867                 valueSer = provider.getDefaultNullValueSerializer();
868             } else {
869                 valueSer = _valueSerializer;
870                 if (valueSer == null) {
871                     valueSer = _findSerializer(provider, valueElem);
872                 }
873                 // also may need to skip non-empty values:
874                 if (checkEmpty) {
875                     if (valueSer.isEmpty(provider, valueElem)) {
876                         continue;
877                     }
878                 } else if (suppressableValue != null) {
879                     if (suppressableValue.equals(valueElem)) {
880                         continue;
881                     }
882                 }
883             }
884             // and with that, ask filter to handle it
885             prop.reset(keyElem, valueElem, keySerializer, valueSer);
886             try {
887                 filter.serializeAsField(value, gen, provider, prop);
888             } catch (Exception e) {
889                 wrapAndThrow(provider, e, value, String.valueOf(keyElem));
890             }
891         }
892     }
893
894     /**
895      * @since 2.5
896      */

897     public void serializeTypedFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
898             Object suppressableValue) // since 2.5
899         throws IOException
900     {
901         final Set<String> ignored = _ignoredEntries;
902         final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
903
904         for (Map.Entry<?,?> entry : value.entrySet()) {
905             Object keyElem = entry.getKey();
906             JsonSerializer<Object> keySerializer;
907             if (keyElem == null) {
908                 keySerializer = provider.findNullKeySerializer(_keyType, _property);
909             } else {
910                 // One twist: is entry ignorable? If so, skip
911                 if (ignored != null && ignored.contains(keyElem)) continue;
912                 keySerializer = _keySerializer;
913             }
914             final Object valueElem = entry.getValue();
915     
916             // And then value
917             JsonSerializer<Object> valueSer;
918             if (valueElem == null) {
919                 if (_suppressNulls) { // all suppression include null suppression
920                     continue;
921                 }
922                 valueSer = provider.getDefaultNullValueSerializer();
923             } else {
924                 valueSer = _valueSerializer;
925                 if (valueSer == null) {
926                     valueSer = _findSerializer(provider, valueElem);
927                 }
928                 // also may need to skip non-empty values:
929                 if (checkEmpty) {
930                     if (valueSer.isEmpty(provider, valueElem)) {
931                         continue;
932                     }
933                 } else if (suppressableValue != null) {
934                     if (suppressableValue.equals(valueElem)) {
935                         continue;
936                     }
937                 }
938             }
939             keySerializer.serialize(keyElem, gen, provider);
940             try {
941                 valueSer.serializeWithType(valueElem, gen, provider, _valueTypeSerializer);
942             } catch (Exception e) {
943                 wrapAndThrow(provider, e, value, String.valueOf(keyElem));
944             }
945         }
946     }
947
948     /**
949      * Helper method used when we have a JSON Filter to use AND contents are
950      * "any properties" of a POJO.
951      *
952      * @param bean Enclosing POJO that has any-getter used to obtain "any properties"
953      * 
954      * @since 2.9
955      */

956     public void serializeFilteredAnyProperties(SerializerProvider provider, JsonGenerator gen,
957             Object bean, Map<?,?> value, PropertyFilter filter,
958             Object suppressableValue)
959         throws IOException
960     {
961         final Set<String> ignored = _ignoredEntries;
962         final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
963         final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
964
965         for (Map.Entry<?,?> entry : value.entrySet()) {
966             // First, serialize key; unless ignorable by key
967             final Object keyElem = entry.getKey();
968             if (ignored != null && ignored.contains(keyElem)) continue;
969
970             JsonSerializer<Object> keySerializer;
971             if (keyElem == null) {
972                 keySerializer = provider.findNullKeySerializer(_keyType, _property);
973             } else {
974                 keySerializer = _keySerializer;
975             }
976             // or by value; nulls often suppressed
977             final Object valueElem = entry.getValue();
978
979             JsonSerializer<Object> valueSer;
980             // And then value
981             if (valueElem == null) {
982                 if (_suppressNulls) {
983                     continue;
984                 }
985                 valueSer = provider.getDefaultNullValueSerializer();
986             } else {
987                 valueSer = _valueSerializer;
988                 if (valueSer == null) {
989                     valueSer = _findSerializer(provider, valueElem);
990                 }
991                 // also may need to skip non-empty values:
992                 if (checkEmpty) {
993                     if (valueSer.isEmpty(provider, valueElem)) {
994                         continue;
995                     }
996                 } else if (suppressableValue != null) {
997                     if (suppressableValue.equals(valueElem)) {
998                         continue;
999                     }
1000                 }
1001             }
1002             // and with that, ask filter to handle it
1003             prop.reset(keyElem, valueElem, keySerializer, valueSer);
1004             try {
1005                 filter.serializeAsField(bean, gen, provider, prop);
1006             } catch (Exception e) {
1007                 wrapAndThrow(provider, e, value, String.valueOf(keyElem));
1008             }
1009         }
1010     }
1011
1012     /*
1013     /**********************************************************
1014     /* Schema related functionality
1015     /**********************************************************
1016      */

1017
1018     @Override
1019     public JsonNode getSchema(SerializerProvider provider, Type typeHint)
1020     {
1021         // even though it's possible to statically determine the "value" type of the map,
1022         // there's no way to statically determine the keys, so the "Entries" can't be determined.
1023         return createSchemaNode("object"true);
1024     }
1025
1026     @Override
1027     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
1028         throws JsonMappingException
1029     {
1030         JsonMapFormatVisitor v2 = visitor.expectMapFormat(typeHint);        
1031         if (v2 != null) {
1032             v2.keyFormat(_keySerializer, _keyType);
1033             JsonSerializer<?> valueSer = _valueSerializer;
1034             if (valueSer == null) {
1035                 valueSer = _findAndAddDynamic(_dynamicValueSerializers,
1036                             _valueType, visitor.getProvider());
1037             }
1038             v2.valueFormat(valueSer, _valueType);
1039         }
1040     }
1041
1042     /*
1043     /**********************************************************
1044     /* Internal helper methods
1045     /**********************************************************
1046      */

1047
1048     protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
1049             Class<?> type, SerializerProvider provider) throws JsonMappingException
1050     {
1051         PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property);
1052         // did we get a new map of serializers? If so, start using it
1053         if (map != result.map) {
1054             _dynamicValueSerializers = result.map;
1055         }
1056         return result.serializer;
1057     }
1058
1059     protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
1060             JavaType type, SerializerProvider provider) throws JsonMappingException
1061     {
1062         PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property);
1063         if (map != result.map) {
1064             _dynamicValueSerializers = result.map;
1065         }
1066         return result.serializer;
1067     }
1068
1069     protected Map<?,?> _orderEntries(Map<?,?> input, JsonGenerator gen,
1070             SerializerProvider provider) throws IOException
1071     {
1072         // minor optimization: may already be sorted?
1073         if (input instanceof SortedMap<?,?>) {
1074             return input;
1075         }
1076         // [databind#1411]: TreeMap does not like null key... (although note that
1077         //   check above should prevent this code from being called in that case)
1078         // [databind#153]: but, apparently, some custom Maps do manage hit this
1079         //   problem.
1080         if (_hasNullKey(input)) {
1081             TreeMap<Object,Object> result = new TreeMap<Object,Object>();
1082             for (Map.Entry<?,?> entry : input.entrySet()) {
1083                 Object key = entry.getKey();
1084                 if (key == null) {
1085                     _writeNullKeyedEntry(gen, provider, entry.getValue());
1086                     continue;
1087                 } 
1088                 result.put(key, entry.getValue());
1089             }
1090             return result;
1091         }
1092         return new TreeMap<Object,Object>(input);
1093     }
1094
1095     /**
1096      * @since 2.8.7
1097      */

1098     protected boolean _hasNullKey(Map<?,?> input) {
1099         // 19-Feb-2017, tatu: As per [databind#1513] there are many cases where `null`
1100         //   keys are not allowed, and even attempt to check for presence can cause
1101         //   problems. Without resorting to external sorting (and internal API change),
1102         //   or custom sortable Map implementation (more code) we can try black- or
1103         //   white-listing (that is; either skip known problem cases; or only apply for
1104         //   known good cases).
1105         //   While my first instinct was to do black-listing (remove Hashtable and ConcurrentHashMap),
1106         //   all in all it is probably better to just white list `HashMap` (and its sub-classes).
1107         
1108         return (input instanceof HashMap) && input.containsKey(null);
1109     }
1110     
1111     protected void _writeNullKeyedEntry(JsonGenerator gen, SerializerProvider provider,
1112             Object value) throws IOException
1113     {
1114         JsonSerializer<Object> keySerializer = provider.findNullKeySerializer(_keyType, _property);
1115         JsonSerializer<Object> valueSer;
1116         if (value == null) {
1117             if (_suppressNulls) {
1118                 return;
1119             }
1120             valueSer = provider.getDefaultNullValueSerializer();
1121         } else {
1122             valueSer = _valueSerializer;
1123             if (valueSer == null) {
1124                 valueSer = _findSerializer(provider, value);
1125             }
1126             if (_suppressableValue == MARKER_FOR_EMPTY) {
1127                 if (valueSer.isEmpty(provider, value)) {
1128                     return;
1129                 }
1130             } else if ((_suppressableValue != null)
1131                 && (_suppressableValue.equals(value))) {
1132                 return;
1133             }
1134         }
1135
1136         try {
1137             keySerializer.serialize(null, gen, provider);
1138             valueSer.serialize(value, gen, provider);
1139         } catch (Exception e) {
1140             wrapAndThrow(provider, e, value, "");
1141         }
1142     }
1143
1144     private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
1145             Object value) throws JsonMappingException
1146     {
1147         final Class<?> cc = value.getClass();
1148         JsonSerializer<Object> valueSer = _dynamicValueSerializers.serializerFor(cc);
1149         if (valueSer != null) {
1150             return valueSer;
1151         }
1152         if (_valueType.hasGenericTypes()) {
1153             return _findAndAddDynamic(_dynamicValueSerializers,
1154                     provider.constructSpecializedType(_valueType, cc), provider);
1155         }
1156         return _findAndAddDynamic(_dynamicValueSerializers, cc, provider);
1157     }
1158 }
1159