From 7089146c92db8eefebfe9776453f58199fcf25de Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sun, 8 Dec 2024 18:08:09 +0900 Subject: [PATCH 01/14] Add fix --- .../deser/std/ReferenceTypeDeserializer.java | 2 +- .../databind/ser/std/ReferenceTypeSerializer.java | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java index be81d8beeb..24741b8e8e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java @@ -257,6 +257,6 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, if (_valueTypeDeserializer == null) { return deserialize(p, ctxt); } - return referenceValue(_valueTypeDeserializer.deserializeTypedFromAny(p, ctxt)); + return typeDeserializer.deserializeTypedFromAny(p, ctxt); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java index c80bf5afbd..6c7143b3c9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.introspect.Annotated; @@ -407,18 +409,17 @@ public void serializeWithType(T ref, // 19-Apr-2016, tatu: In order to basically "skip" the whole wrapper level // (which is what non-polymorphic serialization does too), we will need // to simply delegate call, I think, and NOT try to use it here. - - // Otherwise apply type-prefix/suffix, then std serialize: - /* - typeSer.writeTypePrefixForScalar(ref, g); - serialize(ref, g, provider); - typeSer.writeTypeSuffixForScalar(ref, g); - */ + // Otherwise apply in order [ type-prefix -> value -> type-suffix ] then std serialize: + // prefix + WritableTypeId typeId = typeSer.writeTypePrefix(g, typeSer.typeId(ref, JsonToken.VALUE_STRING)); + // content JsonSerializer ser = _valueSerializer; if (ser == null) { ser = _findCachedSerializer(provider, value.getClass()); } ser.serializeWithType(value, g, provider, typeSer); + // suffix + typeSer.writeTypeSuffix(g, typeId); } /* From 6d6de031637690135adbf12a13ae2ce2988c2911 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sun, 8 Dec 2024 18:12:40 +0900 Subject: [PATCH 02/14] doc : clean up --- .../OptionalWithTypeResolver86Test.java | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java diff --git a/src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java b/src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java new file mode 100644 index 0000000000..642cce8af8 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java @@ -0,0 +1,139 @@ +package com.fasterxml.jackson.databind; + +import java.util.Objects; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// [modules-java8#86] Cannot read `Optional`s written with `StdTypeResolverBuilder` +// +public class OptionalWithTypeResolver86Test + extends DatabindTestUtil +{ + + public static class Foo { + public Optional value; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Foo foo = (Foo) o; + return Objects.equals(value, foo.value); + } + } + + public static class Pojo86 { + public String name; + + // with static method + public static Pojo86 valueOf(String name) { + Pojo86 pojo = new Pojo86(); + pojo.name = name; + return pojo; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Pojo86 pojo86 = (Pojo86) o; + return Objects.equals(name, pojo86.name); + } + + } + + // Base class for polymorphic types + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_OBJECT) + public static abstract class Animal { + public String name; + + protected Animal(String name) { + this.name = name; + } + } + + // Subclass: Dog + public static class Dog extends Animal { + @JsonCreator + public Dog(@JsonProperty("name") String name) { + super(name); + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof Dog) && name.equals(((Dog) obj).name); + } + } + + @Test + public void testRoundTrip() + throws Exception + { + _testOptionalWith(Optional.of("MyName"), String.class, "MyName"); + _testOptionalWith(Optional.of(42), Integer.class, 42); + _testOptionalWith(Optional.of(Pojo86.valueOf("PojoName")), + Pojo86.class, Pojo86.valueOf("PojoName")); + } + + @Test + public void testRoundTripPolymorphic() + throws Exception + { + _testOptionalPolymorphicWith( + Optional.of(new Dog("Buddy")) + ); + } + + private void _testOptionalPolymorphicWith(Optional value) + throws Exception + { + ObjectMapper mapper = configureObjectMapper(); + + // Serialize + Foo foo = new Foo<>(); + foo.value = value; + String json = mapper.writeValueAsString(foo); + + // Deserialize + Foo bean = mapper.readValue(json, + mapper.getTypeFactory().constructParametricType(Foo.class, Animal.class)); + assertEquals(foo, bean); // Compare Foo objects directly + } + + private void _testOptionalWith(Optional value, Class type, T expectedValue) + throws Exception + { + ObjectMapper mapper = configureObjectMapper(); + + // Serialize + Foo foo = new Foo<>(); + foo.value = value; + String json = mapper.writeValueAsString(foo); + + // Deserialize + Foo bean = mapper.readValue(json, + mapper.getTypeFactory().constructParametricType(Foo.class, type)); + assertEquals(value, bean.value); + assertEquals(expectedValue, bean.value.get()); + } + + private ObjectMapper configureObjectMapper(){ + ObjectMapper mapper = jsonMapperBuilder().addModule(new Jdk8Module()).build(); + mapper.setDefaultTyping( + new StdTypeResolverBuilder() + .init(JsonTypeInfo.Id.CLASS, null) + .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) + ); + return mapper; + } + +} \ No newline at end of file From 30d2cb02e3b98f549faf1e4b2f8313403edb40a2 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sun, 8 Dec 2024 18:16:37 +0900 Subject: [PATCH 03/14] Remove test --- .../OptionalWithTypeResolver86Test.java | 139 ------------------ 1 file changed, 139 deletions(-) delete mode 100644 src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java diff --git a/src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java b/src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java deleted file mode 100644 index 642cce8af8..0000000000 --- a/src/test/java/com/fasterxml/jackson/databind/OptionalWithTypeResolver86Test.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.fasterxml.jackson.databind; - -import java.util.Objects; -import java.util.Optional; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; -import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -// [modules-java8#86] Cannot read `Optional`s written with `StdTypeResolverBuilder` -// -public class OptionalWithTypeResolver86Test - extends DatabindTestUtil -{ - - public static class Foo { - public Optional value; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Foo foo = (Foo) o; - return Objects.equals(value, foo.value); - } - } - - public static class Pojo86 { - public String name; - - // with static method - public static Pojo86 valueOf(String name) { - Pojo86 pojo = new Pojo86(); - pojo.name = name; - return pojo; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Pojo86 pojo86 = (Pojo86) o; - return Objects.equals(name, pojo86.name); - } - - } - - // Base class for polymorphic types - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_OBJECT) - public static abstract class Animal { - public String name; - - protected Animal(String name) { - this.name = name; - } - } - - // Subclass: Dog - public static class Dog extends Animal { - @JsonCreator - public Dog(@JsonProperty("name") String name) { - super(name); - } - - @Override - public boolean equals(Object obj) { - return (obj instanceof Dog) && name.equals(((Dog) obj).name); - } - } - - @Test - public void testRoundTrip() - throws Exception - { - _testOptionalWith(Optional.of("MyName"), String.class, "MyName"); - _testOptionalWith(Optional.of(42), Integer.class, 42); - _testOptionalWith(Optional.of(Pojo86.valueOf("PojoName")), - Pojo86.class, Pojo86.valueOf("PojoName")); - } - - @Test - public void testRoundTripPolymorphic() - throws Exception - { - _testOptionalPolymorphicWith( - Optional.of(new Dog("Buddy")) - ); - } - - private void _testOptionalPolymorphicWith(Optional value) - throws Exception - { - ObjectMapper mapper = configureObjectMapper(); - - // Serialize - Foo foo = new Foo<>(); - foo.value = value; - String json = mapper.writeValueAsString(foo); - - // Deserialize - Foo bean = mapper.readValue(json, - mapper.getTypeFactory().constructParametricType(Foo.class, Animal.class)); - assertEquals(foo, bean); // Compare Foo objects directly - } - - private void _testOptionalWith(Optional value, Class type, T expectedValue) - throws Exception - { - ObjectMapper mapper = configureObjectMapper(); - - // Serialize - Foo foo = new Foo<>(); - foo.value = value; - String json = mapper.writeValueAsString(foo); - - // Deserialize - Foo bean = mapper.readValue(json, - mapper.getTypeFactory().constructParametricType(Foo.class, type)); - assertEquals(value, bean.value); - assertEquals(expectedValue, bean.value.get()); - } - - private ObjectMapper configureObjectMapper(){ - ObjectMapper mapper = jsonMapperBuilder().addModule(new Jdk8Module()).build(); - mapper.setDefaultTyping( - new StdTypeResolverBuilder() - .init(JsonTypeInfo.Id.CLASS, null) - .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) - ); - return mapper; - } - -} \ No newline at end of file From cde7eb89898657e15fcce769214e4b839478d2ae Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Wed, 11 Dec 2024 23:54:08 +0900 Subject: [PATCH 04/14] Add case for AtomicReference --- ...nceWithStdTypeResolverBuilder4838Test.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java new file mode 100644 index 0000000000..d8df4f02ca --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -0,0 +1,35 @@ +package com.fasterxml.jackson.databind.deser.jdk; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class AtomicReferenceWithStdTypeResolverBuilder4838Test + extends DatabindTestUtil { + + static class Wrapper4383Test { + public AtomicReference ref; + } + + @Test + public void testPropertyAnnotationForReferences() throws Exception { + Wrapper4383Test w = jsonMapperBuilder() + .setDefaultTyping( + new StdTypeResolverBuilder() + .init(JsonTypeInfo.Id.CLASS, null) + .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT)) + .build() + .readValue("{\"ref\": 99}", Wrapper4383Test.class); + + assertNotNull(w); + assertNotNull(w.ref); + assertEquals(99, w.ref.get()); + } + +} From a683b1abaaf1dcb22cf5c1555a91292e103c48fa Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 12 Dec 2024 00:28:08 +0900 Subject: [PATCH 05/14] Fix AtomicReference tests to actually make sense --- ...nceWithStdTypeResolverBuilder4838Test.java | 110 +++++++++++++++--- 1 file changed, 92 insertions(+), 18 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java index d8df4f02ca..50b0b5208f 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -1,35 +1,109 @@ package com.fasterxml.jackson.databind.deser.jdk; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +// Reported via [modules-java8#86] Cannot read `Optional`s written with `StdTypeResolverBuilder` public class AtomicReferenceWithStdTypeResolverBuilder4838Test - extends DatabindTestUtil { + extends DatabindTestUtil +{ + + public static class Foo { + public AtomicReference value; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Foo foo = (Foo) o; + return Objects.equals(value.get(), foo.value.get()); + } + } + + public static class Pojo86 { + public String name; + + public static Pojo86 valueOf(String name) { + Pojo86 pojo = new Pojo86(); + pojo.name = name; + return pojo; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Pojo86 pojo86 = (Pojo86) o; + return Objects.equals(name, pojo86.name); + } + } - static class Wrapper4383Test { - public AtomicReference ref; + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_OBJECT) + public static abstract class Animal { + public String name; + + protected Animal(String name) { + this.name = name; + } + } + + public static class Dog extends Animal { + @JsonCreator + public Dog(@JsonProperty("name") String name) { + super(name); + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof Dog) && name.equals(((Dog) obj).name); + } } @Test - public void testPropertyAnnotationForReferences() throws Exception { - Wrapper4383Test w = jsonMapperBuilder() - .setDefaultTyping( - new StdTypeResolverBuilder() - .init(JsonTypeInfo.Id.CLASS, null) - .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT)) - .build() - .readValue("{\"ref\": 99}", Wrapper4383Test.class); + public void testRoundTrip() throws Exception { + _test(new AtomicReference<>("MyName"), String.class); + _test(new AtomicReference<>(42), Integer.class); + _test(new AtomicReference<>(Pojo86.valueOf("PojoName")), Pojo86.class); + } + + @Test + public void testPolymorphic() throws Exception { + _test(new AtomicReference<>(new Dog("Buddy")), Animal.class); + } + + private void _test(AtomicReference value, Class type) throws Exception { + ObjectMapper mapper = configureObjectMapper(); + + // Serialize + Foo foo = new Foo<>(); + foo.value = value; + String json = mapper.writeValueAsString(foo); - assertNotNull(w); - assertNotNull(w.ref); - assertEquals(99, w.ref.get()); + // Deserialize + Foo bean = mapper.readValue(json, mapper.getTypeFactory().constructParametricType(Foo.class, type)); + + // Compare the underlying values of AtomicReference + assertEquals(foo.value.get(), bean.value.get()); } + private ObjectMapper configureObjectMapper() { + return jsonMapperBuilder() + // this is what's causing failure in later versions..... + .setDefaultTyping( + new StdTypeResolverBuilder() + .init(JsonTypeInfo.Id.CLASS, null) + .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) + ).build(); + } } From 42ba9a698d1fde0a97db21627190be0d5cfe6301 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 12 Dec 2024 00:34:58 +0900 Subject: [PATCH 06/14] doc: Add some comments to try to explain things --- .../databind/deser/std/ReferenceTypeDeserializer.java | 3 +++ .../jackson/databind/ser/std/ReferenceTypeSerializer.java | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java index 24741b8e8e..dd0b9c7595 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java @@ -257,6 +257,9 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, if (_valueTypeDeserializer == null) { return deserialize(p, ctxt); } + // [modules-java8#86] Since 2.19.0, Cannot read `Optional`s written with `StdTypeResolverBuilder` return typeDeserializer.deserializeTypedFromAny(p, ctxt); + // Previously was... + // return referenceValue(_valueTypeDeserializer.deserializeTypedFromAny(p, ctxt)); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java index 6c7143b3c9..9aa2740e9d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java @@ -409,16 +409,14 @@ public void serializeWithType(T ref, // 19-Apr-2016, tatu: In order to basically "skip" the whole wrapper level // (which is what non-polymorphic serialization does too), we will need // to simply delegate call, I think, and NOT try to use it here. - // Otherwise apply in order [ type-prefix -> value -> type-suffix ] then std serialize: - // prefix + + // [modules-java8#86] Since 2.19.0, Bringing back the prefix and suffix writing part WritableTypeId typeId = typeSer.writeTypePrefix(g, typeSer.typeId(ref, JsonToken.VALUE_STRING)); - // content JsonSerializer ser = _valueSerializer; if (ser == null) { ser = _findCachedSerializer(provider, value.getClass()); } ser.serializeWithType(value, g, provider, typeSer); - // suffix typeSer.writeTypeSuffix(g, typeId); } From 7381be2daeb34abdda77eec5b4a3dc82d3e77679 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 12 Dec 2024 00:40:44 +0900 Subject: [PATCH 07/14] Add release note --- release-notes/VERSION-2.x | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index a2bf771032..7adcd3d6f1 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -32,6 +32,9 @@ Project: jackson-databind #4773: `SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS` should not apply to Maps with uncomparable keys (requested by @nathanukey) +#4850: Cannot read reference types written with `StdTypeResolverBuilder` + (reported by @isopov) + (fix by Joo-Hyuk K) 2.18.3 (not yet released) From 0fc9a94977286503bf09ec97950208859c3e8811 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 19 Dec 2024 19:38:42 -0800 Subject: [PATCH 08/14] Add a failing case --- ...nceWithStdTypeResolverBuilder4838Test.java | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java index 50b0b5208f..3b8359e1f3 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -16,10 +16,9 @@ // Reported via [modules-java8#86] Cannot read `Optional`s written with `StdTypeResolverBuilder` public class AtomicReferenceWithStdTypeResolverBuilder4838Test - extends DatabindTestUtil + extends DatabindTestUtil { - - public static class Foo { + static class Foo { public AtomicReference value; @Override @@ -31,7 +30,7 @@ public boolean equals(Object o) { } } - public static class Pojo86 { + static class Pojo86 { public String name; public static Pojo86 valueOf(String name) { @@ -50,7 +49,7 @@ public boolean equals(Object o) { } @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_OBJECT) - public static abstract class Animal { + static abstract class Animal { public String name; protected Animal(String name) { @@ -58,7 +57,7 @@ protected Animal(String name) { } } - public static class Dog extends Animal { + static class Dog extends Animal { @JsonCreator public Dog(@JsonProperty("name") String name) { super(name); @@ -70,6 +69,27 @@ public boolean equals(Object obj) { } } + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) + static abstract class Animal2 { + public String name; + + protected Animal2(String name) { + this.name = name; + } + } + + static class Dog2 extends Animal2 { + @JsonCreator + public Dog2(@JsonProperty("name") String name) { + super(name); + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof Dog2) && name.equals(((Dog2) obj).name); + } + } + @Test public void testRoundTrip() throws Exception { _test(new AtomicReference<>("MyName"), String.class); @@ -79,31 +99,29 @@ public void testRoundTrip() throws Exception { @Test public void testPolymorphic() throws Exception { - _test(new AtomicReference<>(new Dog("Buddy")), Animal.class); + //_test(new AtomicReference<>(new Dog("Buddy")), Animal.class); + _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class); } - private void _test(AtomicReference value, Class type) throws Exception { - ObjectMapper mapper = configureObjectMapper(); + private final ObjectMapper STD_RESOLVER_MAPPER = jsonMapperBuilder() + // this is what's causing failure in later versions..... + .setDefaultTyping( + new StdTypeResolverBuilder() + .init(JsonTypeInfo.Id.CLASS, null) + .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) + ).build(); + private void _test(AtomicReference value, Class type) throws Exception { // Serialize Foo foo = new Foo<>(); foo.value = value; - String json = mapper.writeValueAsString(foo); + String json = STD_RESOLVER_MAPPER.writeValueAsString(foo); // Deserialize - Foo bean = mapper.readValue(json, mapper.getTypeFactory().constructParametricType(Foo.class, type)); + Foo bean = STD_RESOLVER_MAPPER.readValue(json, + STD_RESOLVER_MAPPER.getTypeFactory().constructParametricType(Foo.class, type)); // Compare the underlying values of AtomicReference assertEquals(foo.value.get(), bean.value.get()); } - - private ObjectMapper configureObjectMapper() { - return jsonMapperBuilder() - // this is what's causing failure in later versions..... - .setDefaultTyping( - new StdTypeResolverBuilder() - .init(JsonTypeInfo.Id.CLASS, null) - .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) - ).build(); - } } From 2f19d45db2bcc08813a8240e2f75be42c323e154 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 19 Dec 2024 19:44:36 -0800 Subject: [PATCH 09/14] uncomment accidentally commented out test case --- .../jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java index 3b8359e1f3..bf5c1b384a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -99,7 +99,7 @@ public void testRoundTrip() throws Exception { @Test public void testPolymorphic() throws Exception { - //_test(new AtomicReference<>(new Dog("Buddy")), Animal.class); + _test(new AtomicReference<>(new Dog("Buddy")), Animal.class); _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class); } From e374faa3efcabefb262f92fc1917cb0b3b64ca4b Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Fri, 20 Dec 2024 21:26:37 +0900 Subject: [PATCH 10/14] review: Use `assertInstanceOf()` --- .../deser/enums/EnumSetPolymorphicDeser4214Test.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/enums/EnumSetPolymorphicDeser4214Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/enums/EnumSetPolymorphicDeser4214Test.java index 3c409157d5..f2f2336b13 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/enums/EnumSetPolymorphicDeser4214Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/enums/EnumSetPolymorphicDeser4214Test.java @@ -10,10 +10,8 @@ import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.jsonMapperBuilder; +import static org.junit.jupiter.api.Assertions.*; // For [databind#4214] public class EnumSetPolymorphicDeser4214Test @@ -50,6 +48,6 @@ public void testPolymorphicDeserialization4214() throws Exception String json = mapper.writeValueAsString(enumSetHolder); EnumSetHolder result = mapper.readValue(json, EnumSetHolder.class); assertEquals(result, enumSetHolder); - assertTrue(result.enumSet instanceof EnumSet); + assertInstanceOf(EnumSet.class, result.enumSet); } } From de925686e54db60c91e4010934c71f0a796d0a51 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Fri, 20 Dec 2024 21:37:43 +0900 Subject: [PATCH 11/14] Modify test to use inclusion as expected --- ...nceWithStdTypeResolverBuilder4838Test.java | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java index bf5c1b384a..0752f0e11b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -69,7 +69,7 @@ public boolean equals(Object obj) { } } - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY) static abstract class Animal2 { public String name; @@ -92,18 +92,27 @@ public boolean equals(Object obj) { @Test public void testRoundTrip() throws Exception { - _test(new AtomicReference<>("MyName"), String.class); - _test(new AtomicReference<>(42), Integer.class); - _test(new AtomicReference<>(Pojo86.valueOf("PojoName")), Pojo86.class); + _test(new AtomicReference<>("MyName"), String.class, + STD_RESOLVER_MAPPER_WRAPPER_OBJECT); + _test(new AtomicReference<>(42), Integer.class, + STD_RESOLVER_MAPPER_WRAPPER_OBJECT); + _test(new AtomicReference<>(Pojo86.valueOf("PojoName")), Pojo86.class, + STD_RESOLVER_MAPPER_WRAPPER_OBJECT); } @Test public void testPolymorphic() throws Exception { - _test(new AtomicReference<>(new Dog("Buddy")), Animal.class); - _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class); + _test(new AtomicReference<>(new Dog("Buddy")), Animal.class, + STD_RESOLVER_MAPPER_WRAPPER_OBJECT); } - private final ObjectMapper STD_RESOLVER_MAPPER = jsonMapperBuilder() + @Test + public void testPolymorphic2() throws Exception { + _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class, + STD_RESOLVER_MAPPER_WRAPPER_ARRAY); + } + + private final ObjectMapper STD_RESOLVER_MAPPER_WRAPPER_OBJECT = jsonMapperBuilder() // this is what's causing failure in later versions..... .setDefaultTyping( new StdTypeResolverBuilder() @@ -111,15 +120,25 @@ public void testPolymorphic() throws Exception { .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) ).build(); - private void _test(AtomicReference value, Class type) throws Exception { + private final ObjectMapper STD_RESOLVER_MAPPER_WRAPPER_ARRAY = jsonMapperBuilder() + // this is what's causing failure in later versions..... + .setDefaultTyping( + new StdTypeResolverBuilder() + .init(JsonTypeInfo.Id.CLASS, null) + .inclusion(JsonTypeInfo.As.WRAPPER_ARRAY) + ).build(); + + private void _test(AtomicReference value, Class type, ObjectMapper mapper) + throws Exception + { // Serialize Foo foo = new Foo<>(); foo.value = value; - String json = STD_RESOLVER_MAPPER.writeValueAsString(foo); + String json = mapper.writeValueAsString(foo); // Deserialize - Foo bean = STD_RESOLVER_MAPPER.readValue(json, - STD_RESOLVER_MAPPER.getTypeFactory().constructParametricType(Foo.class, type)); + Foo bean = mapper.readValue(json, + mapper.getTypeFactory().constructParametricType(Foo.class, type)); // Compare the underlying values of AtomicReference assertEquals(foo.value.get(), bean.value.get()); From 4ac30d382609542473ec373fe2fcf2f8d8f4e896 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Fri, 20 Dec 2024 21:38:35 +0900 Subject: [PATCH 12/14] Modify test names --- .../AtomicReferenceWithStdTypeResolverBuilder4838Test.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java index 0752f0e11b..7596d39baa 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -101,13 +101,13 @@ public void testRoundTrip() throws Exception { } @Test - public void testPolymorphic() throws Exception { + public void testPolymorphicWithWrapperObject() throws Exception { _test(new AtomicReference<>(new Dog("Buddy")), Animal.class, STD_RESOLVER_MAPPER_WRAPPER_OBJECT); } @Test - public void testPolymorphic2() throws Exception { + public void testPolymorphicWithWrapperArray() throws Exception { _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class, STD_RESOLVER_MAPPER_WRAPPER_ARRAY); } From 1b3f3d98a9f987cdef93e7bc3786ac46c9bf7a28 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sat, 21 Dec 2024 11:44:15 +0900 Subject: [PATCH 13/14] Revert modifying test (wrt varying Inclusion in default typing, when there is specific type already) --- ...nceWithStdTypeResolverBuilder4838Test.java | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java index 7596d39baa..bf5c1b384a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -69,7 +69,7 @@ public boolean equals(Object obj) { } } - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY) + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) static abstract class Animal2 { public String name; @@ -92,27 +92,18 @@ public boolean equals(Object obj) { @Test public void testRoundTrip() throws Exception { - _test(new AtomicReference<>("MyName"), String.class, - STD_RESOLVER_MAPPER_WRAPPER_OBJECT); - _test(new AtomicReference<>(42), Integer.class, - STD_RESOLVER_MAPPER_WRAPPER_OBJECT); - _test(new AtomicReference<>(Pojo86.valueOf("PojoName")), Pojo86.class, - STD_RESOLVER_MAPPER_WRAPPER_OBJECT); + _test(new AtomicReference<>("MyName"), String.class); + _test(new AtomicReference<>(42), Integer.class); + _test(new AtomicReference<>(Pojo86.valueOf("PojoName")), Pojo86.class); } @Test - public void testPolymorphicWithWrapperObject() throws Exception { - _test(new AtomicReference<>(new Dog("Buddy")), Animal.class, - STD_RESOLVER_MAPPER_WRAPPER_OBJECT); + public void testPolymorphic() throws Exception { + _test(new AtomicReference<>(new Dog("Buddy")), Animal.class); + _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class); } - @Test - public void testPolymorphicWithWrapperArray() throws Exception { - _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class, - STD_RESOLVER_MAPPER_WRAPPER_ARRAY); - } - - private final ObjectMapper STD_RESOLVER_MAPPER_WRAPPER_OBJECT = jsonMapperBuilder() + private final ObjectMapper STD_RESOLVER_MAPPER = jsonMapperBuilder() // this is what's causing failure in later versions..... .setDefaultTyping( new StdTypeResolverBuilder() @@ -120,25 +111,15 @@ public void testPolymorphicWithWrapperArray() throws Exception { .inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) ).build(); - private final ObjectMapper STD_RESOLVER_MAPPER_WRAPPER_ARRAY = jsonMapperBuilder() - // this is what's causing failure in later versions..... - .setDefaultTyping( - new StdTypeResolverBuilder() - .init(JsonTypeInfo.Id.CLASS, null) - .inclusion(JsonTypeInfo.As.WRAPPER_ARRAY) - ).build(); - - private void _test(AtomicReference value, Class type, ObjectMapper mapper) - throws Exception - { + private void _test(AtomicReference value, Class type) throws Exception { // Serialize Foo foo = new Foo<>(); foo.value = value; - String json = mapper.writeValueAsString(foo); + String json = STD_RESOLVER_MAPPER.writeValueAsString(foo); // Deserialize - Foo bean = mapper.readValue(json, - mapper.getTypeFactory().constructParametricType(Foo.class, type)); + Foo bean = STD_RESOLVER_MAPPER.readValue(json, + STD_RESOLVER_MAPPER.getTypeFactory().constructParametricType(Foo.class, type)); // Compare the underlying values of AtomicReference assertEquals(foo.value.get(), bean.value.get()); From 940034d0c2376e06ad7530f3d4f8b38481c89b99 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sat, 21 Dec 2024 13:27:55 +0900 Subject: [PATCH 14/14] Test : Add more --- ...nceWithStdTypeResolverBuilder4838Test.java | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java index bf5c1b384a..696bc81873 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/AtomicReferenceWithStdTypeResolverBuilder4838Test.java @@ -1,6 +1,6 @@ package com.fasterxml.jackson.databind.deser.jdk; -import java.util.Objects; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; @@ -90,6 +90,27 @@ public boolean equals(Object obj) { } } + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY) + static abstract class Animal3 { + public String name; + + protected Animal3(String name) { + this.name = name; + } + } + + static class Dog3 extends Animal3 { + @JsonCreator + public Dog3(@JsonProperty("name") String name) { + super(name); + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof Dog3) && name.equals(((Dog3) obj).name); + } + } + @Test public void testRoundTrip() throws Exception { _test(new AtomicReference<>("MyName"), String.class); @@ -98,11 +119,42 @@ public void testRoundTrip() throws Exception { } @Test - public void testPolymorphic() throws Exception { + public void testPolymorphicWrapperObject() throws Exception { + // Note that default typing set to WRAPPER_OBJECT as well _test(new AtomicReference<>(new Dog("Buddy")), Animal.class); + } + + @Test + public void testPolymorphicProperty() throws Exception { _test(new AtomicReference<>(new Dog2("Buttercup")), Animal2.class); } + @Test + public void testPolymorphicWrapperArray() throws Exception { + _test(new AtomicReference<>(new Dog3("Buddy")), Animal3.class); + } + + @Test + public void testAtomicReferenceWithMapAndCollection() throws Exception { + // Test AtomicReference with Map + Map sampleMap = new HashMap<>(); + sampleMap.put("key1", 1); + sampleMap.put("key2", 2); + _test(new AtomicReference<>(sampleMap), Map.class); + + // Test AtomicReference with List + List sampleList = Arrays.asList("value1", "value2", "value3"); + _test(new AtomicReference<>(sampleList), List.class); + + // Test AtomicReference with Set + Set sampleSet = new HashSet<>(Arrays.asList(1, 2, 3)); + _test(new AtomicReference<>(sampleSet), Set.class); + + // Test AtomicReference with Queue + Queue sampleQueue = new LinkedList<>(Arrays.asList("q1", "q2", "q3")); + _test(new AtomicReference<>(sampleQueue), Queue.class); + } + private final ObjectMapper STD_RESOLVER_MAPPER = jsonMapperBuilder() // this is what's causing failure in later versions..... .setDefaultTyping(