1 package com.fasterxml.jackson.databind.deser.std;
2 
3 import java.io.IOException;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
7 
8 import com.fasterxml.jackson.annotation.JsonIncludeProperties;
9 import com.fasterxml.jackson.core.*;
10 
11 import com.fasterxml.jackson.databind.*;
12 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
13 import com.fasterxml.jackson.databind.deser.*;
14 import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator;
15 import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
16 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
17 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
18 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
19 import com.fasterxml.jackson.databind.type.LogicalType;
20 import com.fasterxml.jackson.databind.util.ArrayBuilders;
21 import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
22 
23 /**
24  * Basic deserializer that can take JSON "Object" structure and
25  * construct a {@link java.util.Map} instance, with typed contents.
26  *<p>
27  * Note: for untyped content (one indicated by passing Object.class
28  * as the type), {@link UntypedObjectDeserializer} is used instead.
29  * It can also construct {@link java.util.Map}s, but not with specific
30  * POJO types, only other containers and primitives/wrappers.
31  */
32 @JacksonStdImpl
33 public class MapDeserializer
34     extends ContainerDeserializerBase<Map<Object,Object>>
35     implements ContextualDeserializer, ResolvableDeserializer
36 {
37     private static final long serialVersionUID = 1L;
38 
39     // // Configuration: typing, deserializers
40 
41     /**
42      * Key deserializer to use; either passed via constructor
43      * (when indicated by annotations), or resolved when
44      * {@link #resolve} is called;
45      */
46     protected final KeyDeserializer _keyDeserializer;
47 
48     /**
49      * Flag set to indicate that the key type is
50      * {@link java.lang.String} (or {@link java.lang.Object}, for
51      * which String is acceptable), <b>and</b> that the
52      * default Jackson key deserializer would be used.
53      * If both are true, can optimize handling.
54      */
55     protected boolean _standardStringKey;
56 
57     /**
58      * Value deserializer.
59      */
60     protected final JsonDeserializer<Object> _valueDeserializer;
61 
62     /**
63      * If value instances have polymorphic type information, this
64      * is the type deserializer that can handle it
65      */
66     protected final TypeDeserializer _valueTypeDeserializer;
67 
68     // // Instance construction settings:
69 
70     protected final ValueInstantiator _valueInstantiator;
71 
72     /**
73      * Deserializer that is used iff delegate-based creator is
74      * to be used for deserializing from JSON Object.
75      */
76     protected JsonDeserializer<Object> _delegateDeserializer;
77 
78     /**
79      * If the Map is to be instantiated using non-default constructor
80      * or factory method
81      * that takes one or more named properties as argument(s),
82      * this creator is used for instantiation.
83      */
84     protected PropertyBasedCreator _propertyBasedCreator;
85 
86     protected final boolean _hasDefaultCreator;
87 
88     // // Any properties to ignore if seen?
89 
90     protected Set<String> _ignorableProperties;
91 
92     /**
93      * @since 2.12
94      */
95     protected Set<String> _includableProperties;
96 
97     /**
98      * Helper object used for name-based filtering
99      *
100      * @since 2.12
101      */
102     protected IgnorePropertiesUtil.Checker _inclusionChecker;
103 
104     /*
105     /**********************************************************
106     /* Life-cycle
107     /**********************************************************
108      */
109 
MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator, KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser)110     public MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator,
111             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
112             TypeDeserializer valueTypeDeser)
113     {
114         super(mapType, null, null);
115         _keyDeserializer = keyDeser;
116         _valueDeserializer = valueDeser;
117         _valueTypeDeserializer = valueTypeDeser;
118         _valueInstantiator = valueInstantiator;
119         _hasDefaultCreator = valueInstantiator.canCreateUsingDefault();
120         _delegateDeserializer = null;
121         _propertyBasedCreator = null;
122         _standardStringKey = _isStdKeyDeser(mapType, keyDeser);
123         _inclusionChecker = null;
124     }
125 
126     /**
127      * Copy-constructor that can be used by sub-classes to allow
128      * copy-on-write styling copying of settings of an existing instance.
129      */
MapDeserializer(MapDeserializer src)130     protected MapDeserializer(MapDeserializer src)
131     {
132         super(src);
133         _keyDeserializer = src._keyDeserializer;
134         _valueDeserializer = src._valueDeserializer;
135         _valueTypeDeserializer = src._valueTypeDeserializer;
136         _valueInstantiator = src._valueInstantiator;
137         _propertyBasedCreator = src._propertyBasedCreator;
138         _delegateDeserializer = src._delegateDeserializer;
139         _hasDefaultCreator = src._hasDefaultCreator;
140         // should we make a copy here?
141         _ignorableProperties = src._ignorableProperties;
142         _includableProperties = src._includableProperties;
143         _inclusionChecker = src._inclusionChecker;
144 
145         _standardStringKey = src._standardStringKey;
146     }
147 
MapDeserializer(MapDeserializer src, KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser, NullValueProvider nuller, Set<String> ignorable)148     protected MapDeserializer(MapDeserializer src,
149             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
150             TypeDeserializer valueTypeDeser,
151             NullValueProvider nuller,
152             Set<String> ignorable)
153     {
154        this(src, keyDeser,valueDeser, valueTypeDeser, nuller, ignorable, null);
155     }
156 
157     /**
158      * @since 2.12
159      */
MapDeserializer(MapDeserializer src, KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser, NullValueProvider nuller, Set<String> ignorable, Set<String> includable)160     protected MapDeserializer(MapDeserializer src,
161             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
162             TypeDeserializer valueTypeDeser,
163             NullValueProvider nuller,
164             Set<String> ignorable,
165             Set<String> includable)
166     {
167         super(src, nuller, src._unwrapSingle);
168         _keyDeserializer = keyDeser;
169         _valueDeserializer = valueDeser;
170         _valueTypeDeserializer = valueTypeDeser;
171         _valueInstantiator = src._valueInstantiator;
172         _propertyBasedCreator = src._propertyBasedCreator;
173         _delegateDeserializer = src._delegateDeserializer;
174         _hasDefaultCreator = src._hasDefaultCreator;
175         _ignorableProperties = ignorable;
176         _includableProperties = includable;
177         _inclusionChecker = IgnorePropertiesUtil.buildCheckerIfNeeded(ignorable, includable);
178 
179         _standardStringKey = _isStdKeyDeser(_containerType, keyDeser);
180     }
181 
182     /**
183      * Fluent factory method used to create a copy with slightly
184      * different settings. When sub-classing, MUST be overridden.
185      */
withResolved(KeyDeserializer keyDeser, TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser, NullValueProvider nuller, Set<String> ignorable)186     protected MapDeserializer withResolved(KeyDeserializer keyDeser,
187             TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
188             NullValueProvider nuller,
189             Set<String> ignorable)
190     {
191         return withResolved(keyDeser, valueTypeDeser, valueDeser, nuller, ignorable, _includableProperties);
192     }
193 
194     /**
195      * @since 2.12
196      */
197     @SuppressWarnings("unchecked")
withResolved(KeyDeserializer keyDeser, TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser, NullValueProvider nuller, Set<String> ignorable, Set<String> includable)198     protected MapDeserializer withResolved(KeyDeserializer keyDeser,
199             TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
200             NullValueProvider nuller,
201             Set<String> ignorable, Set<String> includable)
202     {
203         if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
204                 && (_valueTypeDeserializer == valueTypeDeser) && (_nullProvider == nuller)
205                 && (_ignorableProperties == ignorable) && (_includableProperties == includable)) {
206             return this;
207         }
208         return new MapDeserializer(this,
209                 keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser,
210                 nuller, ignorable, includable);
211     }
212 
213     /**
214      * Helper method used to check whether we can just use the default key
215      * deserialization, where JSON String becomes Java String.
216      */
_isStdKeyDeser(JavaType mapType, KeyDeserializer keyDeser)217     protected final boolean _isStdKeyDeser(JavaType mapType, KeyDeserializer keyDeser)
218     {
219         if (keyDeser == null) {
220             return true;
221         }
222         JavaType keyType = mapType.getKeyType();
223         if (keyType == null) { // assumed to be Object
224             return true;
225         }
226         Class<?> rawKeyType = keyType.getRawClass();
227         return ((rawKeyType == String.class || rawKeyType == Object.class)
228                 && isDefaultKeyDeserializer(keyDeser));
229     }
230 
231     /**
232      * @deprecated in 2.12, remove from 3.0
233      */
234     @Deprecated
setIgnorableProperties(String[] ignorable)235     public void setIgnorableProperties(String[] ignorable) {
236         _ignorableProperties = (ignorable == null || ignorable.length == 0) ?
237             null : ArrayBuilders.arrayToSet(ignorable);
238         _inclusionChecker = IgnorePropertiesUtil.buildCheckerIfNeeded(_ignorableProperties, _includableProperties);
239     }
240 
setIgnorableProperties(Set<String> ignorable)241     public void setIgnorableProperties(Set<String> ignorable) {
242         _ignorableProperties = (ignorable == null || ignorable.size() == 0) ?
243                 null : ignorable;
244         _inclusionChecker = IgnorePropertiesUtil.buildCheckerIfNeeded(_ignorableProperties, _includableProperties);
245     }
246 
setIncludableProperties(Set<String> includable)247     public void setIncludableProperties(Set<String> includable) {
248         _includableProperties = includable;
249         _inclusionChecker = IgnorePropertiesUtil.buildCheckerIfNeeded(_ignorableProperties, _includableProperties);
250     }
251 
252     /*
253     /**********************************************************
254     /* Validation, post-processing (ResolvableDeserializer)
255     /**********************************************************
256      */
257 
258     @Override
resolve(DeserializationContext ctxt)259     public void resolve(DeserializationContext ctxt) throws JsonMappingException
260     {
261         // May need to resolve types for delegate- and/or property-based creators:
262         if (_valueInstantiator.canCreateUsingDelegate()) {
263             JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
264             if (delegateType == null) {
265                 ctxt.reportBadDefinition(_containerType, String.format(
266 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
267                 _containerType,
268                 _valueInstantiator.getClass().getName()));
269             }
270             // Theoretically should be able to get CreatorProperty for delegate
271             // parameter to pass; but things get tricky because DelegateCreator
272             // may contain injectable values. So, for now, let's pass nothing.
273             _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
274         } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
275             JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
276             if (delegateType == null) {
277                 ctxt.reportBadDefinition(_containerType, String.format(
278 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
279                     _containerType,
280                     _valueInstantiator.getClass().getName()));
281             }
282             _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
283         }
284         if (_valueInstantiator.canCreateFromObjectWith()) {
285             SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
286             _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps,
287                     ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
288         }
289         _standardStringKey = _isStdKeyDeser(_containerType, _keyDeserializer);
290     }
291 
292     /**
293      * Method called to finalize setup of this deserializer,
294      * when it is known for which property deserializer is needed for.
295      */
296     @Override
createContextual(DeserializationContext ctxt, BeanProperty property)297     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
298             BeanProperty property) throws JsonMappingException
299     {
300         KeyDeserializer keyDeser = _keyDeserializer;
301         if (keyDeser == null) {
302             keyDeser = ctxt.findKeyDeserializer(_containerType.getKeyType(), property);
303         } else {
304             if (keyDeser instanceof ContextualKeyDeserializer) {
305                 keyDeser = ((ContextualKeyDeserializer) keyDeser).createContextual(ctxt, property);
306             }
307         }
308 
309         JsonDeserializer<?> valueDeser = _valueDeserializer;
310         // [databind#125]: May have a content converter
311         if (property != null) {
312             valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
313         }
314         final JavaType vt = _containerType.getContentType();
315         if (valueDeser == null) {
316             valueDeser = ctxt.findContextualValueDeserializer(vt, property);
317         } else { // if directly assigned, probably not yet contextual, so:
318             valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
319         }
320         TypeDeserializer vtd = _valueTypeDeserializer;
321         if (vtd != null) {
322             vtd = vtd.forProperty(property);
323         }
324         Set<String> ignored = _ignorableProperties;
325         Set<String> included = _includableProperties;
326         AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
327         if (_neitherNull(intr, property)) {
328             AnnotatedMember member = property.getMember();
329             if (member != null) {
330                 final DeserializationConfig config = ctxt.getConfig();
331                 JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnoralByName(config, member);
332                 if (ignorals != null) {
333                     Set<String> ignoresToAdd = ignorals.findIgnoredForDeserialization();
334                     if (!ignoresToAdd.isEmpty()) {
335                         ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
336                         for (String str : ignoresToAdd) {
337                             ignored.add(str);
338                         }
339                     }
340                 }
341                 JsonIncludeProperties.Value inclusions = intr.findPropertyInclusionByName(config, member);
342                 if (inclusions != null) {
343                     Set<String> includedToAdd = inclusions.getIncluded();
344                     if (includedToAdd != null) {
345                         Set<String> newIncluded = new HashSet<>();
346                         if (included == null) {
347                             newIncluded = new HashSet<>(includedToAdd);
348                         } else {
349                             for (String str : includedToAdd) {
350                                 if (included.contains(str)) {
351                                     newIncluded.add(str);
352                                 }
353                             }
354                         }
355                         included = newIncluded;
356                     }
357                 }
358             }
359         }
360         return withResolved(keyDeser, vtd, valueDeser,
361                 findContentNullProvider(ctxt, property, valueDeser), ignored, included);
362     }
363 
364     /*
365     /**********************************************************
366     /* ContainerDeserializerBase API
367     /**********************************************************
368      */
369 
370     @Override
getContentDeserializer()371     public JsonDeserializer<Object> getContentDeserializer() {
372         return _valueDeserializer;
373     }
374 
375     @Override
getValueInstantiator()376     public ValueInstantiator getValueInstantiator() {
377         return _valueInstantiator;
378     }
379 
380     /*
381     /**********************************************************
382     /* JsonDeserializer API
383     /**********************************************************
384      */
385 
386     /**
387      * Turns out that these are expensive enough to create so that caching
388      * does make sense.
389      *<p>
390      * IMPORTANT: but, note, that instances CAN NOT BE CACHED if there is
391      * a value type deserializer; this caused an issue with 2.4.4 of
392      * JAXB Annotations (failing a test).
393      * It is also possible that some other settings could make deserializers
394      * un-cacheable; but on the other hand, caching can make a big positive
395      * difference with performance... so it's a hard choice.
396      *
397      * @since 2.4.4
398      */
399     @Override
isCachable()400     public boolean isCachable() {
401         // As per [databind#735], existence of value or key deserializer (only passed
402         // if annotated to use non-standard one) should also prevent caching.
403         return (_valueDeserializer == null)
404                 && (_keyDeserializer == null)
405                 && (_valueTypeDeserializer == null)
406                 && (_ignorableProperties == null)
407                 && (_includableProperties == null);
408     }
409 
410     @Override // since 2.12
logicalType()411     public LogicalType logicalType() {
412         return LogicalType.Map;
413     }
414 
415     @Override
416     @SuppressWarnings("unchecked")
deserialize(JsonParser p, DeserializationContext ctxt)417     public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
418     {
419         if (_propertyBasedCreator != null) {
420             return _deserializeUsingCreator(p, ctxt);
421         }
422         if (_delegateDeserializer != null) {
423             return (Map<Object,Object>) _valueInstantiator.createUsingDelegate(ctxt,
424                     _delegateDeserializer.deserialize(p, ctxt));
425         }
426         if (!_hasDefaultCreator) {
427             return (Map<Object,Object> ) ctxt.handleMissingInstantiator(getMapClass(),
428                     getValueInstantiator(), p,
429                     "no default constructor found");
430         }
431         switch (p.currentTokenId()) {
432         case JsonTokenId.ID_START_OBJECT:
433         case JsonTokenId.ID_END_OBJECT:
434         case JsonTokenId.ID_FIELD_NAME:
435             final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
436             if (_standardStringKey) {
437                 _readAndBindStringKeyMap(p, ctxt, result);
438                 return result;
439             }
440             _readAndBind(p, ctxt, result);
441             return result;
442         case JsonTokenId.ID_STRING:
443             // (empty) String may be ok however; or single-String-arg ctor
444             return _deserializeFromString(p, ctxt);
445         case JsonTokenId.ID_START_ARRAY:
446             // Empty array, or single-value wrapped in array?
447             return _deserializeFromArray(p, ctxt);
448         default:
449         }
450         return (Map<Object,Object>) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
451     }
452 
453     @SuppressWarnings("unchecked")
454     @Override
deserialize(JsonParser p, DeserializationContext ctxt, Map<Object,Object> result)455     public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
456             Map<Object,Object> result)
457         throws IOException
458     {
459         // [databind#631]: Assign current value, to be accessible by custom deserializers
460         p.setCurrentValue(result);
461 
462         // Ok: must point to START_OBJECT or FIELD_NAME
463         JsonToken t = p.currentToken();
464         if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) {
465             return (Map<Object,Object>) ctxt.handleUnexpectedToken(getMapClass(), p);
466         }
467         // 21-Apr-2017, tatu: Need separate methods to do proper merging
468         if (_standardStringKey) {
469             _readAndUpdateStringKeyMap(p, ctxt, result);
470             return result;
471         }
472         _readAndUpdate(p, ctxt, result);
473         return result;
474     }
475 
476     @Override
deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer)477     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
478             TypeDeserializer typeDeserializer)
479         throws IOException
480     {
481         // In future could check current token... for now this should be enough:
482         return typeDeserializer.deserializeTypedFromObject(p, ctxt);
483     }
484 
485     /*
486     /**********************************************************
487     /* Other public accessors
488     /**********************************************************
489      */
490 
491     @SuppressWarnings("unchecked")
getMapClass()492     public final Class<?> getMapClass() { return (Class<Map<Object,Object>>) _containerType.getRawClass(); }
493 
getValueType()494     @Override public JavaType getValueType() { return _containerType; }
495 
496     /*
497     /**********************************************************
498     /* Internal methods, non-merging deserialization
499     /**********************************************************
500      */
501 
_readAndBind(JsonParser p, DeserializationContext ctxt, Map<Object,Object> result)502     protected final void _readAndBind(JsonParser p, DeserializationContext ctxt,
503             Map<Object,Object> result) throws IOException
504     {
505         final KeyDeserializer keyDes = _keyDeserializer;
506         final JsonDeserializer<Object> valueDes = _valueDeserializer;
507         final TypeDeserializer typeDeser = _valueTypeDeserializer;
508 
509         MapReferringAccumulator referringAccumulator = null;
510         boolean useObjectId = valueDes.getObjectIdReader() != null;
511         if (useObjectId) {
512             referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(),
513                     result);
514         }
515 
516         String keyStr;
517         if (p.isExpectedStartObjectToken()) {
518             keyStr = p.nextFieldName();
519         } else {
520             JsonToken t = p.currentToken();
521             if (t != JsonToken.FIELD_NAME) {
522                 if (t == JsonToken.END_OBJECT) {
523                     return;
524                 }
525                 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
526             }
527             keyStr = p.currentName();
528         }
529 
530         for (; keyStr != null; keyStr = p.nextFieldName()) {
531             Object key = keyDes.deserializeKey(keyStr, ctxt);
532             // And then the value...
533             JsonToken t = p.nextToken();
534             if ((_inclusionChecker != null) && _inclusionChecker.shouldIgnore(keyStr)) {
535                 p.skipChildren();
536                 continue;
537             }
538             try {
539                 // Note: must handle null explicitly here; value deserializers won't
540                 Object value;
541                 if (t == JsonToken.VALUE_NULL) {
542                     if (_skipNullValues) {
543                         continue;
544                     }
545                     value = _nullProvider.getNullValue(ctxt);
546                 } else if (typeDeser == null) {
547                     value = valueDes.deserialize(p, ctxt);
548                 } else {
549                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
550                 }
551                 if (useObjectId) {
552                     referringAccumulator.put(key, value);
553                 } else {
554                     result.put(key, value);
555                 }
556             } catch (UnresolvedForwardReference reference) {
557                 handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
558             } catch (Exception e) {
559                 wrapAndThrow(e, result, keyStr);
560             }
561         }
562     }
563 
564     /**
565      * Optimized method used when keys can be deserialized as plain old
566      * {@link java.lang.String}s, and there is no custom deserialized
567      * specified.
568      */
_readAndBindStringKeyMap(JsonParser p, DeserializationContext ctxt, Map<Object,Object> result)569     protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationContext ctxt,
570             Map<Object,Object> result) throws IOException
571     {
572         final JsonDeserializer<Object> valueDes = _valueDeserializer;
573         final TypeDeserializer typeDeser = _valueTypeDeserializer;
574         MapReferringAccumulator referringAccumulator = null;
575         boolean useObjectId = (valueDes.getObjectIdReader() != null);
576         if (useObjectId) {
577             referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result);
578         }
579 
580         String key;
581         if (p.isExpectedStartObjectToken()) {
582             key = p.nextFieldName();
583         } else {
584             JsonToken t = p.currentToken();
585             if (t == JsonToken.END_OBJECT) {
586                 return;
587             }
588             if (t != JsonToken.FIELD_NAME) {
589                 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
590             }
591             key = p.currentName();
592         }
593 
594         for (; key != null; key = p.nextFieldName()) {
595             JsonToken t = p.nextToken();
596             if ((_inclusionChecker != null) && _inclusionChecker.shouldIgnore(key)) {
597                 p.skipChildren();
598                 continue;
599             }
600             try {
601                 // Note: must handle null explicitly here; value deserializers won't
602                 Object value;
603                 if (t == JsonToken.VALUE_NULL) {
604                     if (_skipNullValues) {
605                         continue;
606                     }
607                     value = _nullProvider.getNullValue(ctxt);
608                 } else if (typeDeser == null) {
609                     value = valueDes.deserialize(p, ctxt);
610                 } else {
611                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
612                 }
613                 if (useObjectId) {
614                     referringAccumulator.put(key, value);
615                 } else {
616                     result.put(key, value);
617                 }
618             } catch (UnresolvedForwardReference reference) {
619                 handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
620             } catch (Exception e) {
621                 wrapAndThrow(e, result, key);
622             }
623         }
624         // 23-Mar-2015, tatu: TODO: verify we got END_OBJECT?
625     }
626 
627     @SuppressWarnings("unchecked")
_deserializeUsingCreator(JsonParser p, DeserializationContext ctxt)628     public Map<Object,Object> _deserializeUsingCreator(JsonParser p, DeserializationContext ctxt) throws IOException
629     {
630         final PropertyBasedCreator creator = _propertyBasedCreator;
631         // null -> no ObjectIdReader for Maps (yet?)
632         PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null);
633 
634         final JsonDeserializer<Object> valueDes = _valueDeserializer;
635         final TypeDeserializer typeDeser = _valueTypeDeserializer;
636 
637         String key;
638         if (p.isExpectedStartObjectToken()) {
639             key = p.nextFieldName();
640         } else if (p.hasToken(JsonToken.FIELD_NAME)) {
641             key = p.currentName();
642         } else {
643             key = null;
644         }
645 
646         for (; key != null; key = p.nextFieldName()) {
647             JsonToken t = p.nextToken(); // to get to value
648             if ((_inclusionChecker != null) && _inclusionChecker.shouldIgnore(key)) {
649                 p.skipChildren(); // and skip it (in case of array/object)
650                 continue;
651             }
652             // creator property?
653             SettableBeanProperty prop = creator.findCreatorProperty(key);
654             if (prop != null) {
655                 // Last property to set?
656                 if (buffer.assignParameter(prop, prop.deserialize(p, ctxt))) {
657                     p.nextToken(); // from value to END_OBJECT or FIELD_NAME
658                     Map<Object,Object> result;
659                     try {
660                         result = (Map<Object,Object>)creator.build(ctxt, buffer);
661                     } catch (Exception e) {
662                         return wrapAndThrow(e, _containerType.getRawClass(), key);
663                     }
664                     _readAndBind(p, ctxt, result);
665                     return result;
666                 }
667                 continue;
668             }
669             // other property? needs buffering
670             Object actualKey = _keyDeserializer.deserializeKey(key, ctxt);
671             Object value;
672 
673             try {
674                 if (t == JsonToken.VALUE_NULL) {
675                     if (_skipNullValues) {
676                         continue;
677                     }
678                     value = _nullProvider.getNullValue(ctxt);
679                 } else if (typeDeser == null) {
680                     value = valueDes.deserialize(p, ctxt);
681                 } else {
682                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
683                 }
684             } catch (Exception e) {
685                 wrapAndThrow(e, _containerType.getRawClass(), key);
686                 return null;
687             }
688             buffer.bufferMapProperty(actualKey, value);
689         }
690         // end of JSON object?
691         // if so, can just construct and leave...
692         try {
693             return (Map<Object,Object>)creator.build(ctxt, buffer);
694         } catch (Exception e) {
695             wrapAndThrow(e, _containerType.getRawClass(), key);
696             return null;
697         }
698     }
699 
700     /*
701     /**********************************************************
702     /* Internal methods, non-merging deserialization
703     /**********************************************************
704      */
705 
706     /**
707      * @since 2.9
708      */
_readAndUpdate(JsonParser p, DeserializationContext ctxt, Map<Object,Object> result)709     protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt,
710             Map<Object,Object> result) throws IOException
711     {
712         final KeyDeserializer keyDes = _keyDeserializer;
713         final JsonDeserializer<Object> valueDes = _valueDeserializer;
714         final TypeDeserializer typeDeser = _valueTypeDeserializer;
715 
716         // Note: assumption is that Object Id handling can't really work with merging
717         // and thereby we can (and should) just drop that part
718 
719         String keyStr;
720         if (p.isExpectedStartObjectToken()) {
721             keyStr = p.nextFieldName();
722         } else {
723             JsonToken t = p.currentToken();
724             if (t == JsonToken.END_OBJECT) {
725                 return;
726             }
727             if (t != JsonToken.FIELD_NAME) {
728                 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
729             }
730             keyStr = p.currentName();
731         }
732 
733         for (; keyStr != null; keyStr = p.nextFieldName()) {
734             Object key = keyDes.deserializeKey(keyStr, ctxt);
735             // And then the value...
736             JsonToken t = p.nextToken();
737             if ((_inclusionChecker != null) && _inclusionChecker.shouldIgnore(keyStr)) {
738                 p.skipChildren();
739                 continue;
740             }
741             try {
742                 // Note: must handle null explicitly here, can't merge etc
743                 if (t == JsonToken.VALUE_NULL) {
744                     if (_skipNullValues) {
745                         continue;
746                     }
747                     result.put(key, _nullProvider.getNullValue(ctxt));
748                     continue;
749                 }
750                 Object old = result.get(key);
751                 Object value;
752                 if (old != null) {
753                     if (typeDeser == null) {
754                         value = valueDes.deserialize(p, ctxt, old);
755                     } else {
756                         value = valueDes.deserializeWithType(p, ctxt, typeDeser, old);
757                     }
758                 } else if (typeDeser == null) {
759                     value = valueDes.deserialize(p, ctxt);
760                 } else {
761                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
762                 }
763                 if (value != old) {
764                     result.put(key, value);
765                 }
766             } catch (Exception e) {
767                 wrapAndThrow(e, result, keyStr);
768             }
769         }
770     }
771 
772     /**
773      * Optimized method used when keys can be deserialized as plain old
774      * {@link java.lang.String}s, and there is no custom deserializer
775      * specified.
776      *
777      * @since 2.9
778      */
_readAndUpdateStringKeyMap(JsonParser p, DeserializationContext ctxt, Map<Object,Object> result)779     protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationContext ctxt,
780             Map<Object,Object> result) throws IOException
781     {
782         final JsonDeserializer<Object> valueDes = _valueDeserializer;
783         final TypeDeserializer typeDeser = _valueTypeDeserializer;
784 
785         // Note: assumption is that Object Id handling can't really work with merging
786         // and thereby we can (and should) just drop that part
787 
788         String key;
789         if (p.isExpectedStartObjectToken()) {
790             key = p.nextFieldName();
791         } else {
792             JsonToken t = p.currentToken();
793             if (t == JsonToken.END_OBJECT) {
794                 return;
795             }
796             if (t != JsonToken.FIELD_NAME) {
797                 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
798             }
799             key = p.currentName();
800         }
801 
802         for (; key != null; key = p.nextFieldName()) {
803             JsonToken t = p.nextToken();
804             if ((_inclusionChecker != null) && _inclusionChecker.shouldIgnore(key)) {
805                 p.skipChildren();
806                 continue;
807             }
808             try {
809                 // Note: must handle null explicitly here, can't merge etc
810                 if (t == JsonToken.VALUE_NULL) {
811                     if (_skipNullValues) {
812                         continue;
813                     }
814                     result.put(key, _nullProvider.getNullValue(ctxt));
815                     continue;
816                 }
817                 Object old = result.get(key);
818                 Object value;
819                 if (old != null) {
820                     if (typeDeser == null) {
821                         value = valueDes.deserialize(p, ctxt, old);
822                     } else {
823                         value = valueDes.deserializeWithType(p, ctxt, typeDeser, old);
824                     }
825                 } else if (typeDeser == null) {
826                     value = valueDes.deserialize(p, ctxt);
827                 } else {
828                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
829                 }
830                 if (value != old) {
831                     result.put(key, value);
832                 }
833             } catch (Exception e) {
834                 wrapAndThrow(e, result, key);
835             }
836         }
837     }
838 
839     /*
840     /**********************************************************
841     /* Internal methods, other
842     /**********************************************************
843      */
844 
handleUnresolvedReference(DeserializationContext ctxt, MapReferringAccumulator accumulator, Object key, UnresolvedForwardReference reference)845     private void handleUnresolvedReference(DeserializationContext ctxt,
846             MapReferringAccumulator accumulator,
847             Object key, UnresolvedForwardReference reference)
848         throws JsonMappingException
849     {
850         if (accumulator == null) {
851             ctxt.reportInputMismatch(this,
852                     "Unresolved forward reference but no identity info: "+reference);
853         }
854         Referring referring = accumulator.handleUnresolvedReference(reference, key);
855         reference.getRoid().appendReferring(referring);
856     }
857 
858     private final static class MapReferringAccumulator {
859         private final Class<?> _valueType;
860         private Map<Object,Object> _result;
861         /**
862          * A list of {@link MapReferring} to maintain ordering.
863          */
864         private List<MapReferring> _accumulator = new ArrayList<MapReferring>();
865 
MapReferringAccumulator(Class<?> valueType, Map<Object, Object> result)866         public MapReferringAccumulator(Class<?> valueType, Map<Object, Object> result) {
867             _valueType = valueType;
868             _result = result;
869         }
870 
put(Object key, Object value)871         public void put(Object key, Object value)
872         {
873             if (_accumulator.isEmpty()) {
874                 _result.put(key, value);
875             } else {
876                 MapReferring ref = _accumulator.get(_accumulator.size() - 1);
877                 ref.next.put(key, value);
878             }
879         }
880 
handleUnresolvedReference(UnresolvedForwardReference reference, Object key)881         public Referring handleUnresolvedReference(UnresolvedForwardReference reference, Object key)
882         {
883             MapReferring id = new MapReferring(this, reference, _valueType, key);
884             _accumulator.add(id);
885             return id;
886         }
887 
resolveForwardReference(Object id, Object value)888         public void resolveForwardReference(Object id, Object value) throws IOException
889         {
890             Iterator<MapReferring> iterator = _accumulator.iterator();
891             // Resolve ordering after resolution of an id. This means either:
892             // 1- adding to the result map in case of the first unresolved id.
893             // 2- merge the content of the resolved id with its previous unresolved id.
894             Map<Object,Object> previous = _result;
895             while (iterator.hasNext()) {
896                 MapReferring ref = iterator.next();
897                 if (ref.hasId(id)) {
898                     iterator.remove();
899                     previous.put(ref.key, value);
900                     previous.putAll(ref.next);
901                     return;
902                 }
903                 previous = ref.next;
904             }
905 
906             throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id
907                     + "] that wasn't previously seen as unresolved.");
908         }
909     }
910 
911     /**
912      * Helper class to maintain processing order of value.
913      * The resolved object associated with {@link #key} comes before the values in
914      * {@link #next}.
915      */
916     static class MapReferring extends Referring {
917         private final MapReferringAccumulator _parent;
918 
919         public final Map<Object, Object> next = new LinkedHashMap<Object, Object>();
920         public final Object key;
921 
MapReferring(MapReferringAccumulator parent, UnresolvedForwardReference ref, Class<?> valueType, Object key)922         MapReferring(MapReferringAccumulator parent, UnresolvedForwardReference ref,
923                 Class<?> valueType, Object key)
924         {
925             super(ref, valueType);
926             _parent = parent;
927             this.key = key;
928         }
929 
930         @Override
handleResolvedForwardReference(Object id, Object value)931         public void handleResolvedForwardReference(Object id, Object value) throws IOException {
932             _parent.resolveForwardReference(id, value);
933         }
934     }
935 }
936