Skip to content

Commit c887f1c

Browse files
authored
Merge branch '3.x' into 5405-map-format-shape-bug
2 parents c544871 + eff875a commit c887f1c

File tree

4 files changed

+91
-105
lines changed

4 files changed

+91
-105
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ jobs:
117117
COMMENT_ID=$(gh pr view ${{ github.event.pull_request.number }} --json comments --jq '.comments[] | select(.body | contains("<!-- jacoco-coverage-comment -->")) | .id' | head -1)
118118
119119
if [ -n "$COMMENT_ID" ]; then
120-
gh api -X DELETE "repos/${{ github.repository }}/issues/comments/$COMMENT_ID"
120+
gh api -X DELETE "repos/${{ github.repository }}/issues/comments/$COMMENT_ID" || true
121121
fi
122122
123123
# Post new comment

src/main/java/tools/jackson/databind/deser/jdk/UntypedObjectDeserializerNR.java

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,11 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object into
144144
}
145145
break;
146146
case JsonTokenId.ID_START_ARRAY:
147-
{
148-
JsonToken t = p.nextToken(); // to get to FIELD_NAME or END_OBJECT
147+
if (intoValue instanceof Collection<?>) {
148+
JsonToken t = p.nextToken(); // to get to END_OBJECT
149149
if (t == JsonToken.END_ARRAY) {
150150
return intoValue;
151151
}
152-
}
153-
154-
if (intoValue instanceof Collection<?>) {
155152
Collection<Object> c = (Collection<Object>) intoValue;
156153
// NOTE: merge for arrays/Collections means append, can't merge contents
157154
do {
@@ -161,6 +158,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object into
161158
}
162159
// 21-Apr-2017, tatu: Should we try to support merging of Object[] values too?
163160
// ... maybe future improvement
161+
164162
break;
165163
}
166164
// Easiest handling for the rest, delegate. Only (?) question: how about nulls?
@@ -355,48 +353,6 @@ protected Object _deserializeFP(JsonParser p, DeserializationContext ctxt)
355353
}
356354
return p.getDoubleValue();
357355
}
358-
359-
// NOTE: copied from above (alas, no easy way to share/reuse)
360-
// @since 2.12 (wrt [databind#2733]
361-
protected Object _mapObjectWithDups(JsonParser p, DeserializationContext ctxt,
362-
final Map<String, Object> result, String initialKey,
363-
Object oldValue, Object newValue, String nextKey)
364-
throws JacksonException
365-
{
366-
final boolean squashDups = ctxt.isEnabled(StreamReadCapability.DUPLICATE_PROPERTIES);
367-
368-
if (squashDups) {
369-
_squashDups(result, initialKey, oldValue, newValue);
370-
}
371-
372-
while (nextKey != null) {
373-
p.nextToken();
374-
newValue = deserialize(p, ctxt);
375-
oldValue = result.put(nextKey, newValue);
376-
if ((oldValue != null) && squashDups) {
377-
_squashDups(result, nextKey, oldValue, newValue);
378-
}
379-
nextKey = p.nextName();
380-
}
381-
382-
return result;
383-
}
384-
385-
// NOTE: copied from above (alas, no easy way to share/reuse)
386-
@SuppressWarnings("unchecked")
387-
private void _squashDups(final Map<String, Object> result, String key,
388-
Object oldValue, Object newValue)
389-
{
390-
if (oldValue instanceof List<?>) {
391-
((List<Object>) oldValue).add(newValue);
392-
result.put(key, oldValue);
393-
} else {
394-
ArrayList<Object> l = new ArrayList<>();
395-
l.add(oldValue);
396-
l.add(newValue);
397-
result.put(key, l);
398-
}
399-
}
400356

401357
/*
402358
/**********************************************************************

src/test/java/tools/jackson/databind/deser/jdk/UntypedDeserializationTest.java

Lines changed: 79 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import tools.jackson.databind.testutil.NoCheckSubTypeValidator;
2424

2525
import static org.junit.jupiter.api.Assertions.*;
26+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
2627

2728
import static tools.jackson.databind.testutil.DatabindTestUtil.*;
2829

@@ -215,7 +216,7 @@ public void testUntypedMap() throws Exception
215216
}
216217

217218
@Test
218-
public void testSimpleVanillaScalars() throws IOException
219+
public void testSimpleVanillaScalars() throws Exception
219220
{
220221
assertEquals("foo", MAPPER.readValue(q("foo"), Object.class));
221222

@@ -226,14 +227,14 @@ public void testSimpleVanillaScalars() throws IOException
226227
}
227228

228229
@Test
229-
public void testSimpleVanillaStructured() throws IOException
230+
public void testSimpleVanillaStructured() throws Exception
230231
{
231232
List<?> list = (List<?>) MAPPER.readValue("[ 1, 2, 3]", Object.class);
232233
assertEquals(Integer.valueOf(1), list.get(0));
233234
}
234235

235236
@Test
236-
public void testNestedUntypes() throws IOException
237+
public void testNestedUntypes() throws Exception
237238
{
238239
// 05-Apr-2014, tatu: Odd failures if using shared mapper; so work around:
239240
Object root = MAPPER.readValue(a2q("{'a':3,'b':[1,2]}"),
@@ -250,7 +251,7 @@ public void testNestedUntypes() throws IOException
250251
}
251252

252253
@Test
253-
public void testUntypedWithCustomScalarDesers() throws IOException
254+
public void testUntypedWithCustomScalarDesers() throws Exception
254255
{
255256
SimpleModule m = new SimpleModule("test-module");
256257
m.addDeserializer(String.class, new UCStringDeserializer());
@@ -274,31 +275,51 @@ public void testUntypedWithCustomScalarDesers() throws IOException
274275

275276
// Test that exercises non-vanilla variant, with just one simple custom deserializer
276277
@Test
277-
public void testNonVanilla() throws IOException
278+
public void testNonVanilla() throws Exception
278279
{
279280
SimpleModule m = new SimpleModule("test-module");
280281
m.addDeserializer(String.class, new UCStringDeserializer());
281282
final ObjectMapper mapper = jsonMapperBuilder()
282283
.polymorphicTypeValidator(new NoCheckSubTypeValidator())
283284
.addModule(m)
284285
.build();
286+
ObjectReader r = mapper.readerFor(Object.class);
285287
// Also: since this is now non-vanilla variant, try more alternatives
286-
List<?> l = (List<?>) mapper.readValue("[ true, false, 7, 0.5, \"foo\"]", Object.class);
287-
assertEquals(5, l.size());
288+
List<?> l = (List<?>) r.readValue("[ true, false, 7, 0.5, \"foo\", null]");
289+
assertEquals(6, l.size());
288290
assertEquals(Boolean.TRUE, l.get(0));
289291
assertEquals(Boolean.FALSE, l.get(1));
290292
assertEquals(Integer.valueOf(7), l.get(2));
291293
assertEquals(Double.valueOf(0.5), l.get(3));
292294
assertEquals("FOO", l.get(4));
293-
295+
assertNull(l.get(5));
296+
297+
// And Maps
298+
Map<?,?> map = (Map<?,?>) r.readValue(a2q("{'a':0.25,'b':3,'c':true,'d':false}"));
299+
assertEquals(Map.of("a", 0.25, "b", 3, "c", true, "d", false), map);
300+
301+
// And Scalars too; regular and "updating" readers
302+
l = new ArrayList<>();
303+
assertEquals(Integer.valueOf(42), r.readValue("42"));
304+
assertEquals(Integer.valueOf(42), r.withValueToUpdate(l).readValue("42"));
305+
assertEquals(Double.valueOf(2.5), r.readValue("2.5"));
306+
assertEquals(Double.valueOf(2.5), r.withValueToUpdate(l).readValue("2.5"));
307+
assertEquals(true, r.readValue("true"));
308+
assertEquals(true, r.withValueToUpdate(l).readValue("true"));
309+
assertEquals(false, r.readValue("false"));
310+
assertEquals(false, r.withValueToUpdate(l).readValue("false"));
311+
assertNull(r.readValue("null"));
312+
assertSame(l, r.withValueToUpdate(l).readValue("null"));
313+
314+
// and minimal nesting
294315
l = (List<?>) mapper.readValue("[ {}, [] ]", Object.class);
295316
assertEquals(2, l.size());
296-
assertTrue(l.get(0) instanceof Map<?,?>);
297-
assertTrue(l.get(1) instanceof List<?>);
317+
assertEquals(Map.of(), l.get(0));
318+
assertEquals(List.of(), l.get(1));
298319
}
299320

300321
@Test
301-
public void testUntypedWithListDeser() throws IOException
322+
public void testUntypedWithListDeser() throws Exception
302323
{
303324
SimpleModule m = new SimpleModule("test-module");
304325
m.addDeserializer(List.class, new ListDeserializer());
@@ -316,7 +337,7 @@ public void testUntypedWithListDeser() throws IOException
316337
}
317338

318339
@Test
319-
public void testUntypedWithMapDeser() throws IOException
340+
public void testUntypedWithMapDeser() throws Exception
320341
{
321342
SimpleModule m = new SimpleModule("test-module");
322343
m.addDeserializer(Map.class, new YMapDeserializer());
@@ -332,7 +353,7 @@ public void testUntypedWithMapDeser() throws IOException
332353
}
333354

334355
@Test
335-
public void testNestedUntyped989() throws IOException
356+
public void testNestedUntyped989() throws Exception
336357
{
337358
DelegatingUntyped pojo;
338359
ObjectReader r = MAPPER.readerFor(DelegatingUntyped.class);
@@ -359,8 +380,10 @@ public void testUntypedWithJsonArrays() throws Exception
359380
ObjectMapper mapper = jsonMapperBuilder()
360381
.enable(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)
361382
.build();
362-
ob = mapper.readValue("[1]", Object.class);
383+
ob = mapper.readValue("[1, false, true, 0.5, {}]", Object.class);
363384
assertEquals(Object[].class, ob.getClass());
385+
assertEquals(List.of(1, false, true, 0.5, Map.of()),
386+
Arrays.asList((Object[]) ob));
364387
}
365388

366389
@Test
@@ -411,20 +434,43 @@ public void testValueUpdateVanillaUntyped() throws Exception
411434
Map<String, Object> map = new LinkedHashMap<>();
412435
map.put("a", 42);
413436

437+
// First update Map with JSON Object
414438
ObjectReader r = MAPPER.readerFor(Object.class).withValueToUpdate(map);
415-
Object result = r.readValue(a2q("{'b' : 57}"));
439+
Object result;
440+
441+
result = r.readValue(a2q("{'b': 0.25, 'c': [] }"));
416442
assertSame(map, result);
417-
assertEquals(2, map.size());
418-
assertEquals(Integer.valueOf(57), map.get("b"));
443+
assertEquals(3, map.size());
444+
assertEquals(0.25, map.get("b"));
445+
assertEquals(List.of(), map.get("c"));
419446

420-
// Try same with other types, too
447+
// Then List with Array
421448
List<Object> list = new ArrayList<>();
422449
list.add(1);
423450
r = MAPPER.readerFor(Object.class).withValueToUpdate(list);
424-
result = r.readValue("[ 2, true ]");
451+
result = r.readValue("[ true, -0.5, { } ]");
425452
assertSame(list, result);
426-
assertEquals(3, list.size());
427-
assertEquals(Boolean.TRUE, list.get(2));
453+
assertEquals(List.of(1, true, -0.5, Map.of()), result);
454+
455+
// Then mismatches: Map with JSON Array
456+
r = MAPPER.readerFor(Object.class)
457+
.withValueToUpdate(map);
458+
result = r.readValue("[ 42, -0.25, false, null ]");
459+
List<Object> exp = new ArrayList<>();
460+
exp.add(42);
461+
exp.add(-0.25);
462+
exp.add(false);
463+
exp.add(null);
464+
assertEquals(exp, result);
465+
466+
// And then List with JSON Object
467+
r = MAPPER.readerFor(Object.class)
468+
.withValueToUpdate(new ArrayList<>());
469+
map.clear();
470+
map.put("a", 0.5);
471+
map.put("b", null);
472+
result = r.readValue(a2q("{'a': 0.5, 'b': null}"));
473+
assertEquals(map, result);
428474
}
429475

430476
@Test
@@ -465,7 +511,7 @@ public void testValueUpdateCustomUntyped() throws Exception
465511

466512
// Allow 'upgrade' of big integers into Long, BigInteger
467513
@Test
468-
public void testObjectSerializeWithLong() throws IOException
514+
public void testObjectSerializeWithLong() throws Exception
469515
{
470516
final ObjectMapper mapper = jsonMapperBuilder()
471517
.activateDefaultTyping(NoCheckSubTypeValidator.instance,
@@ -486,7 +532,7 @@ public void testObjectSerializeWithLong() throws IOException
486532
}
487533

488534
@Test
489-
public void testPolymorphicUntypedVanilla() throws IOException
535+
public void testPolymorphicUntypedVanilla() throws Exception
490536
{
491537
ObjectReader rDefault = jsonMapperBuilder()
492538
.polymorphicTypeValidator(new NoCheckSubTypeValidator())
@@ -523,10 +569,14 @@ public void testPolymorphicUntypedVanilla() throws IOException
523569
for (int i = 0; i < 100; ++i) {
524570
assertEquals(Integer.valueOf(i), obs[i]);
525571
}
572+
573+
// Finally, true polymorphism
574+
w = rDefault.readValue(a2q("{'value': ['java.util.Date', 123]}"));
575+
assertThat(w.value).isInstanceOf(java.util.Date.class);
526576
}
527577

528578
@Test
529-
public void testPolymorphicUntypedCustom() throws IOException
579+
public void testPolymorphicUntypedCustom() throws Exception
530580
{
531581
// register module just to override one deserializer, to prevent use of Vanilla deser
532582
SimpleModule m = new SimpleModule("test-module")
@@ -569,37 +619,16 @@ public void testPolymorphicUntypedCustom() throws IOException
569619
@Test
570620
public void testEmptyArrayAndObject() throws Exception
571621
{
572-
// Empty array
573-
Object result = MAPPER.readValue("[]", Object.class);
574-
assertNotNull(result);
575-
assertTrue(result instanceof List);
576-
assertEquals(0, ((List<?>) result).size());
577-
578-
// Empty object
579-
result = MAPPER.readValue("{}", Object.class);
580-
assertNotNull(result);
581-
assertTrue(result instanceof Map);
582-
assertEquals(0, ((Map<?,?>) result).size());
622+
assertEquals(List.of(), MAPPER.readValue("[]", Object.class));
623+
assertEquals(Map.of(), MAPPER.readValue("{}", Object.class));
583624
}
584625

585626
@Test
586627
public void testSingleElementArrayAndObject() throws Exception
587628
{
588-
// Single element array
589-
Object result = MAPPER.readValue("[42]", Object.class);
590-
assertNotNull(result);
591-
assertTrue(result instanceof List);
592-
List<?> list = (List<?>) result;
593-
assertEquals(1, list.size());
594-
assertEquals(Integer.valueOf(42), list.get(0));
595-
596-
// Single property object
597-
result = MAPPER.readValue("{\"key\":\"value\"}", Object.class);
598-
assertNotNull(result);
599-
assertTrue(result instanceof Map);
600-
Map<?,?> map = (Map<?,?>) result;
601-
assertEquals(1, map.size());
602-
assertEquals("value", map.get("key"));
629+
assertEquals(List.of(42), MAPPER.readValue("[42]", Object.class));
630+
assertEquals(Map.of("key", true),
631+
MAPPER.readValue("{\"key\": true}", Object.class));
603632
}
604633

605634
@Test
@@ -618,7 +647,6 @@ public void testFloatDeserializationWithBigDecimal() throws Exception
618647
@Test
619648
public void testFloatTypesFloat32AndFloat64() throws Exception
620649
{
621-
// Test Float (32-bit)
622650
Object result = MAPPER.readValue("1.5", Object.class);
623651
assertTrue(result instanceof Double);
624652
result = MAPPER.readValue("2.718281828", Object.class);

src/test/java/tools/jackson/databind/interop/UntypedObjectWithDupsTest.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ static class StringStringMap extends LinkedHashMap<String,String> { };
2424
private final String DOC_WITH_DUPS = a2q(
2525
"{'hello': 'world',\n"
2626
+ "'lists' : 1,\n"
27-
+ "'lists' : 2,\n"
27+
+ "'lists' : 2.5,\n"
2828
+ "'lists' : {\n"
2929
+ " 'inner' : 'internal',\n"
3030
+ " 'time' : 123\n"
3131
+ "},\n"
32-
+ "'lists' : 3,\n"
33-
+ "'single' : 'one'\n"
32+
+ "'lists' : true,\n"
33+
+ "'single' : 'one',\n"
34+
+ "'lists' : false,\n"
35+
+ "'lists' : null\n"
3436
+ "}");
3537

3638
// Testing the baseline non-merging behavior
@@ -84,7 +86,7 @@ private <T> void _verifyDupsNoMerging(Class<T> cls) throws Exception
8486

8587
String json = JSON_MAPPER.writeValueAsString(value);
8688
assertEquals(a2q(
87-
"{'hello':'world','lists':3,'single':'one'}"),
89+
"{'hello':'world','lists':null,'single':'one'}"),
8890
json);
8991
}
9092

@@ -94,8 +96,8 @@ private <T> void _verifyDupsNoMerging(Class<T> cls) throws Exception
9496
private void _verifyDupsAreMerged(Class<?> cls) throws Exception
9597
{
9698
assertEquals(a2q(
97-
"{'hello':'world','lists':[1,2,"
98-
+"{'inner':'internal','time':123},3],'single':'one'}"),
99+
"{'hello':'world','lists':[1,2.5,"
100+
+"{'inner':'internal','time':123},true,false,null],'single':'one'}"),
99101
_readWriteDupDoc(DOC_WITH_DUPS, cls));
100102
}
101103

0 commit comments

Comments
 (0)