@@ -101,6 +101,15 @@ public class MapDeserializer
101
101
*/
102
102
protected IgnorePropertiesUtil .Checker _inclusionChecker ;
103
103
104
+
105
+ /**
106
+ * Flag used to check, whether the {@link com.fasterxml.jackson.core.StreamReadCapability#DUPLICATE_PROPERTIES}
107
+ * can be applied, because the Map has declared value type of {@code java.lang.Object}.
108
+ *
109
+ * @since 2.14
110
+ */
111
+ protected boolean _checkDupSquash ;
112
+
104
113
/*
105
114
/**********************************************************
106
115
/* Life-cycle
@@ -121,6 +130,7 @@ public MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator,
121
130
_propertyBasedCreator = null ;
122
131
_standardStringKey = _isStdKeyDeser (mapType , keyDeser );
123
132
_inclusionChecker = null ;
133
+ _checkDupSquash = mapType .getContentType ().hasRawClass (Object .class );
124
134
}
125
135
126
136
/**
@@ -143,6 +153,7 @@ protected MapDeserializer(MapDeserializer src)
143
153
_inclusionChecker = src ._inclusionChecker ;
144
154
145
155
_standardStringKey = src ._standardStringKey ;
156
+ _checkDupSquash = src ._checkDupSquash ;
146
157
}
147
158
148
159
protected MapDeserializer (MapDeserializer src ,
@@ -177,6 +188,7 @@ protected MapDeserializer(MapDeserializer src,
177
188
_inclusionChecker = IgnorePropertiesUtil .buildCheckerIfNeeded (ignorable , includable );
178
189
179
190
_standardStringKey = _isStdKeyDeser (_containerType , keyDeser );
191
+ _checkDupSquash = src ._checkDupSquash ;
180
192
}
181
193
182
194
/**
@@ -434,11 +446,9 @@ public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt)
434
446
case JsonTokenId .ID_FIELD_NAME :
435
447
final Map <Object ,Object > result = (Map <Object ,Object >) _valueInstantiator .createUsingDefault (ctxt );
436
448
if (_standardStringKey ) {
437
- _readAndBindStringKeyMap (p , ctxt , result );
438
- return result ;
449
+ return _readAndBindStringKeyMap (p , ctxt , result );
439
450
}
440
- _readAndBind (p , ctxt , result );
441
- return result ;
451
+ return _readAndBind (p , ctxt , result );
442
452
case JsonTokenId .ID_STRING :
443
453
// (empty) String may be ok however; or single-String-arg ctor
444
454
return _deserializeFromString (p , ctxt );
@@ -499,7 +509,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
499
509
/**********************************************************
500
510
*/
501
511
502
- protected final void _readAndBind (JsonParser p , DeserializationContext ctxt ,
512
+ protected final Map < Object , Object > _readAndBind (JsonParser p , DeserializationContext ctxt ,
503
513
Map <Object ,Object > result ) throws IOException
504
514
{
505
515
final KeyDeserializer keyDes = _keyDeserializer ;
@@ -520,7 +530,7 @@ protected final void _readAndBind(JsonParser p, DeserializationContext ctxt,
520
530
JsonToken t = p .currentToken ();
521
531
if (t != JsonToken .FIELD_NAME ) {
522
532
if (t == JsonToken .END_OBJECT ) {
523
- return ;
533
+ return result ;
524
534
}
525
535
ctxt .reportWrongTokenException (this , JsonToken .FIELD_NAME , null );
526
536
}
@@ -551,22 +561,26 @@ protected final void _readAndBind(JsonParser p, DeserializationContext ctxt,
551
561
if (useObjectId ) {
552
562
referringAccumulator .put (key , value );
553
563
} else {
554
- result .put (key , value );
564
+ Object oldValue = result .put (key , value );
565
+ if (oldValue != null ) {
566
+ _squashDups (ctxt , result , key , oldValue , value );
567
+ }
555
568
}
556
569
} catch (UnresolvedForwardReference reference ) {
557
570
handleUnresolvedReference (ctxt , referringAccumulator , key , reference );
558
571
} catch (Exception e ) {
559
572
wrapAndThrow (ctxt , e , result , keyStr );
560
573
}
561
574
}
575
+ return result ;
562
576
}
563
577
564
578
/**
565
579
* Optimized method used when keys can be deserialized as plain old
566
580
* {@link java.lang.String}s, and there is no custom deserialized
567
581
* specified.
568
582
*/
569
- protected final void _readAndBindStringKeyMap (JsonParser p , DeserializationContext ctxt ,
583
+ protected final Map < Object , Object > _readAndBindStringKeyMap (JsonParser p , DeserializationContext ctxt ,
570
584
Map <Object ,Object > result ) throws IOException
571
585
{
572
586
final JsonDeserializer <Object > valueDes = _valueDeserializer ;
@@ -583,7 +597,7 @@ protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationConte
583
597
} else {
584
598
JsonToken t = p .currentToken ();
585
599
if (t == JsonToken .END_OBJECT ) {
586
- return ;
600
+ return result ;
587
601
}
588
602
if (t != JsonToken .FIELD_NAME ) {
589
603
ctxt .reportWrongTokenException (this , JsonToken .FIELD_NAME , null );
@@ -613,7 +627,10 @@ protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationConte
613
627
if (useObjectId ) {
614
628
referringAccumulator .put (key , value );
615
629
} else {
616
- result .put (key , value );
630
+ Object oldValue = result .put (key , value );
631
+ if (oldValue != null ) {
632
+ _squashDups (ctxt , result , key , oldValue , value );
633
+ }
617
634
}
618
635
} catch (UnresolvedForwardReference reference ) {
619
636
handleUnresolvedReference (ctxt , referringAccumulator , key , reference );
@@ -622,6 +639,8 @@ protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationConte
622
639
}
623
640
}
624
641
// 23-Mar-2015, tatu: TODO: verify we got END_OBJECT?
642
+
643
+ return result ;
625
644
}
626
645
627
646
@ SuppressWarnings ("unchecked" )
@@ -661,8 +680,7 @@ public Map<Object,Object> _deserializeUsingCreator(JsonParser p, Deserialization
661
680
} catch (Exception e ) {
662
681
return wrapAndThrow (ctxt , e , _containerType .getRawClass (), key );
663
682
}
664
- _readAndBind (p , ctxt , result );
665
- return result ;
683
+ return _readAndBind (p , ctxt , result );
666
684
}
667
685
continue ;
668
686
}
@@ -836,6 +854,27 @@ protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationCon
836
854
}
837
855
}
838
856
857
+ /**
858
+ * @since 2.14
859
+ */
860
+ @ SuppressWarnings ("unchecked" )
861
+ protected void _squashDups (final DeserializationContext ctxt ,
862
+ final Map <Object , Object > result ,
863
+ final Object key , final Object oldValue , final Object newValue )
864
+ {
865
+ if (_checkDupSquash && ctxt .isEnabled (StreamReadCapability .DUPLICATE_PROPERTIES )) {
866
+ if (oldValue instanceof List <?>) {
867
+ ((List <Object >) oldValue ).add (newValue );
868
+ result .put (key , oldValue );
869
+ } else {
870
+ ArrayList <Object > l = new ArrayList <>();
871
+ l .add (oldValue );
872
+ l .add (newValue );
873
+ result .put (key , l );
874
+ }
875
+ }
876
+ }
877
+
839
878
/*
840
879
/**********************************************************
841
880
/* Internal methods, other
0 commit comments