From 8c128f79f84716da82f4032310682000ff2ae052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20=27Bluexin=27=20Sol=C3=A9?= Date: Thu, 2 Jun 2022 23:03:47 +0200 Subject: [PATCH 1/3] Fix deduction deserializer with DefaultTypeResolverBuilder --- .../deser/BasicDeserializerFactory.java | 5 +- ...FactoryWithDefaultTypeResolverBuilder.java | 69 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java index e8ca72191e..a152cf5535 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -1784,15 +1784,14 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config, // Ok: if there is no explicit type info handler, we may want to // use a default. If so, config object knows what to use. - Collection subtypes = null; if (b == null) { b = config.getDefaultTyper(baseType); if (b == null) { return null; } - } else { - subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, ac); } + final Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, ac); + // May need to figure out default implementation, if none found yet // (note: check for abstract type is not 100% mandatory, more of an optimization) if ((b.getDefaultImpl() == null) && baseType.isAbstract()) { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java new file mode 100644 index 0000000000..dafb48574f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java @@ -0,0 +1,69 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +// Tests for [databind#pending] causing a NPE when setting a DefaultTypeResolverBuilder +// and registering subtypes through ObjectMapper (no annotations) +public class TestDeserializerFactoryWithDefaultTypeResolverBuilder extends BaseMapTest { + private final ObjectMapper MAPPER = sharedMapper(); + + private interface Parent { + class ChildOne implements Parent { + public String one; + } + + class ChildTwo implements Parent { + public String two; + } + } + + // This class is technically not needed for the test to fail without the fix + // (AsDeductionTypeDeserializer will crash in #buildFingerprints), but was + // added to have more assertions on the subtypes values + private static final class AssertingTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder { + + public AssertingTypeResolverBuilder() { + super(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS, + BasicPolymorphicTypeValidator.builder().allowIfSubType(Parent.class).build()); + } + + @Override + public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection subtypes) { + // this also gets called for String, not sure if that's expected with PTV + // this behaviour is outside the scope of this test + if (baseType.isAbstract()) { + assertNotNull(subtypes); + assertEquals(2, subtypes.size()); + final List expected = new ArrayList<>(2); + expected.add(new NamedType(Parent.ChildOne.class)); + expected.add(new NamedType(Parent.ChildTwo.class)); + assertTrue(subtypes.containsAll(expected)); + } + + return super.buildTypeDeserializer(config, baseType, subtypes); + } + } + + public void testDeductionWithDefaultTypeResolverBuilder() throws Exception { + MAPPER.registerSubtypes(Parent.ChildOne.class, Parent.ChildTwo.class); + MAPPER.setDefaultTyping(new AssertingTypeResolverBuilder() + .init(JsonTypeInfo.Id.DEDUCTION, null)); + + try { + MAPPER.readValue("{ \"one\": \"Hello World\" }", Parent.class); + } catch (Exception e) { + fail("This call should not throw"); + } + } +} From 6bee66e11bc0431c0118592eb74ad62736b56b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20=27Bluexin=27=20Sol=C3=A9?= Date: Sat, 1 Oct 2022 02:04:21 +0200 Subject: [PATCH 2/3] Fix tests --- ...alizerFactoryWithDefaultTypeResolverBuilder.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java index dafb48574f..1c56214238 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java @@ -16,7 +16,6 @@ // Tests for [databind#pending] causing a NPE when setting a DefaultTypeResolverBuilder // and registering subtypes through ObjectMapper (no annotations) public class TestDeserializerFactoryWithDefaultTypeResolverBuilder extends BaseMapTest { - private final ObjectMapper MAPPER = sharedMapper(); private interface Parent { class ChildOne implements Parent { @@ -55,13 +54,15 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, Java } } - public void testDeductionWithDefaultTypeResolverBuilder() throws Exception { - MAPPER.registerSubtypes(Parent.ChildOne.class, Parent.ChildTwo.class); - MAPPER.setDefaultTyping(new AssertingTypeResolverBuilder() - .init(JsonTypeInfo.Id.DEDUCTION, null)); + public void testDeductionWithDefaultTypeResolverBuilder() { + final ObjectMapper mapper = jsonMapperBuilder() + .registerSubtypes(Parent.ChildOne.class, Parent.ChildTwo.class) + .setDefaultTyping(new AssertingTypeResolverBuilder() + .init(JsonTypeInfo.Id.DEDUCTION, null)) + .build(); try { - MAPPER.readValue("{ \"one\": \"Hello World\" }", Parent.class); + mapper.readValue("{ \"one\": \"Hello World\" }", Parent.class); } catch (Exception e) { fail("This call should not throw"); } From 972ab8cdfeaefcadc4a145ad5fcb8d8e4c5a8d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20=27Bluexin=27=20Sol=C3=A9?= Date: Sat, 1 Oct 2022 03:07:20 +0200 Subject: [PATCH 3/3] Add more assertions --- ...eserializerFactoryWithDefaultTypeResolverBuilder.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java index 1c56214238..b9b152469d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestDeserializerFactoryWithDefaultTypeResolverBuilder.java @@ -62,7 +62,14 @@ public void testDeductionWithDefaultTypeResolverBuilder() { .build(); try { - mapper.readValue("{ \"one\": \"Hello World\" }", Parent.class); + final Parent firstRead = mapper.readValue("{ \"one\": \"Hello World\" }", Parent.class); + assertNotNull(firstRead); + assertTrue(firstRead instanceof Parent.ChildOne); + assertEquals("Hello World", ((Parent.ChildOne) firstRead).one); + final Parent secondRead = mapper.readValue("{ \"two\": \"Hello World\" }", Parent.class); + assertNotNull(secondRead); + assertTrue(secondRead instanceof Parent.ChildTwo); + assertEquals("Hello World", ((Parent.ChildTwo) secondRead).two); } catch (Exception e) { fail("This call should not throw"); }