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.core.*;
9
10 import com.fasterxml.jackson.databind.*;
11 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
12 import com.fasterxml.jackson.databind.deser.*;
13 import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator;
14 import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
15 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
16 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
17 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
18 import com.fasterxml.jackson.databind.util.ArrayBuilders;
19
20 /**
21  * Basic serializer that can take JSON "Object" structure and
22  * construct a {@link java.util.Map} instance, with typed contents.
23  *<p>
24  * Note: for untyped content (one indicated by passing Object.class
25  * as the type), {@link UntypedObjectDeserializer} is used instead.
26  * It can also construct {@link java.util.Map}s, but not with specific
27  * POJO types, only other containers and primitives/wrappers.
28  */

29 @JacksonStdImpl
30 public class MapDeserializer
31     extends ContainerDeserializerBase<Map<Object,Object>>
32     implements ContextualDeserializer, ResolvableDeserializer
33 {
34     private static final long serialVersionUID = 1L;
35
36     // // Configuration: typing, deserializers
37
38     /**
39      * Key deserializer to use; either passed via constructor
40      * (when indicated by annotations), or resolved when
41      * {@link #resolve} is called;
42      */

43     protected final KeyDeserializer _keyDeserializer;
44
45     /**
46      * Flag set to indicate that the key type is
47      * {@link java.lang.String} (or {@link java.lang.Object}, for
48      * which String is acceptable), <b>and</b> that the
49      * default Jackson key deserializer would be used.
50      * If both are true, can optimize handling.
51      */

52     protected boolean _standardStringKey;
53
54     /**
55      * Value deserializer.
56      */

57     protected final JsonDeserializer<Object> _valueDeserializer;
58
59     /**
60      * If value instances have polymorphic type information, this
61      * is the type deserializer that can handle it
62      */

63     protected final TypeDeserializer _valueTypeDeserializer;
64
65     // // Instance construction settings:
66
67     protected final ValueInstantiator _valueInstantiator;
68
69     /**
70      * Deserializer that is used iff delegate-based creator is
71      * to be used for deserializing from JSON Object.
72      */

73     protected JsonDeserializer<Object> _delegateDeserializer;
74
75     /**
76      * If the Map is to be instantiated using non-default constructor
77      * or factory method
78      * that takes one or more named properties as argument(s),
79      * this creator is used for instantiation.
80      */

81     protected PropertyBasedCreator _propertyBasedCreator;    
82
83     protected final boolean _hasDefaultCreator;
84
85     // // Any properties to ignore if seen?
86
87     protected Set<String> _ignorableProperties;
88
89     /*
90     /**********************************************************
91     /* Life-cycle
92     /**********************************************************
93      */

94
95     public MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator,
96             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
97             TypeDeserializer valueTypeDeser)
98     {
99         super(mapType, nullnull);
100         _keyDeserializer = keyDeser;
101         _valueDeserializer = valueDeser;
102         _valueTypeDeserializer = valueTypeDeser;
103         _valueInstantiator = valueInstantiator;
104         _hasDefaultCreator = valueInstantiator.canCreateUsingDefault();
105         _delegateDeserializer = null;
106         _propertyBasedCreator = null;
107         _standardStringKey = _isStdKeyDeser(mapType, keyDeser);
108     }
109
110     /**
111      * Copy-constructor that can be used by sub-classes to allow
112      * copy-on-write styling copying of settings of an existing instance.
113      */

114     protected MapDeserializer(MapDeserializer src)
115     {
116         super(src);
117         _keyDeserializer = src._keyDeserializer;
118         _valueDeserializer = src._valueDeserializer;
119         _valueTypeDeserializer = src._valueTypeDeserializer;
120         _valueInstantiator = src._valueInstantiator;
121         _propertyBasedCreator = src._propertyBasedCreator;
122         _delegateDeserializer = src._delegateDeserializer;
123         _hasDefaultCreator = src._hasDefaultCreator;
124         // should we make a copy here?
125         _ignorableProperties = src._ignorableProperties;
126
127         _standardStringKey = src._standardStringKey;
128     }
129
130     protected MapDeserializer(MapDeserializer src,
131             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
132             TypeDeserializer valueTypeDeser,
133             NullValueProvider nuller,
134             Set<String> ignorable)
135     {
136         super(src, nuller, src._unwrapSingle);
137         _keyDeserializer = keyDeser;
138         _valueDeserializer = valueDeser;
139         _valueTypeDeserializer = valueTypeDeser;
140         _valueInstantiator = src._valueInstantiator;
141         _propertyBasedCreator = src._propertyBasedCreator;
142         _delegateDeserializer = src._delegateDeserializer;
143         _hasDefaultCreator = src._hasDefaultCreator;
144         _ignorableProperties = ignorable;
145
146         _standardStringKey = _isStdKeyDeser(_containerType, keyDeser);
147     }
148
149     /**
150      * Fluent factory method used to create a copy with slightly
151      * different settings. When sub-classing, MUST be overridden.
152      */

153     @SuppressWarnings("unchecked")
154     protected MapDeserializer withResolved(KeyDeserializer keyDeser,
155             TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
156             NullValueProvider nuller,
157             Set<String> ignorable)
158     {
159         
160         if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
161                 && (_valueTypeDeserializer == valueTypeDeser) && (_nullProvider == nuller)
162                 && (_ignorableProperties == ignorable)) {
163             return this;
164         }
165         return new MapDeserializer(this,
166                 keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser,
167                 nuller, ignorable);
168     }
169
170     /**
171      * Helper method used to check whether we can just use the default key
172      * deserialization, where JSON String becomes Java String.
173      */

174     protected final boolean _isStdKeyDeser(JavaType mapType, KeyDeserializer keyDeser)
175     {
176         if (keyDeser == null) {
177             return true;
178         }
179         JavaType keyType = mapType.getKeyType();
180         if (keyType == null) { // assumed to be Object
181             return true;
182         }
183         Class<?> rawKeyType = keyType.getRawClass();
184         return ((rawKeyType == String.class || rawKeyType == Object.class)
185                 && isDefaultKeyDeserializer(keyDeser));
186     }
187
188     public void setIgnorableProperties(String[] ignorable) {
189         _ignorableProperties = (ignorable == null || ignorable.length == 0) ?
190             null : ArrayBuilders.arrayToSet(ignorable);
191     }
192
193     public void setIgnorableProperties(Set<String> ignorable) {
194         _ignorableProperties = (ignorable == null || ignorable.size() == 0) ?
195                 null : ignorable;
196     }
197
198     /*
199     /**********************************************************
200     /* Validation, post-processing (ResolvableDeserializer)
201     /**********************************************************
202      */

203
204     @Override
205     public void resolve(DeserializationContext ctxt) throws JsonMappingException
206     {
207         // May need to resolve types for delegate- and/or property-based creators:
208         if (_valueInstantiator.canCreateUsingDelegate()) {
209             JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
210             if (delegateType == null) {
211                 ctxt.reportBadDefinition(_containerType, String.format(
212 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
213                 _containerType,
214                 _valueInstantiator.getClass().getName()));
215             }
216             // Theoretically should be able to get CreatorProperty for delegate
217             // parameter to pass; but things get tricky because DelegateCreator
218             // may contain injectable values. So, for now, let's pass nothing.
219             _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
220         } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
221             JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
222             if (delegateType == null) {
223                 ctxt.reportBadDefinition(_containerType, String.format(
224 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
225                     _containerType,
226                     _valueInstantiator.getClass().getName()));
227             }
228             _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
229         }
230         if (_valueInstantiator.canCreateFromObjectWith()) {
231             SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
232             _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps,
233                     ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
234         }
235         _standardStringKey = _isStdKeyDeser(_containerType, _keyDeserializer);
236     }
237
238     /**
239      * Method called to finalize setup of this deserializer,
240      * when it is known for which property deserializer is needed for.
241      */

242     @Override
243     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
244             BeanProperty property) throws JsonMappingException
245     {
246         KeyDeserializer keyDeser = _keyDeserializer;
247         if (keyDeser == null) {
248             keyDeser = ctxt.findKeyDeserializer(_containerType.getKeyType(), property);
249         } else {
250             if (keyDeser instanceof ContextualKeyDeserializer) {
251                 keyDeser = ((ContextualKeyDeserializer) keyDeser).createContextual(ctxt, property);
252             }
253         }
254         
255         JsonDeserializer<?> valueDeser = _valueDeserializer;
256         // [databind#125]: May have a content converter
257         if (property != null) {
258             valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
259         }
260         final JavaType vt = _containerType.getContentType();
261         if (valueDeser == null) {
262             valueDeser = ctxt.findContextualValueDeserializer(vt, property);
263         } else { // if directly assigned, probably not yet contextual, so:
264             valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
265         }
266         TypeDeserializer vtd = _valueTypeDeserializer;
267         if (vtd != null) {
268             vtd = vtd.forProperty(property);
269         }
270         Set<String> ignored = _ignorableProperties;
271         AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
272         if (_neitherNull(intr, property)) {
273             AnnotatedMember member = property.getMember();
274             if (member != null) {
275                 JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(member);
276                 if (ignorals != null) {
277                     Set<String> ignoresToAdd = ignorals.findIgnoredForDeserialization();
278                     if (!ignoresToAdd.isEmpty()) {
279                         ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
280                         for (String str : ignoresToAdd) {
281                             ignored.add(str);
282                         }
283                     }
284                 }
285             }
286         }
287         return withResolved(keyDeser, vtd, valueDeser,
288                 findContentNullProvider(ctxt, property, valueDeser), ignored);
289     }
290
291     /*
292     /**********************************************************
293     /* ContainerDeserializerBase API
294     /**********************************************************
295      */

296
297     @Override
298     public JsonDeserializer<Object> getContentDeserializer() {
299         return _valueDeserializer;
300     }
301
302     @Override
303     public ValueInstantiator getValueInstantiator() {
304         return _valueInstantiator;
305     }
306
307     /*
308     /**********************************************************
309     /* JsonDeserializer API
310     /**********************************************************
311      */

312
313     /**
314      * Turns out that these are expensive enough to create so that caching
315      * does make sense.
316      *<p>
317      * IMPORTANT: but, note, that instances CAN NOT BE CACHED if there is
318      * a value type deserializer; this caused an issue with 2.4.4 of
319      * JAXB Annotations (failing a test).
320      * It is also possible that some other settings could make deserializers
321      * un-cacheable; but on the other hand, caching can make a big positive
322      * difference with performance... so it's a hard choice.
323      * 
324      * @since 2.4.4
325      */

326     @Override
327     public boolean isCachable() {
328         // As per [databind#735], existence of value or key deserializer (only passed
329         // if annotated to use non-standard one) should also prevent caching.
330         return (_valueDeserializer == null)
331                 && (_keyDeserializer == null)
332                 && (_valueTypeDeserializer == null)
333                 && (_ignorableProperties == null);
334     }
335
336     @Override
337     @SuppressWarnings("unchecked")
338     public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
339     {
340         if (_propertyBasedCreator != null) {
341             return _deserializeUsingCreator(p, ctxt);
342         }
343         if (_delegateDeserializer != null) {
344             return (Map<Object,Object>) _valueInstantiator.createUsingDelegate(ctxt,
345                     _delegateDeserializer.deserialize(p, ctxt));
346         }
347         if (!_hasDefaultCreator) {
348             return (Map<Object,Object> ) ctxt.handleMissingInstantiator(getMapClass(),
349                     getValueInstantiator(), p,
350                     "no default constructor found");
351         }
352         // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
353         JsonToken t = p.getCurrentToken();
354         if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
355             // (empty) String may be ok however; or single-String-arg ctor
356             if (t == JsonToken.VALUE_STRING) {
357                 return (Map<Object,Object>) _valueInstantiator.createFromString(ctxt, p.getText());
358             }
359             if (t == JsonToken.START_ARRAY) {
360                 if (p.nextToken() == JsonToken.END_ARRAY) {
361                     if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
362                         return null;
363                     }
364                 } else if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
365                     final Object value = deserialize(p, ctxt);
366                     if (p.nextToken() != JsonToken.END_ARRAY) {
367                         handleMissingEndArrayForSingle(p, ctxt);
368                     }
369                     return (Map<Object,Object>) value;
370                 }
371                 // fall through to failing case
372             }
373             return (Map<Object,Object>) ctxt.handleUnexpectedToken(getValueType(ctxt), t, p, null);
374         }
375         final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
376         if (_standardStringKey) {
377             _readAndBindStringKeyMap(p, ctxt, result);
378             return result;
379         }
380         _readAndBind(p, ctxt, result);
381         return result;
382     }
383
384     @SuppressWarnings("unchecked")
385     @Override
386     public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
387             Map<Object,Object> result)
388         throws IOException
389     {
390         // [databind#631]: Assign current value, to be accessible by custom deserializers
391         p.setCurrentValue(result);
392         
393         // Ok: must point to START_OBJECT or FIELD_NAME
394         JsonToken t = p.getCurrentToken();
395         if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) {
396             return (Map<Object,Object>) ctxt.handleUnexpectedToken(getMapClass(), p);
397         }
398         // 21-Apr-2017, tatu: Need separate methods to do proper merging
399         if (_standardStringKey) {
400             _readAndUpdateStringKeyMap(p, ctxt, result);
401             return result;
402         }
403         _readAndUpdate(p, ctxt, result);
404         return result;
405     }
406
407     @Override
408     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
409             TypeDeserializer typeDeserializer)
410         throws IOException
411     {
412         // In future could check current token... for now this should be enough:
413         return typeDeserializer.deserializeTypedFromObject(p, ctxt);
414     }
415
416     /*
417     /**********************************************************
418     /* Other public accessors
419     /**********************************************************
420      */

421
422     @SuppressWarnings("unchecked")
423     public final Class<?> getMapClass() { return (Class<Map<Object,Object>>) _containerType.getRawClass(); }
424
425     @Override public JavaType getValueType() { return _containerType; }
426
427     /*
428     /**********************************************************
429     /* Internal methods, non-merging deserialization
430     /**********************************************************
431      */

432
433     protected final void _readAndBind(JsonParser p, DeserializationContext ctxt,
434             Map<Object,Object> result) throws IOException
435     {
436         final KeyDeserializer keyDes = _keyDeserializer;
437         final JsonDeserializer<Object> valueDes = _valueDeserializer;
438         final TypeDeserializer typeDeser = _valueTypeDeserializer;
439         
440         MapReferringAccumulator referringAccumulator = null;
441         boolean useObjectId = valueDes.getObjectIdReader() != null;
442         if (useObjectId) {
443             referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(),
444                     result);
445         }
446
447         String keyStr;
448         if (p.isExpectedStartObjectToken()) {
449             keyStr = p.nextFieldName();
450         } else {
451             JsonToken t = p.getCurrentToken();
452             if (t != JsonToken.FIELD_NAME) {
453                 if (t == JsonToken.END_OBJECT) {
454                     return;
455                 }
456                 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
457             }
458             keyStr = p.getCurrentName();
459         }
460         
461         for (; keyStr != null; keyStr = p.nextFieldName()) {
462             Object key = keyDes.deserializeKey(keyStr, ctxt);
463             // And then the value...
464             JsonToken t = p.nextToken();
465             if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) {
466                 p.skipChildren();
467                 continue;
468             }
469             try {
470                 // Note: must handle null explicitly here; value deserializers won't
471                 Object value;
472                 if (t == JsonToken.VALUE_NULL) {
473                     if (_skipNullValues) {
474                         continue;
475                     }
476                     value = _nullProvider.getNullValue(ctxt);
477                 } else if (typeDeser == null) {
478                     value = valueDes.deserialize(p, ctxt);
479                 } else {
480                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
481                 }
482                 if (useObjectId) {
483                     referringAccumulator.put(key, value);
484                 } else {
485                     result.put(key, value);
486                 }
487             } catch (UnresolvedForwardReference reference) {
488                 handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
489             } catch (Exception e) {
490                 wrapAndThrow(e, result, keyStr);
491             }
492         }
493     }
494
495     /**
496      * Optimized method used when keys can be deserialized as plain old
497      * {@link java.lang.String}s, and there is no custom deserialized
498      * specified.
499      */

500     protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationContext ctxt,
501             Map<Object,Object> result) throws IOException
502     {
503         final JsonDeserializer<Object> valueDes = _valueDeserializer;
504         final TypeDeserializer typeDeser = _valueTypeDeserializer;
505         MapReferringAccumulator referringAccumulator = null;
506         boolean useObjectId = (valueDes.getObjectIdReader() != null);
507         if (useObjectId) {
508             referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result);
509         }
510         
511         String key;
512         if (p.isExpectedStartObjectToken()) {
513             key = p.nextFieldName();
514         } else {
515             JsonToken t = p.getCurrentToken();
516             if (t == JsonToken.END_OBJECT) {
517                 return;
518             }
519             if (t != JsonToken.FIELD_NAME) {
520                 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
521             }
522             key = p.getCurrentName();
523         }
524
525         for (; key != null; key = p.nextFieldName()) {
526             JsonToken t = p.nextToken();
527             if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
528                 p.skipChildren();
529                 continue;
530             }
531             try {
532                 // Note: must handle null explicitly here; value deserializers won't
533                 Object value;
534                 if (t == JsonToken.VALUE_NULL) {
535                     if (_skipNullValues) {
536                         continue;
537                     }
538                     value = _nullProvider.getNullValue(ctxt);
539                 } else if (typeDeser == null) {
540                     value = valueDes.deserialize(p, ctxt);
541                 } else {
542                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
543                 }
544                 if (useObjectId) {
545                     referringAccumulator.put(key, value);
546                 } else {
547                     result.put(key, value);
548                 }
549             } catch (UnresolvedForwardReference reference) {
550                 handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
551             } catch (Exception e) {
552                 wrapAndThrow(e, result, key);
553             }
554         }
555         // 23-Mar-2015, tatu: TODO: verify we got END_OBJECT?
556     }
557     
558     @SuppressWarnings("unchecked"
559     public Map<Object,Object> _deserializeUsingCreator(JsonParser p, DeserializationContext ctxt) throws IOException
560     {
561         final PropertyBasedCreator creator = _propertyBasedCreator;
562         // null -> no ObjectIdReader for Maps (yet?)
563         PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null);
564
565         final JsonDeserializer<Object> valueDes = _valueDeserializer;
566         final TypeDeserializer typeDeser = _valueTypeDeserializer;
567
568         String key;
569         if (p.isExpectedStartObjectToken()) {
570             key = p.nextFieldName();
571         } else if (p.hasToken(JsonToken.FIELD_NAME)) {
572             key = p.getCurrentName();
573         } else {
574             key = null;
575         }
576         
577         for (; key != null; key = p.nextFieldName()) {
578             JsonToken t = p.nextToken(); // to get to value
579             if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
580                 p.skipChildren(); // and skip it (in case of array/object)
581                 continue;
582             }
583             // creator property?
584             SettableBeanProperty prop = creator.findCreatorProperty(key);
585             if (prop != null) {
586                 // Last property to set?
587                 if (buffer.assignParameter(prop, prop.deserialize(p, ctxt))) {
588                     p.nextToken(); // from value to END_OBJECT or FIELD_NAME
589                     Map<Object,Object> result;
590                     try {
591                         result = (Map<Object,Object>)creator.build(ctxt, buffer);
592                     } catch (Exception e) {
593                         return wrapAndThrow(e, _containerType.getRawClass(), key);
594                     }
595                     _readAndBind(p, ctxt, result);
596                     return result;
597                 }
598                 continue;
599             }
600             // other property? needs buffering
601             Object actualKey = _keyDeserializer.deserializeKey(key, ctxt);
602             Object value; 
603
604             try {
605                 if (t == JsonToken.VALUE_NULL) {
606                     if (_skipNullValues) {
607                         continue;
608                     }
609                     value = _nullProvider.getNullValue(ctxt);
610                 } else if (typeDeser == null) {
611                     value = valueDes.deserialize(p, ctxt);
612                 } else {
613                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
614                 }
615             } catch (Exception e) {
616                 wrapAndThrow(e, _containerType.getRawClass(), key);
617                 return null;
618             }
619             buffer.bufferMapProperty(actualKey, value);
620         }
621         // end of JSON object?
622         // if so, can just construct and leave...
623         try {
624             return (Map<Object,Object>)creator.build(ctxt, buffer);
625         } catch (Exception e) {
626             wrapAndThrow(e, _containerType.getRawClass(), key);
627             return null;
628         }
629     }
630
631     /*
632     /**********************************************************
633     /* Internal methods, non-merging deserialization
634     /**********************************************************
635      */

636
637     /**
638      * @since 2.9
639      */

640     protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt,
641             Map<Object,Object> result) throws IOException
642     {
643         final KeyDeserializer keyDes = _keyDeserializer;
644         final JsonDeserializer<Object> valueDes = _valueDeserializer;
645         final TypeDeserializer typeDeser = _valueTypeDeserializer;
646
647         // Note: assumption is that Object Id handling can't really work with merging
648         // and thereby we can (and should) just drop that part
649
650         String keyStr;
651         if (p.isExpectedStartObjectToken()) {
652             keyStr = p.nextFieldName();
653         } else {
654             JsonToken t = p.getCurrentToken();
655             if (t == JsonToken.END_OBJECT) {
656                 return;
657             }
658             if (t != JsonToken.FIELD_NAME) {
659                 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
660             }
661             keyStr = p.getCurrentName();
662         }
663         
664         for (; keyStr != null; keyStr = p.nextFieldName()) {
665             Object key = keyDes.deserializeKey(keyStr, ctxt);
666             // And then the value...
667             JsonToken t = p.nextToken();
668             if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) {
669                 p.skipChildren();
670                 continue;
671             }
672             try {
673                 // Note: must handle null explicitly here, can't merge etc
674                 if (t == JsonToken.VALUE_NULL) {
675                     if (_skipNullValues) {
676                         continue;
677                     }
678                     result.put(key, _nullProvider.getNullValue(ctxt));
679                     continue;
680                 }
681                 Object old = result.get(key);
682                 Object value;
683                 if (old != null) {
684                     if (typeDeser == null) {
685                         value = valueDes.deserialize(p, ctxt, old);
686                     } else {
687                         value = valueDes.deserializeWithType(p, ctxt, typeDeser, old);
688                     }
689                 } else if (typeDeser == null) {
690                     value = valueDes.deserialize(p, ctxt);
691                 } else {
692                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
693                 }
694                 if (value != old) {
695                     result.put(key, value);
696                 }
697             } catch (Exception e) {
698                 wrapAndThrow(e, result, keyStr);
699             }
700         }
701     }
702
703     /**
704      * Optimized method used when keys can be deserialized as plain old
705      * {@link java.lang.String}s, and there is no custom deserializer
706      * specified.
707      *
708      * @since 2.9
709      */

710     protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationContext ctxt,
711             Map<Object,Object> result) throws IOException
712     {
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 key;
720         if (p.isExpectedStartObjectToken()) {
721             key = p.nextFieldName();
722         } else {
723             JsonToken t = p.getCurrentToken();
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             key = p.getCurrentName();
731         }
732
733         for (; key != null; key = p.nextFieldName()) {
734             JsonToken t = p.nextToken();
735             if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
736                 p.skipChildren();
737                 continue;
738             }
739             try {
740                 // Note: must handle null explicitly here, can't merge etc
741                 if (t == JsonToken.VALUE_NULL) {
742                     if (_skipNullValues) {
743                         continue;
744                     }
745                     result.put(key, _nullProvider.getNullValue(ctxt));
746                     continue;
747                 }
748                 Object old = result.get(key);
749                 Object value;
750                 if (old != null) {
751                     if (typeDeser == null) {
752                         value = valueDes.deserialize(p, ctxt, old);
753                     } else {
754                         value = valueDes.deserializeWithType(p, ctxt, typeDeser, old);
755                     }
756                 } else if (typeDeser == null) {
757                     value = valueDes.deserialize(p, ctxt);
758                 } else {
759                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
760                 }
761                 if (value != old) {
762                     result.put(key, value);
763                 }
764             } catch (Exception e) {
765                 wrapAndThrow(e, result, key);
766             }
767         }
768     }
769
770     /*
771     /**********************************************************
772     /* Internal methods, other
773     /**********************************************************
774      */

775
776     private void handleUnresolvedReference(DeserializationContext ctxt,
777             MapReferringAccumulator accumulator,
778             Object key, UnresolvedForwardReference reference)
779         throws JsonMappingException
780     {
781         if (accumulator == null) {
782             ctxt.reportInputMismatch(this,
783                     "Unresolved forward reference but no identity info: "+reference);
784         }
785         Referring referring = accumulator.handleUnresolvedReference(reference, key);
786         reference.getRoid().appendReferring(referring);
787     }
788
789     private final static class MapReferringAccumulator {
790         private final Class<?> _valueType;
791         private Map<Object,Object> _result;
792         /**
793          * A list of {@link MapReferring} to maintain ordering.
794          */

795         private List<MapReferring> _accumulator = new ArrayList<MapReferring>();
796
797         public MapReferringAccumulator(Class<?> valueType, Map<Object, Object> result) {
798             _valueType = valueType;
799             _result = result;
800         }
801
802         public void put(Object key, Object value)
803         {
804             if (_accumulator.isEmpty()) {
805                 _result.put(key, value);
806             } else {
807                 MapReferring ref = _accumulator.get(_accumulator.size() - 1);
808                 ref.next.put(key, value);
809             }
810         }
811
812         public Referring handleUnresolvedReference(UnresolvedForwardReference reference, Object key)
813         {
814             MapReferring id = new MapReferring(this, reference, _valueType, key);
815             _accumulator.add(id);
816             return id;
817         }
818
819         public void resolveForwardReference(Object id, Object value) throws IOException
820         {
821             Iterator<MapReferring> iterator = _accumulator.iterator();
822             // Resolve ordering after resolution of an id. This means either:
823             // 1- adding to the result map in case of the first unresolved id.
824             // 2- merge the content of the resolved id with its previous unresolved id.
825             Map<Object,Object> previous = _result;
826             while (iterator.hasNext()) {
827                 MapReferring ref = iterator.next();
828                 if (ref.hasId(id)) {
829                     iterator.remove();
830                     previous.put(ref.key, value);
831                     previous.putAll(ref.next);
832                     return;
833                 }
834                 previous = ref.next;
835             }
836
837             throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id
838                     + "] that wasn't previously seen as unresolved.");
839         }
840     }
841
842     /**
843      * Helper class to maintain processing order of value.
844      * The resolved object associated with {@link #key} comes before the values in
845      * {@link #next}.
846      */

847     static class MapReferring extends Referring {
848         private final MapReferringAccumulator _parent;
849
850         public final Map<Object, Object> next = new LinkedHashMap<Object, Object>();
851         public final Object key;
852         
853         MapReferring(MapReferringAccumulator parent, UnresolvedForwardReference ref,
854                 Class<?> valueType, Object key)
855         {
856             super(ref, valueType);
857             _parent = parent;
858             this.key = key;
859         }
860
861         @Override
862         public void handleResolvedForwardReference(Object id, Object value) throws IOException {
863             _parent.resolveForwardReference(id, value);
864         }
865     }
866 }
867