@@ -197,9 +197,11 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
197
197
&& getClass () == UntypedObjectDeserializer .class ) {
198
198
return Vanilla .instance (preventMerge );
199
199
}
200
+
200
201
if (preventMerge != _nonMerging ) {
201
202
return new UntypedObjectDeserializer (this , preventMerge );
202
203
}
204
+
203
205
return this ;
204
206
}
205
207
@@ -501,44 +503,92 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOE
501
503
}
502
504
if (key1 == null ) {
503
505
// empty map might work; but caller may want to modify... so better just give small modifiable
504
- return new LinkedHashMap <String , Object >(2 );
506
+ return new LinkedHashMap <>(2 );
505
507
}
506
508
// minor optimization; let's handle 1 and 2 entry cases separately
507
509
// 24-Mar-2015, tatu: Ideally, could use one of 'nextXxx()' methods, but for
508
510
// that we'd need new method(s) in JsonDeserializer. So not quite yet.
509
511
p .nextToken ();
510
512
Object value1 = deserialize (p , ctxt );
511
-
512
513
String key2 = p .nextFieldName ();
513
514
if (key2 == null ) { // has to be END_OBJECT, then
514
515
// single entry; but we want modifiable
515
- LinkedHashMap <String , Object > result = new LinkedHashMap <String , Object >(2 );
516
+ LinkedHashMap <String , Object > result = new LinkedHashMap <>(2 );
516
517
result .put (key1 , value1 );
517
518
return result ;
518
519
}
519
520
p .nextToken ();
520
521
Object value2 = deserialize (p , ctxt );
521
522
522
523
String key = p .nextFieldName ();
523
-
524
524
if (key == null ) {
525
- LinkedHashMap <String , Object > result = new LinkedHashMap <String , Object >(4 );
525
+ LinkedHashMap <String , Object > result = new LinkedHashMap <>(4 );
526
526
result .put (key1 , value1 );
527
- result .put (key2 , value2 );
527
+ if (result .put (key2 , value2 ) != null ) {
528
+ // 22-May-2020, tatu: [databind#2733] may need extra handling
529
+ return _mapObjectWithDups (p , ctxt , result , key1 , value1 , value2 , key );
530
+ }
528
531
return result ;
529
532
}
530
533
// And then the general case; default map size is 16
531
- LinkedHashMap <String , Object > result = new LinkedHashMap <String , Object >();
534
+ LinkedHashMap <String , Object > result = new LinkedHashMap <>();
532
535
result .put (key1 , value1 );
533
- result .put (key2 , value2 );
536
+ if (result .put (key2 , value2 ) != null ) {
537
+ // 22-May-2020, tatu: [databind#2733] may need extra handling
538
+ return _mapObjectWithDups (p , ctxt , result , key1 , value1 , value2 , key );
539
+ }
534
540
535
541
do {
536
542
p .nextToken ();
537
- result .put (key , deserialize (p , ctxt ));
543
+ final Object newValue = deserialize (p , ctxt );
544
+ final Object oldValue = result .put (key , newValue );
545
+ if (oldValue != null ) {
546
+ return _mapObjectWithDups (p , ctxt , result , key , oldValue , newValue ,
547
+ p .nextFieldName ());
548
+ }
538
549
} while ((key = p .nextFieldName ()) != null );
539
550
return result ;
540
551
}
541
552
553
+ // @since 2.12 (wrt [databind#2733]
554
+ protected Object _mapObjectWithDups (JsonParser p , DeserializationContext ctxt ,
555
+ final Map <String , Object > result , String key ,
556
+ Object oldValue , Object newValue , String nextKey ) throws IOException
557
+ {
558
+ final boolean squashDups = ctxt .isEnabled (StreamReadCapability .DUPLICATE_PROPERTIES );
559
+
560
+ if (squashDups ) {
561
+ _squashDups (result , key , oldValue , newValue );
562
+ }
563
+
564
+ while (nextKey != null ) {
565
+ p .nextToken ();
566
+ newValue = deserialize (p , ctxt );
567
+ oldValue = result .put (nextKey , newValue );
568
+ if ((oldValue != null ) && squashDups ) {
569
+ _squashDups (result , key , oldValue , newValue );
570
+ }
571
+ nextKey = p .nextFieldName ();
572
+ }
573
+
574
+ return result ;
575
+ }
576
+
577
+ @ SuppressWarnings ("unchecked" )
578
+ private void _squashDups (final Map <String , Object > result , String key ,
579
+ Object oldValue , Object newValue )
580
+ {
581
+ if (oldValue instanceof List <?>) {
582
+ ((List <Object >) oldValue ).add (newValue );
583
+ result .put (key , oldValue );
584
+ } else {
585
+ ArrayList <Object > l = new ArrayList <>();
586
+ l .add (oldValue );
587
+ l .add (newValue );
588
+ result .put (key , l );
589
+ }
590
+ }
591
+
542
592
/**
543
593
* Method called to map a JSON Array into a Java Object array (Object[]).
544
594
*/
@@ -881,18 +931,72 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOE
881
931
if (key == null ) {
882
932
LinkedHashMap <String , Object > result = new LinkedHashMap <String , Object >(4 );
883
933
result .put (key1 , value1 );
884
- result .put (key2 , value2 );
934
+ if (result .put (key2 , value2 ) != null ) {
935
+ // 22-May-2020, tatu: [databind#2733] may need extra handling
936
+ return _mapObjectWithDups (p , ctxt , result , key1 , value1 , value2 , key );
937
+ }
885
938
return result ;
886
939
}
887
940
// And then the general case; default map size is 16
888
941
LinkedHashMap <String , Object > result = new LinkedHashMap <String , Object >();
889
942
result .put (key1 , value1 );
890
- result .put (key2 , value2 );
943
+ if (result .put (key2 , value2 ) != null ) {
944
+ // 22-May-2020, tatu: [databind#2733] may need extra handling
945
+ return _mapObjectWithDups (p , ctxt , result , key1 , value1 , value2 , key );
946
+ }
947
+
891
948
do {
892
949
p .nextToken ();
893
- result .put (key , deserialize (p , ctxt ));
950
+ final Object newValue = deserialize (p , ctxt );
951
+ final Object oldValue = result .put (key , newValue );
952
+ if (oldValue != null ) {
953
+ return _mapObjectWithDups (p , ctxt , result , key , oldValue , newValue ,
954
+ p .nextFieldName ());
955
+ }
894
956
} while ((key = p .nextFieldName ()) != null );
895
957
return result ;
896
958
}
959
+
960
+ // NOTE: copied from above (alas, no easy way to share/reuse)
961
+ // @since 2.12 (wrt [databind#2733]
962
+ protected Object _mapObjectWithDups (JsonParser p , DeserializationContext ctxt ,
963
+ final Map <String , Object > result , String key ,
964
+ Object oldValue , Object newValue , String nextKey ) throws IOException
965
+ {
966
+ final boolean squashDups = ctxt .isEnabled (StreamReadCapability .DUPLICATE_PROPERTIES );
967
+
968
+ if (squashDups ) {
969
+ _squashDups (result , key , oldValue , newValue );
970
+ }
971
+
972
+ while (nextKey != null ) {
973
+ p .nextToken ();
974
+ newValue = deserialize (p , ctxt );
975
+ oldValue = result .put (nextKey , newValue );
976
+ if ((oldValue != null ) && squashDups ) {
977
+ _squashDups (result , key , oldValue , newValue );
978
+ }
979
+ nextKey = p .nextFieldName ();
980
+ }
981
+
982
+ return result ;
983
+ }
984
+
985
+ // NOTE: copied from above (alas, no easy way to share/reuse)
986
+ @ SuppressWarnings ("unchecked" )
987
+ private void _squashDups (final Map <String , Object > result , String key ,
988
+ Object oldValue , Object newValue )
989
+ {
990
+ if (oldValue instanceof List <?>) {
991
+ ((List <Object >) oldValue ).add (newValue );
992
+ result .put (key , oldValue );
993
+ } else {
994
+ ArrayList <Object > l = new ArrayList <>();
995
+ l .add (oldValue );
996
+ l .add (newValue );
997
+ result .put (key , l );
998
+ }
999
+ }
1000
+
897
1001
}
898
1002
}
0 commit comments