From 56167b0ae2d629f1d22aa8dc25f8caecdfac4d35 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Fri, 7 Apr 2023 23:25:12 +0900 Subject: [PATCH 01/17] Fix all --- .../databind/deser/BeanDeserializer.java | 8 +++ .../JsonIdentityInfoIdProperty3838Test.java | 69 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index f2487ff713..0fa6b35154 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -367,6 +367,14 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); + + if (_objectIdReader != null) { + // check if there are any properties present in the JSON object + if (!p.hasCurrentToken() || p.getCurrentToken() == JsonToken.END_OBJECT) { + // throw an error here since there are no properties in the JSON object + ctxt.reportUnresolvedObjectId(_objectIdReader, bean); + } + } if (p.canReadObjectId()) { Object id = p.getObjectId(); if (id != null) { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java new file mode 100644 index 0000000000..4a3220a323 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java @@ -0,0 +1,69 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; + +// [databind#3838]: Difference in the handling of ObjectId-property in JsonIdentityInfo depending +// on the deserialization route. +public class JsonIdentityInfoIdProperty3838Test extends BaseMapTest { + + /* + /********************************************************** + /* Set Up + /********************************************************** + */ + + @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class) + static class SetterBased { + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } + + @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class) + static class CreatorBased { + private final String id; + + @JsonCreator + CreatorBased(@JsonProperty(value = "id") String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + /* + /********************************************************** + /* Test + /********************************************************** + */ + + private final ObjectMapper MAPPER = newJsonMapper(); + + public void testUniformHandlingForMissingObjectId() throws Exception { + Class[] classes = {SetterBased.class, CreatorBased.class}; + for (Class cls : classes) { + try { + MAPPER.readValue("{}", cls); + fail("should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, + "No Object Id found for an instance of", "to", + "to assign to property 'id'"); + } + } + } +} From 1e4544af5d6fbc84ecf749cfa1047046d6a54abf Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Fri, 7 Apr 2023 23:53:25 +0900 Subject: [PATCH 02/17] Update JsonIdentityInfoIdProperty3838Test.java --- .../JsonIdentityInfoIdProperty3838Test.java | 97 +++++++++++++++++-- 1 file changed, 91 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java index 4a3220a323..a8f48d489e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java @@ -8,8 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.MismatchedInputException; -// [databind#3838]: Difference in the handling of ObjectId-property in JsonIdentityInfo depending -// on the deserialization route. +// [databind#3838]: Difference in the handling of ObjectId-property in JsonIdentityInfo depending on the deserialization route. public class JsonIdentityInfoIdProperty3838Test extends BaseMapTest { /* @@ -17,9 +16,12 @@ public class JsonIdentityInfoIdProperty3838Test extends BaseMapTest { /* Set Up /********************************************************** */ + interface ResultGetter { + String result(); + } @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class) - static class SetterBased { + static class SetterBased implements ResultGetter { private String id; public String getId() { @@ -29,10 +31,15 @@ public String getId() { public void setId(String id) { this.id = id; } + + @Override + public String result() { + return id; + } } @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class) - static class CreatorBased { + static class CreatorBased implements ResultGetter { private final String id; @JsonCreator @@ -43,6 +50,69 @@ static class CreatorBased { public String getId() { return id; } + + @Override + public String result() { + return id; + } + } + + @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class) + static class DefaultConstructorBased implements ResultGetter { + public String id; + + @Override + public String result() { + return id; + } + } + + @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class) + static class StaticFactoryMethodBased implements ResultGetter { + private final String id; + + private StaticFactoryMethodBased(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + @JsonCreator + public static StaticFactoryMethodBased create(@JsonProperty("id") String id) { + return new StaticFactoryMethodBased(id); + } + + @Override + public String result() { + return id; + } + } + + @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class) + static class MultiArgConstructorBased implements ResultGetter { + private final String id; + private final int value; + + @JsonCreator + MultiArgConstructorBased(@JsonProperty("id") String id, @JsonProperty("value") int value) { + this.id = id; + this.value = value; + } + + public String getId() { + return id; + } + + public int getValue() { + return value; + } + + @Override + public String result() { + return id; + } } /* @@ -53,9 +123,20 @@ public String getId() { private final ObjectMapper MAPPER = newJsonMapper(); + final static Object[][] CLASS_AND_JSON_STRING = new Object[][]{ + {SetterBased.class, "{'id':'great'}"}, + {CreatorBased.class, "{'id':'great'}"}, + {DefaultConstructorBased.class, "{'id':'great'}"}, + {StaticFactoryMethodBased.class, "{'id':'great'}"}, + {MultiArgConstructorBased.class, "{'id':'great','value':42}"} + }; + public void testUniformHandlingForMissingObjectId() throws Exception { - Class[] classes = {SetterBased.class, CreatorBased.class}; - for (Class cls : classes) { + for (Object[] classAndJsonStrEntry : CLASS_AND_JSON_STRING) { + final Class cls = (Class) classAndJsonStrEntry[0]; + final String jsonStr = (String) classAndJsonStrEntry[1]; + + // 1. throws MismatchedInputException with empty JSON object try { MAPPER.readValue("{}", cls); fail("should not pass"); @@ -64,6 +145,10 @@ public void testUniformHandlingForMissingObjectId() throws Exception { "No Object Id found for an instance of", "to", "to assign to property 'id'"); } + + // 2. but works with non-empty JSON object + ResultGetter resultGetter = (ResultGetter) MAPPER.readValue(a2q(jsonStr), cls); + assertEquals("great", resultGetter.result()); } } } From 78c67fdd7521b95969d92d14a72da873ea493e76 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sat, 8 Apr 2023 00:05:21 +0900 Subject: [PATCH 03/17] Add description on change itself --- .../com/fasterxml/jackson/databind/deser/BeanDeserializer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index 0fa6b35154..f485334a1a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -368,6 +368,7 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); + // [databind#3838]: Uniform handling of ObjectId-property if (_objectIdReader != null) { // check if there are any properties present in the JSON object if (!p.hasCurrentToken() || p.getCurrentToken() == JsonToken.END_OBJECT) { From e8ebfb9141b046f68ddfcf3eea7d8a2a1913e7ac Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sat, 8 Apr 2023 00:36:25 +0900 Subject: [PATCH 04/17] add testcase with JsonTypeInfo and Identity together --- .../JsonIdentityInfoIdProperty3838Test.java | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java index a8f48d489e..219cdb332a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java @@ -1,9 +1,6 @@ package com.fasterxml.jackson.databind.deser; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.BaseMapTest; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.MismatchedInputException; @@ -115,6 +112,39 @@ public String result() { } } + @JsonIdentityInfo( + generator = ObjectIdGenerators.PropertyGenerator.class, + property = "id" + ) + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.WRAPPER_OBJECT, + property = "type" + ) + @JsonSubTypes({ + @JsonSubTypes.Type(value = Concrete3838.class, name = "concrete_3838") + }) + static class BaseType3838 implements ResultGetter { + public String id; + + @Override + public String result() { + return id; + } + } + + @JsonTypeName("concrete_3838") + static class Concrete3838 extends BaseType3838 { + public String location; + + protected Concrete3838() {} + + public Concrete3838(String id, String loc) { + this.id = id; + location = loc; + } + } + /* /********************************************************** /* Test @@ -128,7 +158,8 @@ public String result() { {CreatorBased.class, "{'id':'great'}"}, {DefaultConstructorBased.class, "{'id':'great'}"}, {StaticFactoryMethodBased.class, "{'id':'great'}"}, - {MultiArgConstructorBased.class, "{'id':'great','value':42}"} + {MultiArgConstructorBased.class, "{'id':'great','value':42}"}, + {BaseType3838.class, "{'concrete_3838':{'id':'great','location':'Bangkok'}}"} }; public void testUniformHandlingForMissingObjectId() throws Exception { @@ -147,8 +178,8 @@ public void testUniformHandlingForMissingObjectId() throws Exception { } // 2. but works with non-empty JSON object - ResultGetter resultGetter = (ResultGetter) MAPPER.readValue(a2q(jsonStr), cls); - assertEquals("great", resultGetter.result()); + ResultGetter nonEmptyObj = (ResultGetter) MAPPER.readValue(a2q(jsonStr), cls); + assertEquals("great", nonEmptyObj.result()); } } } From 8f7129486a0e6f1eeeb2078f557a23a13bcb64da Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sat, 8 Apr 2023 00:40:30 +0900 Subject: [PATCH 05/17] Update BeanDeserializer.java --- .../com/fasterxml/jackson/databind/deser/BeanDeserializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index f485334a1a..7da9c0c082 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -368,7 +368,7 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); - // [databind#3838]: Uniform handling of ObjectId-property + // [databind#3838]: Uniform handling of missing objectId if (_objectIdReader != null) { // check if there are any properties present in the JSON object if (!p.hasCurrentToken() || p.getCurrentToken() == JsonToken.END_OBJECT) { From 17012c4c8141f53b85ef8b4121134f62ab5a4f32 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sat, 8 Apr 2023 12:36:12 +0900 Subject: [PATCH 06/17] Move the fix after native objectIds --- .../jackson/databind/deser/BeanDeserializer.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index 7da9c0c082..6884ae46cc 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -367,21 +367,20 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); - + if (p.canReadObjectId()) { + Object id = p.getObjectId(); + if (id != null) { + _handleTypedObjectId(p, ctxt, bean, id); + } + } // [databind#3838]: Uniform handling of missing objectId if (_objectIdReader != null) { // check if there are any properties present in the JSON object if (!p.hasCurrentToken() || p.getCurrentToken() == JsonToken.END_OBJECT) { - // throw an error here since there are no properties in the JSON object + // throw an error here since there are no properties in the JSON object ctxt.reportUnresolvedObjectId(_objectIdReader, bean); } } - if (p.canReadObjectId()) { - Object id = p.getObjectId(); - if (id != null) { - _handleTypedObjectId(p, ctxt, bean, id); - } - } if (_injectables != null) { injectValues(ctxt, bean); } From 520fa48b5fdf6f2a12f821daca68f8ab011dd7ed Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Wed, 12 Apr 2023 23:58:43 +0900 Subject: [PATCH 07/17] Add more test --- .../deser/JsonIdentityInfoIdProperty3838Test.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java index 219cdb332a..43904c4bd2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.MismatchedInputException; @@ -145,6 +146,11 @@ public Concrete3838(String id, String loc) { } } + @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id") + static class IntSequencedBean { + public String value; + } + /* /********************************************************** /* Test @@ -159,7 +165,8 @@ public Concrete3838(String id, String loc) { {DefaultConstructorBased.class, "{'id':'great'}"}, {StaticFactoryMethodBased.class, "{'id':'great'}"}, {MultiArgConstructorBased.class, "{'id':'great','value':42}"}, - {BaseType3838.class, "{'concrete_3838':{'id':'great','location':'Bangkok'}}"} + {BaseType3838.class, "{'concrete_3838':{'id':'great','location':'Bangkok'}}"}, + {IntSequencedBean.class, "{'id':-1,'value':'great'}"} }; public void testUniformHandlingForMissingObjectId() throws Exception { From 21a17ec2805d270e46cf87e504883646454e04cf Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 13 Apr 2023 00:04:27 +0900 Subject: [PATCH 08/17] should revert Add passing tests that might need to fail --- .../objectid/TestObjectIdDeserialization.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java index 56c96b287b..cb46e9c31a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java @@ -362,6 +362,7 @@ public void testUnresolvedForwardReference() } } + // [databind#299]: Allow unresolved ids to become nulls public void testUnresolvableAsNull() throws Exception { @@ -372,6 +373,24 @@ public void testUnresolvableAsNull() throws Exception assertNull(w.node); } + public void testUnresolvableIdShouldFail() throws Exception + { + IdWrapper w = MAPPER.readerFor(IdWrapper.class) + .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) + .readValue(a2q("{'node':123}")); + assertNotNull(w); + assertNull(w.node); + } + + public void testUnresolvableIdShouldFail2() throws Exception + { + IdWrapper w = MAPPER.readerFor(IdWrapper.class) + .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) + .readValue(a2q("{}")); + assertNotNull(w); + assertNull(w.node); + } + public void testKeepCollectionOrdering() throws Exception { String json = "{\"employees\":[2,1," From 5cde9497cfa869f28ec16102a8ec97322c10ed53 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 13 Apr 2023 00:08:47 +0900 Subject: [PATCH 09/17] Revert "should revert" This reverts commit 4a5f55194fac19f5da0f72854417cbc9834bd49f. --- .../objectid/TestObjectIdDeserialization.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java index cb46e9c31a..56c96b287b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java @@ -362,7 +362,6 @@ public void testUnresolvedForwardReference() } } - // [databind#299]: Allow unresolved ids to become nulls public void testUnresolvableAsNull() throws Exception { @@ -373,24 +372,6 @@ public void testUnresolvableAsNull() throws Exception assertNull(w.node); } - public void testUnresolvableIdShouldFail() throws Exception - { - IdWrapper w = MAPPER.readerFor(IdWrapper.class) - .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) - .readValue(a2q("{'node':123}")); - assertNotNull(w); - assertNull(w.node); - } - - public void testUnresolvableIdShouldFail2() throws Exception - { - IdWrapper w = MAPPER.readerFor(IdWrapper.class) - .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) - .readValue(a2q("{}")); - assertNotNull(w); - assertNull(w.node); - } - public void testKeepCollectionOrdering() throws Exception { String json = "{\"employees\":[2,1," From f87cf0f0249567d3066b9c9fbdc3307a3c0c8fd3 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 13 Apr 2023 00:04:27 +0900 Subject: [PATCH 10/17] Add passing tests that might need to fail --- .../objectid/TestObjectIdDeserialization.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java index 56c96b287b..0c0be59f30 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java @@ -372,6 +372,24 @@ public void testUnresolvableAsNull() throws Exception assertNull(w.node); } + public void testUnresolvableIdShouldFail() throws Exception + { + IdWrapper w = MAPPER.readerFor(IdWrapper.class) + .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) + .readValue(a2q("{'node':123}")); + assertNotNull(w); + assertNull(w.node); + } + + public void testUnresolvableIdShouldFail2() throws Exception + { + IdWrapper w = MAPPER.readerFor(IdWrapper.class) + .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) + .readValue(a2q("{}")); + assertNotNull(w); + assertNull(w.node); + } + public void testKeepCollectionOrdering() throws Exception { String json = "{\"employees\":[2,1," From f13cbcb26c97f452531d0650a6f215a3df5c96ff Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 13 Apr 2023 00:05:58 +0900 Subject: [PATCH 11/17] Update TestObjectIdDeserialization.java --- .../jackson/databind/objectid/TestObjectIdDeserialization.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java index 0c0be59f30..cb46e9c31a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java @@ -362,6 +362,7 @@ public void testUnresolvedForwardReference() } } + // [databind#299]: Allow unresolved ids to become nulls public void testUnresolvableAsNull() throws Exception { From e63668794e8419e7b68b504a57298a5e54cd88b7 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 13 Apr 2023 00:09:34 +0900 Subject: [PATCH 12/17] Revert "Merge branch '3838' of https://github.com/JooHyukKim/jackson-databind into 3838" This reverts commit 1f3d11af100eeb20454d8eeb7bb6be74afc132d4, reversing changes made to 6b9149ce2f8c348854f85a933704a20d706aa644. --- .../objectid/TestObjectIdDeserialization.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java index cb46e9c31a..56c96b287b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdDeserialization.java @@ -362,7 +362,6 @@ public void testUnresolvedForwardReference() } } - // [databind#299]: Allow unresolved ids to become nulls public void testUnresolvableAsNull() throws Exception { @@ -373,24 +372,6 @@ public void testUnresolvableAsNull() throws Exception assertNull(w.node); } - public void testUnresolvableIdShouldFail() throws Exception - { - IdWrapper w = MAPPER.readerFor(IdWrapper.class) - .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) - .readValue(a2q("{'node':123}")); - assertNotNull(w); - assertNull(w.node); - } - - public void testUnresolvableIdShouldFail2() throws Exception - { - IdWrapper w = MAPPER.readerFor(IdWrapper.class) - .with(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS) - .readValue(a2q("{}")); - assertNotNull(w); - assertNull(w.node); - } - public void testKeepCollectionOrdering() throws Exception { String json = "{\"employees\":[2,1," From 534d1b072d3009ac4cb5514fc21cfa2fcc87151b Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 13 Apr 2023 00:12:52 +0900 Subject: [PATCH 13/17] Fix test --- .../databind/deser/JsonIdentityInfoIdProperty3838Test.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java index 43904c4bd2..e4cdd94c8e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JsonIdentityInfoIdProperty3838Test.java @@ -147,8 +147,13 @@ public Concrete3838(String id, String loc) { } @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id") - static class IntSequencedBean { + static class IntSequencedBean implements ResultGetter{ public String value; + + @Override + public String result() { + return value; + } } /* From 52394ca414dadd83774cdb5d5792b424f5ae160f Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Sat, 15 Apr 2023 20:09:21 +0900 Subject: [PATCH 14/17] simplify checking empty json --- .../jackson/databind/deser/BeanDeserializer.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index 6884ae46cc..91b6f57734 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -364,6 +364,7 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t */ return bean; } + final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); @@ -374,12 +375,8 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t } } // [databind#3838]: Uniform handling of missing objectId - if (_objectIdReader != null) { - // check if there are any properties present in the JSON object - if (!p.hasCurrentToken() || p.getCurrentToken() == JsonToken.END_OBJECT) { - // throw an error here since there are no properties in the JSON object - ctxt.reportUnresolvedObjectId(_objectIdReader, bean); - } + if (_objectIdReader != null && p.getCurrentToken() == JsonToken.END_OBJECT) { + ctxt.reportUnresolvedObjectId(_objectIdReader, bean); } if (_injectables != null) { injectValues(ctxt, bean); From cc201731c9c1bd060710added455999072f55f74 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Mon, 24 Apr 2023 20:49:52 +0900 Subject: [PATCH 15/17] Clean up changes --- .../com/fasterxml/jackson/databind/deser/BeanDeserializer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index 91b6f57734..16a219aac3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -364,7 +364,6 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t */ return bean; } - final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); @@ -374,7 +373,7 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t _handleTypedObjectId(p, ctxt, bean, id); } } - // [databind#3838]: Uniform handling of missing objectId + // [databind#3838]: since 2.16 Uniform handling of missing objectId if (_objectIdReader != null && p.getCurrentToken() == JsonToken.END_OBJECT) { ctxt.reportUnresolvedObjectId(_objectIdReader, bean); } From 7eea4728774527a242e58bbc3ef9cf1290920b53 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Mon, 22 May 2023 23:26:00 +0900 Subject: [PATCH 16/17] Squashed commit of the following: commit 63a7b1d453a11c92d88206fc952e015dce1123ad Merge: 559cd04b8 3140bb749 Author: Tatu Saloranta Date: Thu May 18 16:47:22 2023 -0700 Merge branch '2.15' into 2.16 commit 3140bb749cea10e4a6e79d80b1eb5fe0f8c7cda2 Merge: 4e1c9be17 9f8046254 Author: Tatu Saloranta Date: Thu May 18 16:46:48 2023 -0700 Merge branch '2.15' of github.com:FasterXML/jackson-databind into 2.15 commit 559cd04b81696b15c87907fccbc56694dd42049d Merge: f083348b8 4e1c9be17 Author: Tatu Saloranta Date: Thu May 18 16:45:27 2023 -0700 Merge branch '2.15' into 2.16 commit 9f80462540bd0e0cce847a572dedd4040634ae4f Author: Tatu Saloranta Date: Thu May 18 16:44:56 2023 -0700 Fix #3938: do not skip Method-based setters on Records (#3939) commit 4e1c9be1748d8ec6ebb28dbf45b438a4bab02a3e Merge: 3168975b7 c524e6b63 Author: Tatu Saloranta Date: Thu May 18 15:54:01 2023 -0700 Merge branch '2.14' into 2.15 commit c524e6b6370ca5417b0fbe691fc98bfc4397c17f Author: Tatu Saloranta Date: Thu May 18 15:53:45 2023 -0700 Fix #3938 to repro actual issue commit 3168975b7de2ea817c93f4771addb0c6ecb3ecaa Merge: 3291911aa 04d7ae4a2 Author: Tatu Saloranta Date: Thu May 18 15:18:36 2023 -0700 Merge branch '2.14' into 2.15 commit 04d7ae4a272267b89aae9734028f8ec25a5ca7da Author: Tatu Saloranta Date: Thu May 18 15:13:41 2023 -0700 Add passing test (in 2.14) for #3938 commit f083348b875b94ecf44131853a829d3fc0f0e53c Merge: 6091c4b8d 3291911aa Author: Tatu Saloranta Date: Tue May 16 14:53:20 2023 -0700 Merge branch '2.15' into 2.16 commit 3291911aa2e6ea00880d789e3643a13962d214cf Author: Tatu Saloranta Date: Tue May 16 14:49:51 2023 -0700 Back to snapshot dep commit 05472aedffc71129ca34f204f9c431875b7c8ae5 Author: Tatu Saloranta Date: Tue May 16 14:47:37 2023 -0700 [maven-release-plugin] prepare for next development iteration commit 6e3732557c3fcd45687a9e2780165f5adaa2c41f Author: Tatu Saloranta Date: Tue May 16 14:47:34 2023 -0700 [maven-release-plugin] prepare release jackson-databind-2.15.1 commit 3d9dd34890ad46bb6e6089dac2895c13647ae9ac Author: Tatu Saloranta Date: Tue May 16 14:34:13 2023 -0700 2.15.1 release commit 6091c4b8db30a51b470b880756666e99144934ee Merge: d2ae3a2e2 55d87cfb2 Author: Tatu Saloranta Date: Tue May 16 12:38:57 2023 -0700 Merge branch '2.15' into 2.16 commit 55d87cfb2271ebe507bdf1286839b916bd0c1c80 Merge: a7e17adf0 c7b6c64da Author: Tatu Saloranta Date: Tue May 16 12:38:24 2023 -0700 Merge branch '2.14' into 2.15 commit c7b6c64da52761549e4585cf725e4cbf9d0464af Author: Tatu Saloranta Date: Tue May 16 12:35:44 2023 -0700 Fix #3882 (JsonNode.withArray() fail) commit d2ae3a2e271a7dc6d22d41ff1023f5981fa9b0bf Author: Tatu Saloranta Date: Mon May 15 21:06:11 2023 -0700 manual merge of pom.xml (test) change commit 7ddce076b458dca09bd69570ea734d68a6689905 Merge: a3bb1b90f a7e17adf0 Author: Tatu Saloranta Date: Mon May 15 21:02:46 2023 -0700 Merge branch '2.15' into 2.16 commit a7e17adf04c0bf2c548471c4b7679f3d9399e097 Author: Tatu Saloranta Date: Mon May 15 21:01:49 2023 -0700 Add Junit 5 test dependency commit a3bb1b90ff6753164b5c6c61f6842dc7d382d081 Merge: a3b231cdd 8a8ba5a1f Author: Tatu Saloranta Date: Mon May 15 18:13:22 2023 -0700 Merge branch '2.15' into 2.16 commit 8a8ba5a1f3ea91cf93d22f8623f451a628efc49e Author: Kim, Joo Hyuk Date: Tue May 16 10:13:07 2023 +0900 Update JavaDoc of JsonAppend. (#3933) commit a3b231cdd595a879bbb667f43ba7102056545d7f Author: Tatu Saloranta Date: Mon May 15 15:38:40 2023 -0700 Update release notes wrt #3928 commit 40c97391ba61773922c6d6f8ea21e5fe59c20cf6 Author: PJ Fanning Date: Mon May 15 23:32:27 2023 +0100 Json property affects Record field serialization order (#3929) commit 8fcf9efaa445cd2767887d1a78ec68058cd9de7b Merge: e9db4b355 e5bdcfb31 Author: Tatu Saloranta Date: Sun May 14 17:08:35 2023 -0700 Merge branch '2.15' into 2.16 commit e5bdcfb31771c1e35ed69b1fc49e84ab5d1ac023 Author: Kim, Joo Hyuk Date: Mon May 15 09:06:35 2023 +0900 Remove hard-coded `StreamReadConstraints` test variables to isolate change in `jackson-core` itself (#3930) commit e9db4b3556db3d19c6f6bd5ae1e877c399716d7d Author: Piotr Findeisen Date: Mon May 15 02:04:42 2023 +0200 Fix typo in USE_GETTERS_AS_SETTERS description (#3931) commit d8d4cb69e9046df937788c8edd765888562aaf64 Author: Tatu Saloranta Date: Sat May 13 20:15:45 2023 -0700 Sync tests wrt error messages commit c1b4aad3ff05db8312332e427c086ad0a4d40f20 Merge: 67103c288 6f81a4ed9 Author: Tatu Saloranta Date: Sat May 13 20:04:42 2023 -0700 Merge branch '2.15' into 2.16 commit 6f81a4ed9064a37fd518a1d310cf7509ae7a043f Author: Tatu Saloranta Date: Sat May 13 20:02:20 2023 -0700 Minor change to align with higher max string value length limit commit 67103c2881aa506ebceb25824becac5b80a4f86a Author: Tatu Saloranta Date: Sat May 6 09:44:18 2023 -0700 Clean up attic... commit cfe8e975f37bd182e9616fb4eb931ebebf8369f8 Author: Muhammad Khalikov <55890311+mukham12@users.noreply.github.com> Date: Sat May 6 12:43:35 2023 -0400 Fix a few typos in documentation (#3919) commit df541d3c74133496454dd9bfccc404b54a0faea6 Author: Kim, Joo Hyuk Date: Sat May 6 12:32:13 2023 +0900 Improve and fix JavaDocs for Jackson 2.15 (#3917) commit d44e014015914c4f721dd33e6c43e25c0c9625fc Merge: 924152d11 c8c7d395d Author: Tatu Saloranta Date: Fri May 5 09:36:27 2023 -0700 Merge branch '2.15' into 2.16 commit c8c7d395dddb07bb2e2d098c6232dd0ae35dd4a3 Merge: a7a8a8036 d47d1b642 Author: Tatu Saloranta Date: Fri May 5 09:36:21 2023 -0700 Merge branch '2.14' into 2.15 commit d47d1b642931246770ec67c4a98ad6aacc0f7d7d Author: Tatu Saloranta Date: Fri May 5 09:34:29 2023 -0700 Back to snapshot dep commit 6f3d20f8f3165a375eb5d44105c18285b4132faf Author: Tatu Saloranta Date: Fri May 5 09:31:43 2023 -0700 [maven-release-plugin] prepare for next development iteration commit 8cdba2177a411c9dfc8949bbf0f23b4fa8ef5d8d Author: Tatu Saloranta Date: Fri May 5 09:31:40 2023 -0700 [maven-release-plugin] prepare release jackson-databind-2.14.3 commit 2bd50de31a25a419e63b6f4350709999dbafe758 Author: Tatu Saloranta Date: Fri May 5 09:09:17 2023 -0700 Prepare for 2.14.3 release commit 924152d1112e594e8c6bb5644efd8ecd3b4dda91 Merge: 774ddb838 a7a8a8036 Author: Tatu Saloranta Date: Thu May 4 14:00:26 2023 -0700 Merge branch '2.15' into 2.16 commit a7a8a8036a843dd1b2022561bb1376575b180419 Author: Tatu Saloranta Date: Thu May 4 14:00:13 2023 -0700 ... commit 774ddb83872892f2442bc209d9b57036694cece2 Merge: f8477455d ad308b426 Author: Tatu Saloranta Date: Thu May 4 13:57:18 2023 -0700 Merge branch '2.15' into 2.16 commit ad308b426a253b0d3a8062b22abe1c2191f789c0 Author: Tatu Saloranta Date: Thu May 4 13:55:48 2023 -0700 Update release notes wrt #3897 commit 1fa2d861ecd8595f528db579075c1933cd38495b Author: Sim Yih Tsern Date: Fri May 5 04:52:30 2023 +0800 Record constructor with single write-only parameter should result in properties-based creator, to fix #3897. (#3910) commit f8477455d1e749791bb77c2f6ab455aae0f2f5af Merge: f3c60eddf ee3b89aed Author: Tatu Saloranta Date: Thu May 4 11:13:22 2023 -0700 Merge branch '2.16' of github.com:FasterXML/jackson-databind into 2.16 commit f3c60eddf7c0bbb690811cccbfb9313fd7f39aaf Merge: 23603ea11 75475913f Author: Tatu Saloranta Date: Thu May 4 11:13:15 2023 -0700 Merge branch '2.15' into 2.16 commit 75475913f0e49065652b3e52a2a8bff54d86fca5 Author: Tatu Saloranta Date: Thu May 4 11:12:53 2023 -0700 Mark #3895 as fixed (due to another PR/issue) commit ee3b89aed9b3fbba560a92e78bc58c53ab5c2962 Author: ChangYong Date: Fri May 5 02:09:33 2023 +0900 Fix incorrect comment (#3916) --- attic/MappingIteratorDeserializer.java | 47 --- attic/TypeBindings.java | 337 ------------------ pom.xml | 27 +- release-notes/CREDITS-2.x | 16 + release-notes/VERSION-2.x | 19 +- .../databind/AnnotationIntrospector.java | 4 +- .../jackson/databind/BeanDescription.java | 2 +- .../jackson/databind/MapperFeature.java | 9 +- .../jackson/databind/ObjectMapper.java | 2 +- .../jackson/databind/SerializerProvider.java | 18 +- .../databind/annotation/JsonAppend.java | 10 +- .../deser/BasicDeserializerFactory.java | 4 + .../databind/deser/BeanDeserializer.java | 4 +- .../deser/std/CollectionDeserializer.java | 4 +- .../databind/introspect/AnnotatedClass.java | 3 +- .../introspect/POJOPropertiesCollector.java | 11 +- .../jackson/databind/node/ArrayNode.java | 4 +- .../ser/DefaultSerializerProvider.java | 2 +- .../util/internal/PrivateMaxEntriesMap.java | 4 +- .../databind/records/RecordBasicsTest.java | 61 ++++ .../records/RecordFailingSetter3938Test.java | 42 +++ .../records/RecordSerializationOrderTest.java | 66 ++++ .../deser/dos/DeepJsonNodeDeser3397Test.java | 2 +- .../dos/DeepNestingUntypedDeserTest.java | 2 +- .../dos/StreamReadStringConstraintsTest.java | 19 +- .../TestPolymorphicDeserialization676.java | 2 +- .../jackson/databind/node/WithPathTest.java | 21 +- 27 files changed, 303 insertions(+), 439 deletions(-) delete mode 100644 attic/MappingIteratorDeserializer.java delete mode 100644 attic/TypeBindings.java create mode 100644 src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java create mode 100644 src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java diff --git a/attic/MappingIteratorDeserializer.java b/attic/MappingIteratorDeserializer.java deleted file mode 100644 index fcb12db89f..0000000000 --- a/attic/MappingIteratorDeserializer.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.fasterxml.jackson.databind.deser.std; - -import java.io.IOException; - -import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; - -public class MappingIteratorDeserializer - extends StdDeserializer> - implements ContextualDeserializer -{ - private static final long serialVersionUID = 1L; - - protected final JavaType _valueType; - - protected final JsonDeserializer _valueDeserializer; - - public MappingIteratorDeserializer(JavaType valueType) { - this(valueType, null); - } - - protected MappingIteratorDeserializer(JavaType valueType, JsonDeserializer vdeser) { - super(MappingIterator.class); - _valueType = valueType; - _valueDeserializer = vdeser; - } - - @Override - public JsonDeserializer createContextual(DeserializationContext ctxt, - BeanProperty prop) throws JsonMappingException - { - JsonDeserializer deser = ctxt.findContextualValueDeserializer(_valueType, prop); - return (deser == _valueDeserializer) ? this - : new MappingIteratorDeserializer(_valueType, deser); - } - - @Override - public MappingIterator deserialize(JsonParser p, - DeserializationContext ctxt) throws IOException, - JsonProcessingException - { - MappingIterator mit = new MappingIterator(_valueType, p, ctxt, - _valueDeserializer, false, null); - return mit; - } -} diff --git a/attic/TypeBindings.java b/attic/TypeBindings.java deleted file mode 100644 index 7570cc2bd0..0000000000 --- a/attic/TypeBindings.java +++ /dev/null @@ -1,337 +0,0 @@ -package com.fasterxml.jackson.databind.type; - -import java.lang.reflect.*; -import java.util.*; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.util.ClassUtil; - -/** - * Helper class used for resolving type parameters for given class - */ -public class TypeBindings -{ - private final static JavaType[] NO_TYPES = new JavaType[0]; - - /** - * Marker to use for (temporarily) unbound references. - */ - public final static JavaType UNBOUND = new SimpleType(Object.class); - - /** - * Factory to use for constructing resolved related types. - */ - protected final TypeFactory _typeFactory; - - /** - * @since 2.7 - */ - protected final ClassStack _classStack; - - /** - * Context type used for resolving all types, if specified. May be null, - * in which case {@link #_contextClass} is used instead. - */ - protected final JavaType _contextType; - - /** - * Specific class to use for resolving all types, for methods and fields - * class and its superclasses and -interfaces contain. - */ - protected final Class _contextClass; - - /** - * Lazily-instantiated bindings of resolved type parameters - */ - protected Map _bindings; - - /** - * Also: we may temporarily want to mark certain named types - * as resolved (but without exact type); if so, we'll just store - * names here. - */ - protected HashSet _placeholders; - - /** - * Sometimes it is necessary to allow hierarchic resolution of types: specifically - * in cases where there are local bindings (for methods, constructors). If so, - * we'll just use simple delegation model. - */ - private final TypeBindings _parentBindings; - - /* - /********************************************************** - /* Construction - /********************************************************** - */ - - public TypeBindings(TypeFactory typeFactory, ClassStack stack, Class cc) - { - this(typeFactory, null, stack, cc, null); - } - - public TypeBindings(TypeFactory typeFactory, ClassStack stack, JavaType type) - { - this(typeFactory, null, stack, type.getRawClass(), type); - } - - /** - * Constructor used to create "child" instances; mostly to - * allow delegation from explicitly defined local overrides - * (local type variables for methods, constructors) to - * contextual (class-defined) ones. - */ - public TypeBindings childInstance() { - return new TypeBindings(_typeFactory, this, _classStack, _contextClass, _contextType); - } - - private TypeBindings(TypeFactory tf, TypeBindings parent, ClassStack stack, - Class cc, JavaType type) - { - _typeFactory = tf; - _parentBindings = parent; - _classStack = stack; - _contextClass = cc; - _contextType = type; - } - - /* - /********************************************************** - /* Pass-through type resolution methods - /********************************************************** - */ - - public JavaType resolveType(Class cls) { - return _typeFactory._constructType(_classStack, cls, this); - } - - public JavaType resolveType(Type type) { - return _typeFactory._constructType(_classStack, type, this); - } - - /* - /********************************************************** - /* Accesors - /********************************************************** - */ - - public JavaType findType(String name, boolean mustFind) - { - if (_bindings == null) { - _resolve(); - } - JavaType t = _bindings.get(name); - if (t != null) { - return t; - } - if (_placeholders != null && _placeholders.contains(name)) { - return UNBOUND; - } - if (_parentBindings != null) { - return _parentBindings.findType(name, mustFind); - } - // nothing found, so... - // Should we throw an exception or just return null? - - /* 18-Feb-2011, tatu: There are some tricky type bindings within - * java.util, such as HashMap$KeySet; so let's punt the problem - * (honestly not sure what to do -- they are unbound for good, I think) - */ - if (_contextClass != null) { - if (ClassUtil.getEnclosingClass(_contextClass) != null) { - // [JACKSON-572]: Actually, let's skip this for all non-static inner classes - // (which will also cover 'java.util' type cases... - if (!Modifier.isStatic(_contextClass.getModifiers())) { - return UNBOUND; - } - } - } - - if (!mustFind) { - return null; - } - - String className; - if (_contextClass != null) { - className = _contextClass.getName(); - } else if (_contextType != null) { - className = _contextType.toString(); - } else { - className = "UNKNOWN"; - } - throw new IllegalArgumentException("Type variable '"+name - +"' can not be resolved (with context of class "+className+")"); - //t = UNBOUND; - } - - public void addBinding(String name, JavaType type) - { - // note: emptyMap() is unmodifiable, hence second check is needed: - if (_bindings == null || _bindings.size() == 0) { - _bindings = new LinkedHashMap(); - } - _bindings.put(name, type); - } - - public JavaType[] typesAsArray() - { - if (_bindings == null) { - _resolve(); - } - if (_bindings.size() == 0) { - return NO_TYPES; - } - return _bindings.values().toArray(new JavaType[_bindings.size()]); - } - - /* - /********************************************************** - /* Internal methods - /********************************************************** - */ - - // Only for tests! - protected int getBindingCount() { - if (_bindings == null) { - _resolve(); - } - return _bindings.size(); - } - - protected void _resolve() - { - _resolveBindings(_contextClass); - - // finally: may have root level type info too - if (_contextType != null) { - int count = _contextType.containedTypeCount(); - if (count > 0) { - for (int i = 0; i < count; ++i) { - String name = _contextType.containedTypeName(i); - JavaType type = _contextType.containedType(i); - addBinding(name, type); - } - } - } - - // nothing bound? mark with empty map to prevent further calls - if (_bindings == null) { - _bindings = Collections.emptyMap(); - } - } - - public void _addPlaceholder(String name) { - if (_placeholders == null) { - _placeholders = new HashSet(); - } - _placeholders.add(name); - } - - protected void _resolveBindings(Type t) - { - if (t == null) return; - - Class raw; - if (t instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) t; - Type[] args = pt.getActualTypeArguments(); - if (args != null && args.length > 0) { - Class rawType = (Class) pt.getRawType(); - TypeVariable[] vars = rawType.getTypeParameters(); - if (vars.length != args.length) { - throw new IllegalArgumentException("Strange parametrized type (in class "+rawType.getName()+"): number of type arguments != number of type parameters ("+args.length+" vs "+vars.length+")"); - } - for (int i = 0, len = args.length; i < len; ++i) { - TypeVariable var = vars[i]; - String name = var.getName(); - if (_bindings == null) { - _bindings = new LinkedHashMap(); - } else { - // 24-Mar-2010, tatu: Better ensure that we do not overwrite something - // collected earlier (since we descend towards super-classes): - if (_bindings.containsKey(name)) continue; - } - // first: add a placeholder to prevent infinite loops - _addPlaceholder(name); - // then resolve type - _bindings.put(name, _typeFactory._constructType(_classStack, args[i], this)); - } - } - raw = (Class)pt.getRawType(); - } else if (t instanceof Class) { - raw = (Class) t; - /* [JACKSON-677]: If this is an inner class then the generics are defined on the - * enclosing class so we have to check there as well. We don't - * need to call getEnclosingClass since anonymous classes declare - * generics - */ - Class decl = ClassUtil.getDeclaringClass(raw); - /* 08-Feb-2013, tatu: Except that if context is also super-class, we must - * skip it; context will be checked anyway, and we'd get StackOverflow if - * we went there. - */ - if (decl != null && !decl.isAssignableFrom(raw)) { - _resolveBindings(decl); - } - - /* 24-Mar-2010, tatu: Can not have true generics definitions, but can - * have lower bounds ("") in declaration itself - */ - TypeVariable[] vars = raw.getTypeParameters(); - if (vars != null && vars.length > 0) { - JavaType[] typeParams = null; - - if (_contextType != null && raw.isAssignableFrom(_contextType.getRawClass())) { - typeParams = _typeFactory.findTypeParameters(_contextType, raw); - } - - for (int i = 0; i < vars.length; i++) { - TypeVariable var = vars[i]; - - String name = var.getName(); - Type varType = var.getBounds()[0]; - if (varType != null) { - if (_bindings == null) { - _bindings = new LinkedHashMap(); - } else { // and no overwriting... - if (_bindings.containsKey(name)) continue; - } - _addPlaceholder(name); // to prevent infinite loops - - if (typeParams != null && typeParams.length > i) { - _bindings.put(name, typeParams[i]); - } else { - _bindings.put(name, _typeFactory._constructType(_classStack, varType, this)); - } - } - } - } - } else { // probably can't be any of these... so let's skip for now - //if (type instanceof GenericArrayType) { - //if (type instanceof TypeVariable) { - // if (type instanceof WildcardType) { - return; - } - // but even if it's not a parameterized type, its super types may be: - _resolveBindings(ClassUtil.getGenericSuperclass(raw)); - for (Type intType : raw.getGenericInterfaces()) { - _resolveBindings(intType); - } - } - - @Override - public String toString() - { - if (_bindings == null) { - _resolve(); - } - StringBuilder sb = new StringBuilder("[TypeBindings for "); - if (_contextType != null) { - sb.append(_contextType.toString()); - } else { - sb.append(_contextClass.getName()); - } - sb.append(": ").append(_bindings).append("]"); - return sb.toString(); - } -} diff --git a/pom.xml b/pom.xml index dade30be71..3728c5cd96 100644 --- a/pom.xml +++ b/pom.xml @@ -88,10 +88,17 @@ ${jackson.version.core} - - + + + org.junit.vintage + junit-vintage-engine + test + + + org.junit.jupiter + junit-jupiter + test + org.powermock powermock-core @@ -144,6 +151,18 @@ + + + + org.junit + junit-bom + 5.9.2 + pom + import + + + + diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 73f5c66d1a..c7e9be5ba3 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -6,6 +6,8 @@ databind core component, version 2.x Tatu Saloranta, tatu.saloranta@iki.fi: author +---------------------------------------------------------------------------- + Pascal GŽlinas: * Contributed fixes to 'MappingIterator' handling (Pull#58 and Pull#59) (2.1.0) @@ -1495,6 +1497,7 @@ PJ Fanning (pjfanning@github) (2.14.0) * Contributed #3837: Set transformer factory attributes to improve protection against XXE (2.14.3) + * ... and countless more Igor Shymko (ancane@github) * Contributed #3500: Add optional explicit `JsonSubTypes` repeated names check @@ -1595,6 +1598,9 @@ Sim Yih Tsern (yihtsern@github) (2.15.0) * Contributed fix for #3894: Only avoid Records fields detection for deserialization (2.15.1) + * Contributed fix for #3897: 2.15.0 breaks deserialization when POJO/Record only has a + single field and is marked `Access.WRITE_ONLY` + (2.15.1) Ajay Siwach (Siwach16@github) * Contributed #3637: Add enum features into `@JsonFormat.Feature` @@ -1624,3 +1630,13 @@ Steve Storey (stevestorey@github) strict subtype Type Id handling (2.15.0) +Matteo Bertozzi (matteobertozzi@github) + * Reported #3895: 2.15.0 breaking behaviour change for records and Getter Visibility + (2.15.1) + +Antti Lampinen (arlampin@github) + * Reported #3897: 2.15.0 breaks deserialization when POJO/Record only has a single field + and is marked `Access.WRITE_ONLY` + (2.15.1) + + diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 9855754393..3a80213e88 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -6,12 +6,25 @@ Project: jackson-databind 2.16.0 (not yet released) -No changes since 2.15 +#3928: `@JsonProperty` on constructor parameter changes default field serialization order + (contributed by @pjfanning) + +2.15.2 (not yet released) -2.15.1 (not yet released) +#3938: Record setter not included from interface (2.15 regression) +2.15.1 (16-May-2023) + +#3882: Error in creating nested `ArrayNode`s with `JsonNode.withArray()` + (reported by @SaiKrishna369) #3894: Only avoid Records fields detection for deserialization (contributed by Sim Y-T) +#3895: 2.15.0 breaking behaviour change for records and Getter Visibility + (reported by Matteo B) +#3897: 2.15.0 breaks deserialization when POJO/Record only has a single field + and is marked `Access.WRITE_ONLY` + (reported by Antti L) + (fix contributed by Sim Y-T) #3913: Issue with deserialization when there are unexpected properties (due to null `StreamReadConstraints`) (reported by @sbertault) @@ -99,7 +112,7 @@ No changes since 2.15 #3876: `TypeFactory` cache performance degradation with `constructSpecializedType()` (contributed by Carter K) -2.14.3 (not yet released) +2.14.3 (05-May-2023) #3784: `PrimitiveArrayDeserializers$ByteDeser.deserialize` ignores `DeserializationProblemHandler` for invalid Base64 content diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java index 2905a9beac..53d5bc9838 100644 --- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java @@ -59,7 +59,7 @@ public enum Type { * Usually this can be defined by using * {@link com.fasterxml.jackson.annotation.JsonManagedReference} */ - MANAGED_REFERENCE + MANAGED_REFERENCE, /** * Reference property that Jackson manages by suppressing it during serialization, @@ -67,7 +67,7 @@ public enum Type { * Usually this can be defined by using * {@link com.fasterxml.jackson.annotation.JsonBackReference} */ - ,BACK_REFERENCE + BACK_REFERENCE ; } diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java index 9170f68049..dc256d13b3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java +++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java @@ -40,7 +40,7 @@ protected BeanDescription(JavaType type) { /* /********************************************************** - /* Simple accesors + /* Simple accessors /********************************************************** */ diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java index 2ecf7ee450..830c5f6742 100644 --- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java @@ -40,7 +40,7 @@ public enum MapperFeature implements ConfigFeature * and Map to modify the property, without requiring a setter * method. * This is similar to how JAXB framework sets Collections and - * Maps: no setter is involved, just setter. + * Maps: no setter is involved, just getter. *

* Note that such getters-as-setters methods have lower * precedence than setters, so they are only used if no @@ -322,7 +322,7 @@ public enum MapperFeature implements ConfigFeature * Feature is enabled by default which means that deserialization does * support deserializing types via builders with type parameters (generic types). *

- * See: https://github.com/FasterXML/jackson-databind/issues/921 + * See: databind#921 * * @since 2.12 */ @@ -401,6 +401,11 @@ public enum MapperFeature implements ConfigFeature *

* Note: does not apply to {@link java.util.Map} serialization (since * entries are not considered Bean/POJO properties. + *

+ * WARNING: Disabling it may have a negative impact on deserialization performance. + * When disabled, all properties before the last creator property in the input need to be buffered, + * since all creator properties are required to create the instance. + * Enabling this feature ensures that there is as little buffering as possible. *

* Feature is enabled by default. * diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 7c92f52dab..0ce19c8689 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -4623,7 +4623,7 @@ public T updateValue(T valueToUpdate, Object overrides) * @param t The class to generate schema for * @return Constructed JSON schema. * - * @deprecated Since 2.6 use external JSON Schema generator (https://github.com/FasterXML/jackson-module-jsonSchema) + * @deprecated Since 2.6 use external JSON Schema generator (jackson-module-jsonSchema) * (which under the hood calls {@link #acceptJsonFormatVisitor(JavaType, JsonFormatVisitorWrapper)}) */ @Deprecated diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java index c48639927f..7ad51f1b8c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java @@ -947,19 +947,15 @@ public JsonSerializer getDefaultNullValueSerializer() { } /** - * Method called to get the serializer to use for serializing - * Map keys that are nulls: this is needed since JSON does not allow - * any non-String value as key, including null. - *

- * Typically, returned serializer - * will either throw an exception, or use an empty String; but - * other behaviors are possible. - */ - /** - * Method called to find a serializer to use for null values for given - * declared type. Note that type is completely based on declared type, + * Method called to find a serializer to serializes Map keys that are nulls, + * as JSON does not allow any non-String value as a key, including null. + * Note that type is completely based on declared type, * since nulls in Java have no type and thus runtime type cannot be * determined. + * + * @return JsonSerializer that handles the serialization of null keys, + * usually by throwing an exception or using an empty String, + * but other behaviors are also possible. * * @since 2.0 */ diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java index 122f6a7d08..e77e76e714 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java @@ -9,10 +9,12 @@ import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; /** - * Annotation that may be used to add "virtual" properties to be written - * after regular properties (although ordering may be changed using - * both standard @JsonPropertyOrder annotation, and - * properties of this annotation). + * Annotation used to add "virtual" properties that will be written + * after regular properties during serialization. + *

+ * Please note that the "virtual" properties added using this annotation + * do not obey any specific order, including the order defined + * by {@link com.fasterxml.jackson.annotation.JsonPropertyOrder}. * * @since 2.5 */ 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 adbfcdc012..88b0da75ae 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -1031,6 +1031,10 @@ private boolean _checkIfCreatorPropertyBased(BeanDescription beanDesc, return true; } } + // [databind#3897]: Record canonical constructor will have implicitly named propDef + if (!propDef.isExplicitlyNamed() && beanDesc.isRecordType()) { + return true; + } } // in absence of everything else, default to delegating return false; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index 16a219aac3..fa21406337 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -474,7 +474,9 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri // weren't removed (to help in creating constructor-backed PropertyCreator) // so they ended up in _beanProperties, unlike POJO (whose ignored // props are removed) - if ((prop != null) && !_beanType.isRecordType()) { + if ((prop != null) && + // [databind#3938]: except if it's MethodProperty + (!_beanType.isRecordType() || (prop instanceof MethodProperty))) { try { buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); } catch (UnresolvedForwardReference reference) { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java index 15175767d4..76c07a661f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java @@ -529,8 +529,8 @@ public void resolveForwardReference(Object id, Object value) throws IOException /** * Helper class to maintain processing order of value. The resolved - * object associated with {@link #_id} comes before the values in - * {@link #next}. + * object associated with {@code #id} parameter from {@link #handleResolvedForwardReference(Object, Object)} + * comes before the values in {@link #next}. */ private final static class CollectionReferring extends Referring { private final CollectionReferringAccumulator _parent; diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java index 24e850315b..c184c80701 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -202,8 +202,7 @@ public static AnnotatedClass construct(JavaType type, MapperConfig config, * Method similar to {@link #construct}, but that will NOT include * information from supertypes; only class itself and any direct * mix-ins it may have. - */ - /** + * * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead. */ @Deprecated diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java index fabeb15cbb..40df20e693 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -91,7 +91,7 @@ public class POJOPropertiesCollector /** * A set of "field renamings" that have been discovered, indicating - * intended renaming of other accesors: key is the implicit original + * intended renaming of other accessors: key is the implicit original * name and value intended name to use instead. *

* Note that these renamings are applied earlier than "regular" (explicit) @@ -437,17 +437,18 @@ protected void collectAll() LinkedHashMap props = new LinkedHashMap(); // First: gather basic data - + final boolean isRecord = isRecordType(); // 15-Jan-2023, tatu: [databind#3736] Let's avoid detecting fields of Records // altogether (unless we find a good reason to detect them) // 17-Apr-2023: Need Records' fields for serialization for cases like [databind#3895] & [databind#3628] - if (!isRecordType() || _forSerialization) { + if (!isRecord || _forSerialization) { _addFields(props); // note: populates _fieldRenameMappings } _addMethods(props); // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static // inner classes, see [databind#1502] - if (!_classDef.isNonStaticInnerClass()) { + // 13-May-2023, PJ: Need to avoid adding creators for Records when serializing [databind#3925] + if (!_classDef.isNonStaticInnerClass() && !(_forSerialization && isRecord)) { _addCreators(props); } @@ -761,7 +762,7 @@ protected void _addMethods(Map props) _addGetterMethod(props, m, _annotationIntrospector); } else if (argCount == 1) { // setters _addSetterMethod(props, m, _annotationIntrospector); - } else if (argCount == 2) { // any getter? + } else if (argCount == 2) { // any setter? if (Boolean.TRUE.equals(_annotationIntrospector.hasAnySetter(m))) { if (_anySetters == null) { _anySetters = new LinkedList(); diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java index 0f1ac43b7e..06e2800e68 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java @@ -188,9 +188,9 @@ protected ArrayNode _withArrayAddTailElement(JsonPointer tail, boolean preferInd _withXxxSetArrayElement(index, next); return next._withArrayAddTailElement(tail, preferIndex); } - ArrayNode next = this.arrayNode(); + ObjectNode next = this.objectNode(); _withXxxSetArrayElement(index, next); - return next._withArrayAddTailElement(tail, preferIndex); + return next._withArrayAddTailProperty(tail, preferIndex); } protected void _withXxxSetArrayElement(int index, JsonNode value) { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java index bca1f7d41e..2c9f0ac6f9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java @@ -233,7 +233,7 @@ protected Map _createObjectIdMap() /* /********************************************************** - /* Extended API: simple accesors + /* Extended API: simple accessors /********************************************************** */ diff --git a/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java b/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java index 4cd867afdd..d74b89aa0b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java @@ -136,7 +136,7 @@ public final class PrivateMaxEntriesMap extends AbstractMap /** * The number of read buffers to use. - * The max of 4 was introduced due to https://github.com/FasterXML/jackson-databind/issues/3665. + * The max of 4 was introduced due to databind#3665. */ static final int NUMBER_OF_READ_BUFFERS = Math.min(4, ceilingNextPowerOfTwo(NCPU)); @@ -145,7 +145,7 @@ public final class PrivateMaxEntriesMap extends AbstractMap /** * The number of pending read operations before attempting to drain. - * The threshold of 4 was introduced due to https://github.com/FasterXML/jackson-databind/issues/3665. + * The threshold of 4 was introduced due to databind#3665. */ static final int READ_BUFFER_THRESHOLD = 4; diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java index f94231b526..c2dfb3261d 100644 --- a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java +++ b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java @@ -40,6 +40,20 @@ record SnakeRecord(String myId, String myValue){} record RecordWithJsonDeserialize(int id, @JsonDeserialize(converter = StringTrimmer.class) String name) { } + record RecordSingleWriteOnly(@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) int id) { } + + record RecordSomeWriteOnly( + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) int id, + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String name, + String email) { + } + + record RecordAllWriteOnly( + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) int id, + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String name, + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String email) { + } + private final ObjectMapper MAPPER = newJsonMapper(); /* @@ -204,6 +218,53 @@ public void testDeserializeJsonDeserializeRecord() throws Exception { assertEquals(new RecordWithJsonDeserialize(123, "Bob"), value); } + /* + /********************************************************************** + /* Test methods, JsonProperty(access=WRITE_ONLY) + /********************************************************************** + */ + + public void testSerialize_SingleWriteOnlyParameter() throws Exception { + String json = MAPPER.writeValueAsString(new RecordSingleWriteOnly(123)); + + assertEquals("{}", json); + } + + // [databind#3897] + public void testDeserialize_SingleWriteOnlyParameter() throws Exception { + RecordSingleWriteOnly value = MAPPER.readValue("{\"id\":123}", RecordSingleWriteOnly.class); + + assertEquals(new RecordSingleWriteOnly(123), value); + } + + public void testSerialize_SomeWriteOnlyParameter() throws Exception { + String json = MAPPER.writeValueAsString(new RecordSomeWriteOnly(123, "Bob", "bob@example.com")); + + assertEquals("{\"email\":\"bob@example.com\"}", json); + } + + public void testDeserialize_SomeWriteOnlyParameter() throws Exception { + RecordSomeWriteOnly value = MAPPER.readValue( + "{\"id\":123,\"name\":\"Bob\",\"email\":\"bob@example.com\"}", + RecordSomeWriteOnly.class); + + assertEquals(new RecordSomeWriteOnly(123, "Bob", "bob@example.com"), value); + } + + public void testSerialize_AllWriteOnlyParameter() throws Exception { + String json = MAPPER.writeValueAsString(new RecordAllWriteOnly(123, "Bob", "bob@example.com")); + + assertEquals("{}", json); + } + + public void testDeserialize_AllWriteOnlyParameter() throws Exception { + RecordAllWriteOnly value = MAPPER.readValue( + "{\"id\":123,\"name\":\"Bob\",\"email\":\"bob@example.com\"}", + RecordAllWriteOnly.class); + + assertEquals(new RecordAllWriteOnly(123, "Bob", "bob@example.com"), value); + } + /* /********************************************************************** /* Internal helper methods diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java new file mode 100644 index 0000000000..3fe8cfef18 --- /dev/null +++ b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java @@ -0,0 +1,42 @@ +package com.fasterxml.jackson.databind.records; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.*; + +public class RecordFailingSetter3938Test extends BaseMapTest +{ + private final static String ERROR_3938_PREFIX = "Non-null 'options' not allowed for "; + + // [databind#3938] + interface NoOptionsCommand { + @JsonProperty("options") + default void setOptions(JsonNode value) { + if (value.isNull()) { + return; + } + throw new IllegalArgumentException(ERROR_3938_PREFIX+getClass().getName()); + } + } + + public record Command3938(int id, String filter) implements NoOptionsCommand { } + + private final ObjectMapper MAPPER = newJsonMapper(); + + // [databind#3938]: Should detect and use setters too + public void testFailingSetter3939() throws Exception + { + final ObjectReader R = MAPPER.readerFor(Command3938.class); + + // First, missing value and `null` are fine, as long as we have all fields + assertNotNull(R.readValue(a2q("{'id':1, 'filter':'abc'}"))); + assertNotNull(R.readValue(a2q("{'id':2, 'filter':'abc', 'options':null}"))); + + // But then failure for non-empty Array (f.ex) + try { + R.readValue(a2q("{'id':2,'options':[123]}}")); + fail("Should not pass"); + } catch (DatabindException e) { + verifyException(e, ERROR_3938_PREFIX); + } + } +} diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java new file mode 100644 index 0000000000..494904c3e9 --- /dev/null +++ b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.records; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class RecordSerializationOrderTest extends BaseMapTest +{ + record NestedRecordOne(String id, String email, NestedRecordTwo nestedRecordTwo) {} + record NestedRecordOneWithJsonProperty(String id, String email, + @JsonProperty("nestedProperty") NestedRecordTwo nestedRecordTwo) {} + record NestedRecordOneWithJsonPropertyIndex(@JsonProperty(index = 2) String id, + @JsonProperty(index = 0) String email, + @JsonProperty(value = "nestedProperty", index = 1) NestedRecordTwo nestedRecordTwo) {} + + @JsonPropertyOrder({"email", "nestedProperty", "id"}) + record NestedRecordOneWithJsonPropertyOrder(String id, + String email, + @JsonProperty(value = "nestedProperty") NestedRecordTwo nestedRecordTwo) {} + + record NestedRecordTwo(String id, String passport) {} + + private final ObjectMapper MAPPER = newJsonMapper(); + + /* + /********************************************************************** + /* Test methods, alternate constructors + /********************************************************************** + */ + + public void testSerializationOrder() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOne nestedRecordOne = new NestedRecordOne("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"id\":\"1\",\"email\":\"test@records.com\",\"nestedRecordTwo\":{\"id\":\"2\",\"passport\":\"111110\"}}"; + assertEquals(expected, output); + } + + public void testSerializationOrderWithJsonProperty() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOneWithJsonProperty nestedRecordOne = + new NestedRecordOneWithJsonProperty("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"id\":\"1\",\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"}}"; + assertEquals(expected, output); + } + + public void testSerializationOrderWithJsonPropertyIndexes() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOneWithJsonPropertyIndex nestedRecordOne = + new NestedRecordOneWithJsonPropertyIndex("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"},\"id\":\"1\"}"; + assertEquals(expected, output); + } + + public void testSerializationOrderWithJsonPropertyOrder() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOneWithJsonPropertyOrder nestedRecordOne = + new NestedRecordOneWithJsonPropertyOrder("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"},\"id\":\"1\"}"; + assertEquals(expected, output); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java index f846453282..adbf3e051d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java @@ -13,7 +13,7 @@ public class DeepJsonNodeDeser3397Test extends BaseMapTest // ... currently gets a bit slow at 1M but passes. // But test with 100k as practical limit, to guard against regression // private final static int TOO_DEEP_NESTING = 1_000_000; - private final static int TOO_DEEP_NESTING = 10_000; + private final static int TOO_DEEP_NESTING = StreamReadConstraints.DEFAULT_MAX_DEPTH * 10; private final JsonFactory jsonFactory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build()) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java index 963040b9af..6389da8841 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java @@ -18,7 +18,7 @@ public class DeepNestingUntypedDeserTest extends BaseMapTest // 31-May-2022, tatu: But no more! Can handle much much larger // nesting levels, bounded by memory usage not stack. Tested with // 1 million (!) nesting levels, but to keep tests fast use 100k - private final static int TOO_DEEP_NESTING = 100_000; + private final static int TOO_DEEP_NESTING = StreamReadConstraints.DEFAULT_MAX_DEPTH * 100; private final JsonFactory jsonFactory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build()) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java index d7c26882b4..15c29eee2b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper; /** - * Tests for {@a href="https://github.com/FasterXML/jackson-core/issues/863"}. + * Tests for databind#863" */ public class StreamReadStringConstraintsTest extends BaseMapTest { @@ -32,6 +32,8 @@ void setString(String string) { /********************************************************************** */ + private final static int TOO_LONG_STRING_VALUE = StreamReadConstraints.DEFAULT_MAX_STRING_LEN + 100; + private final ObjectMapper MAPPER = newJsonMapper(); private ObjectMapper newJsonMapperWithUnlimitedStringSizeSupport() { @@ -44,31 +46,32 @@ private ObjectMapper newJsonMapperWithUnlimitedStringSizeSupport() { public void testBigString() throws Exception { try { - MAPPER.readValue(generateJson("string", 5001000), StringWrapper.class); - fail("expected JsonMappingException"); + MAPPER.readValue(generateJson("string", TOO_LONG_STRING_VALUE), StringWrapper.class); + fail("expected DatabindException"); } catch (DatabindException e) { - assertTrue("unexpected exception message: " + e.getMessage(), - e.getMessage().startsWith("String value length (5001000) exceeds the maximum allowed (5000000")); + final String message = e.getMessage(); + assertTrue("unexpected exception message: " + message, message.startsWith("String value length")); + assertTrue("unexpected exception message: " + message, message.contains("exceeds the maximum allowed (")); } } public void testBiggerString() throws Exception { try { - MAPPER.readValue(generateJson("string", 6_000_000), StringWrapper.class); + MAPPER.readValue(generateJson("string", TOO_LONG_STRING_VALUE), StringWrapper.class); fail("expected JsonMappingException"); } catch (DatabindException e) { final String message = e.getMessage(); // this test fails when the TextBuffer is being resized, so we don't yet know just how big the string is // so best not to assert that the String length value in the message is the full 6000000 value assertTrue("unexpected exception message: " + message, message.startsWith("String value length")); - assertTrue("unexpected exception message: " + message, message.contains("exceeds the maximum allowed (5000000")); + assertTrue("unexpected exception message: " + message, message.contains("exceeds the maximum allowed (")); } } public void testUnlimitedString() throws Exception { - final int len = 5_001_000; + final int len = TOO_LONG_STRING_VALUE; StringWrapper sw = newJsonMapperWithUnlimitedStringSizeSupport() .readValue(generateJson("string", len), StringWrapper.class); assertEquals(len, sw.string.length()); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java index d6b1bcc557..063e4bf594 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java @@ -8,7 +8,7 @@ import java.util.*; /** - * Reproduction of [https://github.com/FasterXML/jackson-databind/issues/676] + * Reproduction of databind#676 *

* Deserialization of class with generic collection inside * depends on how is was deserialized first time. diff --git a/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java b/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java index f3e86cc986..73cbf1e1e2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.JsonNode.OverwriteMode; -// for [databuind#1980] implementation +// for [databind#1980] implementation public class WithPathTest extends BaseMapTest { private final ObjectMapper MAPPER = sharedMapper(); @@ -296,4 +296,23 @@ private void _verifyArrayReplaceFail(JsonNode doc, JsonPointer ptr, OverwriteMod verifyException(e, "(mode `OverwriteMode."+mode.name()+"`)"); } } + + // [databind#3882] + public void testWithArray3882() throws Exception + { + ObjectNode root = MAPPER.createObjectNode(); + ArrayNode aN = root.withArray("/key/0/a", + JsonNode.OverwriteMode.ALL, true); + aN.add(123); + assertEquals(a2q("{'key':[{'a':[123]}]}"), + root.toString()); + + // And then the original case + root = MAPPER.createObjectNode(); + aN = root.withArray(JsonPointer.compile("/key1/array1/0/element1"), + JsonNode.OverwriteMode.ALL, true); + aN.add("v1"); + assertEquals(a2q("{'key1':{'array1':[{'element1':['v1']}]}}"), + root.toString()); + } } From 6b71e0163ae7d9a47dd7a145e0f782c624e3e93c Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Mon, 22 May 2023 23:27:03 +0900 Subject: [PATCH 17/17] Revert "Squashed commit of the following:" This reverts commit 7eea4728774527a242e58bbc3ef9cf1290920b53. --- attic/MappingIteratorDeserializer.java | 47 +++ attic/TypeBindings.java | 337 ++++++++++++++++++ pom.xml | 27 +- release-notes/CREDITS-2.x | 16 - release-notes/VERSION-2.x | 19 +- .../databind/AnnotationIntrospector.java | 4 +- .../jackson/databind/BeanDescription.java | 2 +- .../jackson/databind/MapperFeature.java | 9 +- .../jackson/databind/ObjectMapper.java | 2 +- .../jackson/databind/SerializerProvider.java | 18 +- .../databind/annotation/JsonAppend.java | 10 +- .../deser/BasicDeserializerFactory.java | 4 - .../databind/deser/BeanDeserializer.java | 4 +- .../deser/std/CollectionDeserializer.java | 4 +- .../databind/introspect/AnnotatedClass.java | 3 +- .../introspect/POJOPropertiesCollector.java | 11 +- .../jackson/databind/node/ArrayNode.java | 4 +- .../ser/DefaultSerializerProvider.java | 2 +- .../util/internal/PrivateMaxEntriesMap.java | 4 +- .../databind/records/RecordBasicsTest.java | 61 ---- .../records/RecordFailingSetter3938Test.java | 42 --- .../records/RecordSerializationOrderTest.java | 66 ---- .../deser/dos/DeepJsonNodeDeser3397Test.java | 2 +- .../dos/DeepNestingUntypedDeserTest.java | 2 +- .../dos/StreamReadStringConstraintsTest.java | 19 +- .../TestPolymorphicDeserialization676.java | 2 +- .../jackson/databind/node/WithPathTest.java | 21 +- 27 files changed, 439 insertions(+), 303 deletions(-) create mode 100644 attic/MappingIteratorDeserializer.java create mode 100644 attic/TypeBindings.java delete mode 100644 src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java delete mode 100644 src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java diff --git a/attic/MappingIteratorDeserializer.java b/attic/MappingIteratorDeserializer.java new file mode 100644 index 0000000000..fcb12db89f --- /dev/null +++ b/attic/MappingIteratorDeserializer.java @@ -0,0 +1,47 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; + +public class MappingIteratorDeserializer + extends StdDeserializer> + implements ContextualDeserializer +{ + private static final long serialVersionUID = 1L; + + protected final JavaType _valueType; + + protected final JsonDeserializer _valueDeserializer; + + public MappingIteratorDeserializer(JavaType valueType) { + this(valueType, null); + } + + protected MappingIteratorDeserializer(JavaType valueType, JsonDeserializer vdeser) { + super(MappingIterator.class); + _valueType = valueType; + _valueDeserializer = vdeser; + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty prop) throws JsonMappingException + { + JsonDeserializer deser = ctxt.findContextualValueDeserializer(_valueType, prop); + return (deser == _valueDeserializer) ? this + : new MappingIteratorDeserializer(_valueType, deser); + } + + @Override + public MappingIterator deserialize(JsonParser p, + DeserializationContext ctxt) throws IOException, + JsonProcessingException + { + MappingIterator mit = new MappingIterator(_valueType, p, ctxt, + _valueDeserializer, false, null); + return mit; + } +} diff --git a/attic/TypeBindings.java b/attic/TypeBindings.java new file mode 100644 index 0000000000..7570cc2bd0 --- /dev/null +++ b/attic/TypeBindings.java @@ -0,0 +1,337 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.*; +import java.util.*; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Helper class used for resolving type parameters for given class + */ +public class TypeBindings +{ + private final static JavaType[] NO_TYPES = new JavaType[0]; + + /** + * Marker to use for (temporarily) unbound references. + */ + public final static JavaType UNBOUND = new SimpleType(Object.class); + + /** + * Factory to use for constructing resolved related types. + */ + protected final TypeFactory _typeFactory; + + /** + * @since 2.7 + */ + protected final ClassStack _classStack; + + /** + * Context type used for resolving all types, if specified. May be null, + * in which case {@link #_contextClass} is used instead. + */ + protected final JavaType _contextType; + + /** + * Specific class to use for resolving all types, for methods and fields + * class and its superclasses and -interfaces contain. + */ + protected final Class _contextClass; + + /** + * Lazily-instantiated bindings of resolved type parameters + */ + protected Map _bindings; + + /** + * Also: we may temporarily want to mark certain named types + * as resolved (but without exact type); if so, we'll just store + * names here. + */ + protected HashSet _placeholders; + + /** + * Sometimes it is necessary to allow hierarchic resolution of types: specifically + * in cases where there are local bindings (for methods, constructors). If so, + * we'll just use simple delegation model. + */ + private final TypeBindings _parentBindings; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + public TypeBindings(TypeFactory typeFactory, ClassStack stack, Class cc) + { + this(typeFactory, null, stack, cc, null); + } + + public TypeBindings(TypeFactory typeFactory, ClassStack stack, JavaType type) + { + this(typeFactory, null, stack, type.getRawClass(), type); + } + + /** + * Constructor used to create "child" instances; mostly to + * allow delegation from explicitly defined local overrides + * (local type variables for methods, constructors) to + * contextual (class-defined) ones. + */ + public TypeBindings childInstance() { + return new TypeBindings(_typeFactory, this, _classStack, _contextClass, _contextType); + } + + private TypeBindings(TypeFactory tf, TypeBindings parent, ClassStack stack, + Class cc, JavaType type) + { + _typeFactory = tf; + _parentBindings = parent; + _classStack = stack; + _contextClass = cc; + _contextType = type; + } + + /* + /********************************************************** + /* Pass-through type resolution methods + /********************************************************** + */ + + public JavaType resolveType(Class cls) { + return _typeFactory._constructType(_classStack, cls, this); + } + + public JavaType resolveType(Type type) { + return _typeFactory._constructType(_classStack, type, this); + } + + /* + /********************************************************** + /* Accesors + /********************************************************** + */ + + public JavaType findType(String name, boolean mustFind) + { + if (_bindings == null) { + _resolve(); + } + JavaType t = _bindings.get(name); + if (t != null) { + return t; + } + if (_placeholders != null && _placeholders.contains(name)) { + return UNBOUND; + } + if (_parentBindings != null) { + return _parentBindings.findType(name, mustFind); + } + // nothing found, so... + // Should we throw an exception or just return null? + + /* 18-Feb-2011, tatu: There are some tricky type bindings within + * java.util, such as HashMap$KeySet; so let's punt the problem + * (honestly not sure what to do -- they are unbound for good, I think) + */ + if (_contextClass != null) { + if (ClassUtil.getEnclosingClass(_contextClass) != null) { + // [JACKSON-572]: Actually, let's skip this for all non-static inner classes + // (which will also cover 'java.util' type cases... + if (!Modifier.isStatic(_contextClass.getModifiers())) { + return UNBOUND; + } + } + } + + if (!mustFind) { + return null; + } + + String className; + if (_contextClass != null) { + className = _contextClass.getName(); + } else if (_contextType != null) { + className = _contextType.toString(); + } else { + className = "UNKNOWN"; + } + throw new IllegalArgumentException("Type variable '"+name + +"' can not be resolved (with context of class "+className+")"); + //t = UNBOUND; + } + + public void addBinding(String name, JavaType type) + { + // note: emptyMap() is unmodifiable, hence second check is needed: + if (_bindings == null || _bindings.size() == 0) { + _bindings = new LinkedHashMap(); + } + _bindings.put(name, type); + } + + public JavaType[] typesAsArray() + { + if (_bindings == null) { + _resolve(); + } + if (_bindings.size() == 0) { + return NO_TYPES; + } + return _bindings.values().toArray(new JavaType[_bindings.size()]); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + // Only for tests! + protected int getBindingCount() { + if (_bindings == null) { + _resolve(); + } + return _bindings.size(); + } + + protected void _resolve() + { + _resolveBindings(_contextClass); + + // finally: may have root level type info too + if (_contextType != null) { + int count = _contextType.containedTypeCount(); + if (count > 0) { + for (int i = 0; i < count; ++i) { + String name = _contextType.containedTypeName(i); + JavaType type = _contextType.containedType(i); + addBinding(name, type); + } + } + } + + // nothing bound? mark with empty map to prevent further calls + if (_bindings == null) { + _bindings = Collections.emptyMap(); + } + } + + public void _addPlaceholder(String name) { + if (_placeholders == null) { + _placeholders = new HashSet(); + } + _placeholders.add(name); + } + + protected void _resolveBindings(Type t) + { + if (t == null) return; + + Class raw; + if (t instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) t; + Type[] args = pt.getActualTypeArguments(); + if (args != null && args.length > 0) { + Class rawType = (Class) pt.getRawType(); + TypeVariable[] vars = rawType.getTypeParameters(); + if (vars.length != args.length) { + throw new IllegalArgumentException("Strange parametrized type (in class "+rawType.getName()+"): number of type arguments != number of type parameters ("+args.length+" vs "+vars.length+")"); + } + for (int i = 0, len = args.length; i < len; ++i) { + TypeVariable var = vars[i]; + String name = var.getName(); + if (_bindings == null) { + _bindings = new LinkedHashMap(); + } else { + // 24-Mar-2010, tatu: Better ensure that we do not overwrite something + // collected earlier (since we descend towards super-classes): + if (_bindings.containsKey(name)) continue; + } + // first: add a placeholder to prevent infinite loops + _addPlaceholder(name); + // then resolve type + _bindings.put(name, _typeFactory._constructType(_classStack, args[i], this)); + } + } + raw = (Class)pt.getRawType(); + } else if (t instanceof Class) { + raw = (Class) t; + /* [JACKSON-677]: If this is an inner class then the generics are defined on the + * enclosing class so we have to check there as well. We don't + * need to call getEnclosingClass since anonymous classes declare + * generics + */ + Class decl = ClassUtil.getDeclaringClass(raw); + /* 08-Feb-2013, tatu: Except that if context is also super-class, we must + * skip it; context will be checked anyway, and we'd get StackOverflow if + * we went there. + */ + if (decl != null && !decl.isAssignableFrom(raw)) { + _resolveBindings(decl); + } + + /* 24-Mar-2010, tatu: Can not have true generics definitions, but can + * have lower bounds ("") in declaration itself + */ + TypeVariable[] vars = raw.getTypeParameters(); + if (vars != null && vars.length > 0) { + JavaType[] typeParams = null; + + if (_contextType != null && raw.isAssignableFrom(_contextType.getRawClass())) { + typeParams = _typeFactory.findTypeParameters(_contextType, raw); + } + + for (int i = 0; i < vars.length; i++) { + TypeVariable var = vars[i]; + + String name = var.getName(); + Type varType = var.getBounds()[0]; + if (varType != null) { + if (_bindings == null) { + _bindings = new LinkedHashMap(); + } else { // and no overwriting... + if (_bindings.containsKey(name)) continue; + } + _addPlaceholder(name); // to prevent infinite loops + + if (typeParams != null && typeParams.length > i) { + _bindings.put(name, typeParams[i]); + } else { + _bindings.put(name, _typeFactory._constructType(_classStack, varType, this)); + } + } + } + } + } else { // probably can't be any of these... so let's skip for now + //if (type instanceof GenericArrayType) { + //if (type instanceof TypeVariable) { + // if (type instanceof WildcardType) { + return; + } + // but even if it's not a parameterized type, its super types may be: + _resolveBindings(ClassUtil.getGenericSuperclass(raw)); + for (Type intType : raw.getGenericInterfaces()) { + _resolveBindings(intType); + } + } + + @Override + public String toString() + { + if (_bindings == null) { + _resolve(); + } + StringBuilder sb = new StringBuilder("[TypeBindings for "); + if (_contextType != null) { + sb.append(_contextType.toString()); + } else { + sb.append(_contextClass.getName()); + } + sb.append(": ").append(_bindings).append("]"); + return sb.toString(); + } +} diff --git a/pom.xml b/pom.xml index 3728c5cd96..dade30be71 100644 --- a/pom.xml +++ b/pom.xml @@ -88,17 +88,10 @@ ${jackson.version.core} - - - org.junit.vintage - junit-vintage-engine - test - - - org.junit.jupiter - junit-jupiter - test - + + org.powermock powermock-core @@ -151,18 +144,6 @@ - - - - org.junit - junit-bom - 5.9.2 - pom - import - - - - diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index c7e9be5ba3..73f5c66d1a 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -6,8 +6,6 @@ databind core component, version 2.x Tatu Saloranta, tatu.saloranta@iki.fi: author ----------------------------------------------------------------------------- - Pascal GŽlinas: * Contributed fixes to 'MappingIterator' handling (Pull#58 and Pull#59) (2.1.0) @@ -1497,7 +1495,6 @@ PJ Fanning (pjfanning@github) (2.14.0) * Contributed #3837: Set transformer factory attributes to improve protection against XXE (2.14.3) - * ... and countless more Igor Shymko (ancane@github) * Contributed #3500: Add optional explicit `JsonSubTypes` repeated names check @@ -1598,9 +1595,6 @@ Sim Yih Tsern (yihtsern@github) (2.15.0) * Contributed fix for #3894: Only avoid Records fields detection for deserialization (2.15.1) - * Contributed fix for #3897: 2.15.0 breaks deserialization when POJO/Record only has a - single field and is marked `Access.WRITE_ONLY` - (2.15.1) Ajay Siwach (Siwach16@github) * Contributed #3637: Add enum features into `@JsonFormat.Feature` @@ -1630,13 +1624,3 @@ Steve Storey (stevestorey@github) strict subtype Type Id handling (2.15.0) -Matteo Bertozzi (matteobertozzi@github) - * Reported #3895: 2.15.0 breaking behaviour change for records and Getter Visibility - (2.15.1) - -Antti Lampinen (arlampin@github) - * Reported #3897: 2.15.0 breaks deserialization when POJO/Record only has a single field - and is marked `Access.WRITE_ONLY` - (2.15.1) - - diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 3a80213e88..9855754393 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -6,25 +6,12 @@ Project: jackson-databind 2.16.0 (not yet released) -#3928: `@JsonProperty` on constructor parameter changes default field serialization order - (contributed by @pjfanning) - -2.15.2 (not yet released) +No changes since 2.15 -#3938: Record setter not included from interface (2.15 regression) +2.15.1 (not yet released) -2.15.1 (16-May-2023) - -#3882: Error in creating nested `ArrayNode`s with `JsonNode.withArray()` - (reported by @SaiKrishna369) #3894: Only avoid Records fields detection for deserialization (contributed by Sim Y-T) -#3895: 2.15.0 breaking behaviour change for records and Getter Visibility - (reported by Matteo B) -#3897: 2.15.0 breaks deserialization when POJO/Record only has a single field - and is marked `Access.WRITE_ONLY` - (reported by Antti L) - (fix contributed by Sim Y-T) #3913: Issue with deserialization when there are unexpected properties (due to null `StreamReadConstraints`) (reported by @sbertault) @@ -112,7 +99,7 @@ Project: jackson-databind #3876: `TypeFactory` cache performance degradation with `constructSpecializedType()` (contributed by Carter K) -2.14.3 (05-May-2023) +2.14.3 (not yet released) #3784: `PrimitiveArrayDeserializers$ByteDeser.deserialize` ignores `DeserializationProblemHandler` for invalid Base64 content diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java index 53d5bc9838..2905a9beac 100644 --- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java @@ -59,7 +59,7 @@ public enum Type { * Usually this can be defined by using * {@link com.fasterxml.jackson.annotation.JsonManagedReference} */ - MANAGED_REFERENCE, + MANAGED_REFERENCE /** * Reference property that Jackson manages by suppressing it during serialization, @@ -67,7 +67,7 @@ public enum Type { * Usually this can be defined by using * {@link com.fasterxml.jackson.annotation.JsonBackReference} */ - BACK_REFERENCE + ,BACK_REFERENCE ; } diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java index dc256d13b3..9170f68049 100644 --- a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java +++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java @@ -40,7 +40,7 @@ protected BeanDescription(JavaType type) { /* /********************************************************** - /* Simple accessors + /* Simple accesors /********************************************************** */ diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java index 830c5f6742..2ecf7ee450 100644 --- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java @@ -40,7 +40,7 @@ public enum MapperFeature implements ConfigFeature * and Map to modify the property, without requiring a setter * method. * This is similar to how JAXB framework sets Collections and - * Maps: no setter is involved, just getter. + * Maps: no setter is involved, just setter. *

* Note that such getters-as-setters methods have lower * precedence than setters, so they are only used if no @@ -322,7 +322,7 @@ public enum MapperFeature implements ConfigFeature * Feature is enabled by default which means that deserialization does * support deserializing types via builders with type parameters (generic types). *

- * See: databind#921 + * See: https://github.com/FasterXML/jackson-databind/issues/921 * * @since 2.12 */ @@ -401,11 +401,6 @@ public enum MapperFeature implements ConfigFeature *

* Note: does not apply to {@link java.util.Map} serialization (since * entries are not considered Bean/POJO properties. - *

- * WARNING: Disabling it may have a negative impact on deserialization performance. - * When disabled, all properties before the last creator property in the input need to be buffered, - * since all creator properties are required to create the instance. - * Enabling this feature ensures that there is as little buffering as possible. *

* Feature is enabled by default. * diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 0ce19c8689..7c92f52dab 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -4623,7 +4623,7 @@ public T updateValue(T valueToUpdate, Object overrides) * @param t The class to generate schema for * @return Constructed JSON schema. * - * @deprecated Since 2.6 use external JSON Schema generator (jackson-module-jsonSchema) + * @deprecated Since 2.6 use external JSON Schema generator (https://github.com/FasterXML/jackson-module-jsonSchema) * (which under the hood calls {@link #acceptJsonFormatVisitor(JavaType, JsonFormatVisitorWrapper)}) */ @Deprecated diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java index 7ad51f1b8c..c48639927f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java @@ -947,15 +947,19 @@ public JsonSerializer getDefaultNullValueSerializer() { } /** - * Method called to find a serializer to serializes Map keys that are nulls, - * as JSON does not allow any non-String value as a key, including null. - * Note that type is completely based on declared type, + * Method called to get the serializer to use for serializing + * Map keys that are nulls: this is needed since JSON does not allow + * any non-String value as key, including null. + *

+ * Typically, returned serializer + * will either throw an exception, or use an empty String; but + * other behaviors are possible. + */ + /** + * Method called to find a serializer to use for null values for given + * declared type. Note that type is completely based on declared type, * since nulls in Java have no type and thus runtime type cannot be * determined. - * - * @return JsonSerializer that handles the serialization of null keys, - * usually by throwing an exception or using an empty String, - * but other behaviors are also possible. * * @since 2.0 */ diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java index e77e76e714..122f6a7d08 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonAppend.java @@ -9,12 +9,10 @@ import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; /** - * Annotation used to add "virtual" properties that will be written - * after regular properties during serialization. - *

- * Please note that the "virtual" properties added using this annotation - * do not obey any specific order, including the order defined - * by {@link com.fasterxml.jackson.annotation.JsonPropertyOrder}. + * Annotation that may be used to add "virtual" properties to be written + * after regular properties (although ordering may be changed using + * both standard @JsonPropertyOrder annotation, and + * properties of this annotation). * * @since 2.5 */ 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 88b0da75ae..adbfcdc012 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -1031,10 +1031,6 @@ private boolean _checkIfCreatorPropertyBased(BeanDescription beanDesc, return true; } } - // [databind#3897]: Record canonical constructor will have implicitly named propDef - if (!propDef.isExplicitlyNamed() && beanDesc.isRecordType()) { - return true; - } } // in absence of everything else, default to delegating return false; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index fa21406337..16a219aac3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -474,9 +474,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri // weren't removed (to help in creating constructor-backed PropertyCreator) // so they ended up in _beanProperties, unlike POJO (whose ignored // props are removed) - if ((prop != null) && - // [databind#3938]: except if it's MethodProperty - (!_beanType.isRecordType() || (prop instanceof MethodProperty))) { + if ((prop != null) && !_beanType.isRecordType()) { try { buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); } catch (UnresolvedForwardReference reference) { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java index 76c07a661f..15175767d4 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java @@ -529,8 +529,8 @@ public void resolveForwardReference(Object id, Object value) throws IOException /** * Helper class to maintain processing order of value. The resolved - * object associated with {@code #id} parameter from {@link #handleResolvedForwardReference(Object, Object)} - * comes before the values in {@link #next}. + * object associated with {@link #_id} comes before the values in + * {@link #next}. */ private final static class CollectionReferring extends Referring { private final CollectionReferringAccumulator _parent; diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java index c184c80701..24e850315b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -202,7 +202,8 @@ public static AnnotatedClass construct(JavaType type, MapperConfig config, * Method similar to {@link #construct}, but that will NOT include * information from supertypes; only class itself and any direct * mix-ins it may have. - * + */ + /** * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead. */ @Deprecated diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java index 40df20e693..fabeb15cbb 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -91,7 +91,7 @@ public class POJOPropertiesCollector /** * A set of "field renamings" that have been discovered, indicating - * intended renaming of other accessors: key is the implicit original + * intended renaming of other accesors: key is the implicit original * name and value intended name to use instead. *

* Note that these renamings are applied earlier than "regular" (explicit) @@ -437,18 +437,17 @@ protected void collectAll() LinkedHashMap props = new LinkedHashMap(); // First: gather basic data - final boolean isRecord = isRecordType(); + // 15-Jan-2023, tatu: [databind#3736] Let's avoid detecting fields of Records // altogether (unless we find a good reason to detect them) // 17-Apr-2023: Need Records' fields for serialization for cases like [databind#3895] & [databind#3628] - if (!isRecord || _forSerialization) { + if (!isRecordType() || _forSerialization) { _addFields(props); // note: populates _fieldRenameMappings } _addMethods(props); // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static // inner classes, see [databind#1502] - // 13-May-2023, PJ: Need to avoid adding creators for Records when serializing [databind#3925] - if (!_classDef.isNonStaticInnerClass() && !(_forSerialization && isRecord)) { + if (!_classDef.isNonStaticInnerClass()) { _addCreators(props); } @@ -762,7 +761,7 @@ protected void _addMethods(Map props) _addGetterMethod(props, m, _annotationIntrospector); } else if (argCount == 1) { // setters _addSetterMethod(props, m, _annotationIntrospector); - } else if (argCount == 2) { // any setter? + } else if (argCount == 2) { // any getter? if (Boolean.TRUE.equals(_annotationIntrospector.hasAnySetter(m))) { if (_anySetters == null) { _anySetters = new LinkedList(); diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java index 06e2800e68..0f1ac43b7e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java @@ -188,9 +188,9 @@ protected ArrayNode _withArrayAddTailElement(JsonPointer tail, boolean preferInd _withXxxSetArrayElement(index, next); return next._withArrayAddTailElement(tail, preferIndex); } - ObjectNode next = this.objectNode(); + ArrayNode next = this.arrayNode(); _withXxxSetArrayElement(index, next); - return next._withArrayAddTailProperty(tail, preferIndex); + return next._withArrayAddTailElement(tail, preferIndex); } protected void _withXxxSetArrayElement(int index, JsonNode value) { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java index 2c9f0ac6f9..bca1f7d41e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java @@ -233,7 +233,7 @@ protected Map _createObjectIdMap() /* /********************************************************** - /* Extended API: simple accessors + /* Extended API: simple accesors /********************************************************** */ diff --git a/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java b/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java index d74b89aa0b..4cd867afdd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/internal/PrivateMaxEntriesMap.java @@ -136,7 +136,7 @@ public final class PrivateMaxEntriesMap extends AbstractMap /** * The number of read buffers to use. - * The max of 4 was introduced due to databind#3665. + * The max of 4 was introduced due to https://github.com/FasterXML/jackson-databind/issues/3665. */ static final int NUMBER_OF_READ_BUFFERS = Math.min(4, ceilingNextPowerOfTwo(NCPU)); @@ -145,7 +145,7 @@ public final class PrivateMaxEntriesMap extends AbstractMap /** * The number of pending read operations before attempting to drain. - * The threshold of 4 was introduced due to databind#3665. + * The threshold of 4 was introduced due to https://github.com/FasterXML/jackson-databind/issues/3665. */ static final int READ_BUFFER_THRESHOLD = 4; diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java index c2dfb3261d..f94231b526 100644 --- a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java +++ b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordBasicsTest.java @@ -40,20 +40,6 @@ record SnakeRecord(String myId, String myValue){} record RecordWithJsonDeserialize(int id, @JsonDeserialize(converter = StringTrimmer.class) String name) { } - record RecordSingleWriteOnly(@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) int id) { } - - record RecordSomeWriteOnly( - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) int id, - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String name, - String email) { - } - - record RecordAllWriteOnly( - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) int id, - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String name, - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String email) { - } - private final ObjectMapper MAPPER = newJsonMapper(); /* @@ -218,53 +204,6 @@ public void testDeserializeJsonDeserializeRecord() throws Exception { assertEquals(new RecordWithJsonDeserialize(123, "Bob"), value); } - /* - /********************************************************************** - /* Test methods, JsonProperty(access=WRITE_ONLY) - /********************************************************************** - */ - - public void testSerialize_SingleWriteOnlyParameter() throws Exception { - String json = MAPPER.writeValueAsString(new RecordSingleWriteOnly(123)); - - assertEquals("{}", json); - } - - // [databind#3897] - public void testDeserialize_SingleWriteOnlyParameter() throws Exception { - RecordSingleWriteOnly value = MAPPER.readValue("{\"id\":123}", RecordSingleWriteOnly.class); - - assertEquals(new RecordSingleWriteOnly(123), value); - } - - public void testSerialize_SomeWriteOnlyParameter() throws Exception { - String json = MAPPER.writeValueAsString(new RecordSomeWriteOnly(123, "Bob", "bob@example.com")); - - assertEquals("{\"email\":\"bob@example.com\"}", json); - } - - public void testDeserialize_SomeWriteOnlyParameter() throws Exception { - RecordSomeWriteOnly value = MAPPER.readValue( - "{\"id\":123,\"name\":\"Bob\",\"email\":\"bob@example.com\"}", - RecordSomeWriteOnly.class); - - assertEquals(new RecordSomeWriteOnly(123, "Bob", "bob@example.com"), value); - } - - public void testSerialize_AllWriteOnlyParameter() throws Exception { - String json = MAPPER.writeValueAsString(new RecordAllWriteOnly(123, "Bob", "bob@example.com")); - - assertEquals("{}", json); - } - - public void testDeserialize_AllWriteOnlyParameter() throws Exception { - RecordAllWriteOnly value = MAPPER.readValue( - "{\"id\":123,\"name\":\"Bob\",\"email\":\"bob@example.com\"}", - RecordAllWriteOnly.class); - - assertEquals(new RecordAllWriteOnly(123, "Bob", "bob@example.com"), value); - } - /* /********************************************************************** /* Internal helper methods diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java deleted file mode 100644 index 3fe8cfef18..0000000000 --- a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.fasterxml.jackson.databind.records; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.*; - -public class RecordFailingSetter3938Test extends BaseMapTest -{ - private final static String ERROR_3938_PREFIX = "Non-null 'options' not allowed for "; - - // [databind#3938] - interface NoOptionsCommand { - @JsonProperty("options") - default void setOptions(JsonNode value) { - if (value.isNull()) { - return; - } - throw new IllegalArgumentException(ERROR_3938_PREFIX+getClass().getName()); - } - } - - public record Command3938(int id, String filter) implements NoOptionsCommand { } - - private final ObjectMapper MAPPER = newJsonMapper(); - - // [databind#3938]: Should detect and use setters too - public void testFailingSetter3939() throws Exception - { - final ObjectReader R = MAPPER.readerFor(Command3938.class); - - // First, missing value and `null` are fine, as long as we have all fields - assertNotNull(R.readValue(a2q("{'id':1, 'filter':'abc'}"))); - assertNotNull(R.readValue(a2q("{'id':2, 'filter':'abc', 'options':null}"))); - - // But then failure for non-empty Array (f.ex) - try { - R.readValue(a2q("{'id':2,'options':[123]}}")); - fail("Should not pass"); - } catch (DatabindException e) { - verifyException(e, ERROR_3938_PREFIX); - } - } -} diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java deleted file mode 100644 index 494904c3e9..0000000000 --- a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.fasterxml.jackson.databind.records; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.databind.BaseMapTest; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class RecordSerializationOrderTest extends BaseMapTest -{ - record NestedRecordOne(String id, String email, NestedRecordTwo nestedRecordTwo) {} - record NestedRecordOneWithJsonProperty(String id, String email, - @JsonProperty("nestedProperty") NestedRecordTwo nestedRecordTwo) {} - record NestedRecordOneWithJsonPropertyIndex(@JsonProperty(index = 2) String id, - @JsonProperty(index = 0) String email, - @JsonProperty(value = "nestedProperty", index = 1) NestedRecordTwo nestedRecordTwo) {} - - @JsonPropertyOrder({"email", "nestedProperty", "id"}) - record NestedRecordOneWithJsonPropertyOrder(String id, - String email, - @JsonProperty(value = "nestedProperty") NestedRecordTwo nestedRecordTwo) {} - - record NestedRecordTwo(String id, String passport) {} - - private final ObjectMapper MAPPER = newJsonMapper(); - - /* - /********************************************************************** - /* Test methods, alternate constructors - /********************************************************************** - */ - - public void testSerializationOrder() throws Exception { - NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); - NestedRecordOne nestedRecordOne = new NestedRecordOne("1", "test@records.com", nestedRecordTwo); - final String output = MAPPER.writeValueAsString(nestedRecordOne); - final String expected = "{\"id\":\"1\",\"email\":\"test@records.com\",\"nestedRecordTwo\":{\"id\":\"2\",\"passport\":\"111110\"}}"; - assertEquals(expected, output); - } - - public void testSerializationOrderWithJsonProperty() throws Exception { - NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); - NestedRecordOneWithJsonProperty nestedRecordOne = - new NestedRecordOneWithJsonProperty("1", "test@records.com", nestedRecordTwo); - final String output = MAPPER.writeValueAsString(nestedRecordOne); - final String expected = "{\"id\":\"1\",\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"}}"; - assertEquals(expected, output); - } - - public void testSerializationOrderWithJsonPropertyIndexes() throws Exception { - NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); - NestedRecordOneWithJsonPropertyIndex nestedRecordOne = - new NestedRecordOneWithJsonPropertyIndex("1", "test@records.com", nestedRecordTwo); - final String output = MAPPER.writeValueAsString(nestedRecordOne); - final String expected = "{\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"},\"id\":\"1\"}"; - assertEquals(expected, output); - } - - public void testSerializationOrderWithJsonPropertyOrder() throws Exception { - NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); - NestedRecordOneWithJsonPropertyOrder nestedRecordOne = - new NestedRecordOneWithJsonPropertyOrder("1", "test@records.com", nestedRecordTwo); - final String output = MAPPER.writeValueAsString(nestedRecordOne); - final String expected = "{\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"},\"id\":\"1\"}"; - assertEquals(expected, output); - } -} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java index adbf3e051d..f846453282 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepJsonNodeDeser3397Test.java @@ -13,7 +13,7 @@ public class DeepJsonNodeDeser3397Test extends BaseMapTest // ... currently gets a bit slow at 1M but passes. // But test with 100k as practical limit, to guard against regression // private final static int TOO_DEEP_NESTING = 1_000_000; - private final static int TOO_DEEP_NESTING = StreamReadConstraints.DEFAULT_MAX_DEPTH * 10; + private final static int TOO_DEEP_NESTING = 10_000; private final JsonFactory jsonFactory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build()) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java index 6389da8841..963040b9af 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepNestingUntypedDeserTest.java @@ -18,7 +18,7 @@ public class DeepNestingUntypedDeserTest extends BaseMapTest // 31-May-2022, tatu: But no more! Can handle much much larger // nesting levels, bounded by memory usage not stack. Tested with // 1 million (!) nesting levels, but to keep tests fast use 100k - private final static int TOO_DEEP_NESTING = StreamReadConstraints.DEFAULT_MAX_DEPTH * 100; + private final static int TOO_DEEP_NESTING = 100_000; private final JsonFactory jsonFactory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build()) diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java index 15c29eee2b..d7c26882b4 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/StreamReadStringConstraintsTest.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper; /** - * Tests for databind#863" + * Tests for {@a href="https://github.com/FasterXML/jackson-core/issues/863"}. */ public class StreamReadStringConstraintsTest extends BaseMapTest { @@ -32,8 +32,6 @@ void setString(String string) { /********************************************************************** */ - private final static int TOO_LONG_STRING_VALUE = StreamReadConstraints.DEFAULT_MAX_STRING_LEN + 100; - private final ObjectMapper MAPPER = newJsonMapper(); private ObjectMapper newJsonMapperWithUnlimitedStringSizeSupport() { @@ -46,32 +44,31 @@ private ObjectMapper newJsonMapperWithUnlimitedStringSizeSupport() { public void testBigString() throws Exception { try { - MAPPER.readValue(generateJson("string", TOO_LONG_STRING_VALUE), StringWrapper.class); - fail("expected DatabindException"); + MAPPER.readValue(generateJson("string", 5001000), StringWrapper.class); + fail("expected JsonMappingException"); } catch (DatabindException e) { - final String message = e.getMessage(); - assertTrue("unexpected exception message: " + message, message.startsWith("String value length")); - assertTrue("unexpected exception message: " + message, message.contains("exceeds the maximum allowed (")); + assertTrue("unexpected exception message: " + e.getMessage(), + e.getMessage().startsWith("String value length (5001000) exceeds the maximum allowed (5000000")); } } public void testBiggerString() throws Exception { try { - MAPPER.readValue(generateJson("string", TOO_LONG_STRING_VALUE), StringWrapper.class); + MAPPER.readValue(generateJson("string", 6_000_000), StringWrapper.class); fail("expected JsonMappingException"); } catch (DatabindException e) { final String message = e.getMessage(); // this test fails when the TextBuffer is being resized, so we don't yet know just how big the string is // so best not to assert that the String length value in the message is the full 6000000 value assertTrue("unexpected exception message: " + message, message.startsWith("String value length")); - assertTrue("unexpected exception message: " + message, message.contains("exceeds the maximum allowed (")); + assertTrue("unexpected exception message: " + message, message.contains("exceeds the maximum allowed (5000000")); } } public void testUnlimitedString() throws Exception { - final int len = TOO_LONG_STRING_VALUE; + final int len = 5_001_000; StringWrapper sw = newJsonMapperWithUnlimitedStringSizeSupport() .readValue(generateJson("string", len), StringWrapper.class); assertEquals(len, sw.string.length()); diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java index 063e4bf594..d6b1bcc557 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java @@ -8,7 +8,7 @@ import java.util.*; /** - * Reproduction of databind#676 + * Reproduction of [https://github.com/FasterXML/jackson-databind/issues/676] *

* Deserialization of class with generic collection inside * depends on how is was deserialized first time. diff --git a/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java b/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java index 73cbf1e1e2..f3e86cc986 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/WithPathTest.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.JsonNode.OverwriteMode; -// for [databind#1980] implementation +// for [databuind#1980] implementation public class WithPathTest extends BaseMapTest { private final ObjectMapper MAPPER = sharedMapper(); @@ -296,23 +296,4 @@ private void _verifyArrayReplaceFail(JsonNode doc, JsonPointer ptr, OverwriteMod verifyException(e, "(mode `OverwriteMode."+mode.name()+"`)"); } } - - // [databind#3882] - public void testWithArray3882() throws Exception - { - ObjectNode root = MAPPER.createObjectNode(); - ArrayNode aN = root.withArray("/key/0/a", - JsonNode.OverwriteMode.ALL, true); - aN.add(123); - assertEquals(a2q("{'key':[{'a':[123]}]}"), - root.toString()); - - // And then the original case - root = MAPPER.createObjectNode(); - aN = root.withArray(JsonPointer.compile("/key1/array1/0/element1"), - JsonNode.OverwriteMode.ALL, true); - aN.add("v1"); - assertEquals(a2q("{'key1':{'array1':[{'element1':['v1']}]}}"), - root.toString()); - } }