From 14be34534f19690346ed4e80720671ff86e32d2c Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Thu, 2 May 2024 13:03:17 -0700 Subject: [PATCH 01/87] Backport #3275 in 2.12 (proposing micro-release 2.12.7.2) (#4509) --- release-notes/CREDITS-2.x | 5 +++++ release-notes/VERSION-2.x | 6 ++++++ .../deser/BeanDeserializerFactory.java | 18 ++++++++++++++---- .../exc/ExceptionDeserializationTest.java | 2 +- .../exc/ExceptionSerializationTest.java | 12 ++++++++++++ 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 519aac951b..1cfab6f289 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1324,3 +1324,8 @@ Jelle Voost (jellevoost@github) JoeWoo (xJoeWoo@github) * Reported #3139: Deserialization of "empty" subtype with DEDUCTION failed (2.12.4) + +Jason Harper (jsharper@github) + * Reported #3275: JDK 16 Illegal reflective access for `Throwable.setCause()` with + `PropertyNamingStrategy.UPPER_CAMEL_CASE` + (2.12.7.2) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 9ff9b59ba6..ce07dfdc0f 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,6 +4,12 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ +2.12.7.2 (not yet released) + +#3275: JDK 16 Illegal reflective access for `Throwable.setCause()` with + `PropertyNamingStrategy.UPPER_CAMEL_CASE` + (reported by Jason H) + 2.12.7.1 (12-Oct-2022) #3582: Add check in `BeanDeserializer._deserializeFromArray()` to prevent diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java index adf460e88d..9465d3df09 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java @@ -425,6 +425,17 @@ public JsonDeserializer buildThrowableDeserializer(DeserializationContex // But then let's decorate things a bit // Need to add "initCause" as setter for exceptions (sub-classes of Throwable). + // 26-May-2022, tatu: [databind#3275] Looks like JDK 12 added "setCause()" + // which can wreak havoc, at least with NamingStrategy + Iterator it = builder.getProperties(); + while (it.hasNext()) { + SettableBeanProperty prop = it.next(); + if ("setCause".equals(prop.getMember().getName())) { + // For now this is allowed as we are returned "live" Iterator... + it.remove(); + break; + } + } AnnotatedMethod am = beanDesc.findMethod("initCause", INIT_CAUSE_PARAMS); if (am != null) { // should never be null SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am, @@ -454,10 +465,9 @@ public JsonDeserializer buildThrowableDeserializer(DeserializationContex } } JsonDeserializer deserializer = builder.build(); - - /* At this point it ought to be a BeanDeserializer; if not, must assume - * it's some other thing that can handle deserialization ok... - */ + + // At this point it ought to be a BeanDeserializer; if not, must assume + // it's some other thing that can handle deserialization ok... if (deserializer instanceof BeanDeserializer) { deserializer = new ThrowableDeserializer((BeanDeserializer) deserializer); } diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java index 6630f3fd70..c1d45f80ad 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java @@ -172,7 +172,7 @@ public void testLineNumberAsString() throws IOException assertNotNull(exc); } - // [databind#1842]: + // [databind#1842] public void testNullAsMessage() throws IOException { Exception exc = MAPPER.readValue(aposToQuotes( diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java index bce1f8a64f..64b22a5671 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.json.JsonMapper; /** * Unit tests for verifying that simple exceptions can be serialized. @@ -132,4 +133,15 @@ public void testJsonMappingExceptionSerialization() throws IOException { fail("Exception should contain '"+MATCH+"', does not: '"+msg+"'"); } } + + // [databind#3275] + public void testSerializeWithNamingStrategy() throws IOException { + final ObjectMapper mapper = JsonMapper.builder() + .propertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE) + .build(); + String json = mapper.writeValueAsString(new Exception("message!")); + Map map = mapper.readValue(json, Map.class); + assertEquals(new HashSet<>(Arrays.asList("Cause", "StackTrace", "Message", "Suppressed", "LocalizedMessage")), + map.keySet()); + } } From b86db5203956f6386202b546140eaad8b8783bfc Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 2 May 2024 13:08:00 -0700 Subject: [PATCH 02/87] Prepare for 2.12.7.2 release --- release-notes/VERSION-2.x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index ce07dfdc0f..5ef5db9941 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,7 +4,7 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ -2.12.7.2 (not yet released) +2.12.7.2 (02-May-2024) #3275: JDK 16 Illegal reflective access for `Throwable.setCause()` with `PropertyNamingStrategy.UPPER_CAMEL_CASE` From 2e1b6f217aa8da629ff24509060b1108ec776198 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 2 May 2024 13:09:02 -0700 Subject: [PATCH 03/87] [maven-release-plugin] prepare release jackson-databind-2.12.7.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 584249fa4c..aff3c131cf 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.fasterxml.jackson.core jackson-databind - 2.12.7.2-SNAPSHOT + 2.12.7.2 jackson-databind bundle General data-binding functionality for Jackson: works on core streaming API @@ -33,7 +33,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git http://github.com/FasterXML/jackson-databind - HEAD + jackson-databind-2.12.7.2 From f421d1a46aa69c573f8fb9789e8255b313fc8979 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 2 May 2024 13:09:10 -0700 Subject: [PATCH 04/87] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aff3c131cf..79c9f65231 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.fasterxml.jackson.core jackson-databind - 2.12.7.2 + 2.12.7.3-SNAPSHOT jackson-databind bundle General data-binding functionality for Jackson: works on core streaming API @@ -33,7 +33,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git http://github.com/FasterXML/jackson-databind - jackson-databind-2.12.7.2 + HEAD From 6fb591c2d34ea2acb395ce7c2d7ef790dd6052e9 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Fri, 4 Apr 2025 16:00:32 +0530 Subject: [PATCH 05/87] Automatically detect class to deserialize #5064 --- .../jackson/databind/ObjectMapper.java | 28 +++++++++++++++++++ .../jackson/databind/ObjectMapperTest.java | 10 +++++++ 2 files changed, 38 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 140872e85f..4ce22efe6a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -3855,6 +3855,34 @@ public T readValue(String content, Class valueType) return readValue(content, _typeFactory.constructType(valueType)); } + /** + * Utility method to get the class type from a varargs array. + * + * @param the generic type + * @param array the varargs array + * @return the class of the array component + */ + private static Class getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + + /** + * Reads the value from the given JSON content and automatically detects the class type. + * + * @param the type of the object to read + * @param content the JSON string + * @param reified don't pass any values here. It's a trick to detect the class type. + * @return the deserialized object + * @throws JsonProcessingException if there is a problem processing the JSON + */ + public T readValue(String content, T... reified) throws JsonProcessingException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(content, _typeFactory.constructType(getClassOf(reified))); + } + /** * Method to deserialize JSON content from given JSON content String. * diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java index a249682b76..ef5198e84a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java @@ -385,6 +385,16 @@ public void testJsonFactoryLinkage() assertSame(m, f.getCodec()); } + @Test + public void testAutoDetectClasses() throws Exception + { + ObjectMapper m = new ObjectMapper(); + final String JSON = "{ \"x\" : 3 }"; + + Bean bean = m.readValue(JSON); + assertNotNull(bean); + } + @Test public void testProviderConfig() throws Exception { From d885656616a5dda4e8fa981b42c982e8db6210db Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sun, 6 Apr 2025 09:04:57 +0530 Subject: [PATCH 06/87] Automatically detect class to deserialize #5064 --- .../jackson/databind/ObjectMapper.java | 26 +++++++++++ .../databind/BoundsChecksForInputTest.java | 3 ++ .../databind/deser/JacksonTypesDeserTest.java | 44 +++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 4ce22efe6a..16a6dec995 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -3055,6 +3055,23 @@ public T readValue(JsonParser p, Class valueType) return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueType)); } + /** + * Reads the value from the given JSON content and automatically detects the class type. + * + * @param the type of the object to read + * @param p the JsonParser + * @param reified don't pass any values here. It's a trick to detect the class type. + * @return the deserialized object + * @throws JsonProcessingException if there is a problem processing the JSON + */ + public T readValue(JsonParser p, T... reified) throws IOException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(p, _typeFactory.constructType(getClassOf(reified))); + } + /** * Method to deserialize JSON content into a Java type, reference * to which is passed as argument. Type is passed using so-called @@ -3985,6 +4002,15 @@ public T readValue(byte[] src, int offset, int len, return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType)); } + @SuppressWarnings("unchecked") + public T readValue(byte[] src, int offset, int len, T... reified) throws IOException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(src, offset, len, _typeFactory.constructType(getClassOf(reified))); + } + @SuppressWarnings({ "unchecked" }) public T readValue(byte[] src, TypeReference valueTypeRef) throws IOException, StreamReadException, DatabindException diff --git a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java index 493fd4c482..89a0f11d85 100644 --- a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java @@ -45,6 +45,9 @@ public void testBoundsWithByteArrayInput() throws Exception { final JavaType TYPE = MAPPER.constructType(String.class); _testBoundsWithByteArrayInput( (data,offset,len)->MAPPER.readValue(data, offset, len, TYPE)); + + _testBoundsWithByteArrayInput( + (data,offset,len)->MAPPER.readValue(data, offset, len)); } private void _testBoundsWithByteArrayInput(ByteBackedCreation creator) throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java index 922cd34a04..a55440f1d9 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java @@ -82,6 +82,50 @@ public void testTokenBufferWithSample() throws Exception } } + @Test + public void testTokenBufferWithSequenceWithAutoDetectClass() throws Exception + { + final ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .build(); + + // and then sequence of other things + JsonParser p = mapper.createParser("[ 32, [ 1 ], \"abc\", { \"a\" : true } ]"); + assertToken(JsonToken.START_ARRAY, p.nextToken()); + + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + TokenBuffer buf = mapper.readValue(p); + + // check manually... + JsonParser bufParser = buf.asParser(); + assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken()); + assertEquals(32, bufParser.getIntValue()); + assertNull(bufParser.nextToken()); + + // then bind to another + buf = mapper.readValue(p); + bufParser = buf.asParser(); + assertToken(JsonToken.START_ARRAY, bufParser.nextToken()); + assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken()); + assertEquals(1, bufParser.getIntValue()); + assertToken(JsonToken.END_ARRAY, bufParser.nextToken()); + assertNull(bufParser.nextToken()); + + // third one, with automatic binding + buf = mapper.readValue(p); + String str = mapper.readValue(buf.asParser()); + assertEquals("abc", str); + + // and ditto for last one + buf = mapper.readValue(p, TokenBuffer.class); + Map map = mapper.readValue(buf.asParser()); + assertEquals(1, map.size()); + assertEquals(Boolean.TRUE, map.get("a")); + + assertEquals(JsonToken.END_ARRAY, p.nextToken()); + assertNull(p.nextToken()); + } + @SuppressWarnings("resource") @Test public void testTokenBufferWithSequence() throws Exception From 013b19a1726ff44c634cf74f3b7f009039852b2d Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sun, 6 Apr 2025 12:25:57 +0530 Subject: [PATCH 07/87] Automatically detect class to deserialize #5064 --- .../jackson/databind/ObjectReader.java | 28 ++++++++++++++++++- .../jackson/databind/ObjectReaderTest.java | 3 ++ .../databind/deser/jdk/LocaleDeserTest.java | 9 ++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index fa45a4029c..4dec0a2131 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -1492,7 +1492,7 @@ public void writeTree(JsonGenerator g, TreeNode rootNode) { * @param src Source to read content from */ @SuppressWarnings("unchecked") - public T readValue(InputStream src) throws IOException + private T readValue(InputStream src) throws IOException { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false); @@ -1515,6 +1515,12 @@ public T readValue(InputStream src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } + @SuppressWarnings("unchecked") + public T readValue(InputStream src, T... reified) throws IOException + { + return forType(getClassOf(reified)).readValue(src); + } + /** * Method that binds content read from given input source, * using configuration of this reader. @@ -1585,6 +1591,26 @@ public T readValue(String src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } + @SuppressWarnings("unchecked") + public T readValue(String src, T... reified) throws IOException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(src, getClassOf(reified)); + } + + /** + * Utility method to get the class type from a varargs array. + * + * @param the generic type + * @param array the varargs array + * @return the class of the array component + */ + private static Class getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + /** * Method that binds content read from given byte array, * using configuration of this reader. diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index 5c5c16931d..987c8a5577 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -454,6 +454,9 @@ public void testCanPassResultToOverloadedMethod() throws Exception { ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller"); process(reader.readValue(source, POJO.class)); + + POJO pojo = reader.readValue(source); + process(pojo); } void process(POJO pojo) { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java index a65bc137a7..71048da278 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java @@ -268,6 +268,15 @@ public void testLocaleFuzz47034() throws Exception assertNotNull(loc); } + @Test + public void testLocaleFuzz47034WithAutoDetectClss() throws Exception + { + Locale loc = MAPPER.reader() + .without(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .readValue(getClass().getResourceAsStream("/fuzz/oss-fuzz-47034.json")); + assertNotNull(loc); + } + // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47036 // @since 2.14 @Test From 31bfb139ce3a70b4ef3d734e6358ffac49102a75 Mon Sep 17 00:00:00 2001 From: Fawzi Essam Date: Mon, 7 Apr 2025 00:02:46 +0200 Subject: [PATCH 08/87] Fixing records deserialization issue (#5094, parts of #4628) with read-access when json renamed to same property name (#5073) --- release-notes/CREDITS-2.x | 5 ++ release-notes/VERSION-2.x | 8 ++- .../introspect/POJOPropertiesCollector.java | 2 +- .../records/RecordWithJsonIgnoreTest.java | 6 -- .../records/RecordWithReadOnlyTest.java | 50 ++++++++++++++-- .../RecordWIthJsonIgnoreAndValue4628Test.java | 39 ++++++++++++ .../tofix/RecordWithReadOnly5049Test.java | 60 ------------------- 7 files changed, 97 insertions(+), 73 deletions(-) create mode 100644 src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java delete mode 100644 src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithReadOnly5049Test.java diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 2cb1ab3ffb..92ea213b63 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1885,3 +1885,8 @@ Zhen Lin Low (@zhenlin-pay2) * Reported, fixed #4920: Creator properties are ignored on abstract types when collecting bean properties, breaking AsExternalTypeDeserializer (2.18.3) + +Fawzi Essam (@iifawz) + * Contributed fix for #5049: Duplicate creator property "b" (index 0 vs 1) + on simple java record + (2.18.3) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 93ea1bd2db..56796ffe1e 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,6 +4,12 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ +2.18.4 (not yet released) + +#5049: Duplicate creator property "b" (index 0 vs 1) on simple java record + (reported by @richard-melvin) + (fix contributed by Fawzi E) + 2.18.3 (28-Feb-2025) #4444: The `KeyDeserializer` specified in the class with `@JsonDeserialize(keyUsing = ...)` @@ -13,7 +19,7 @@ Project: jackson-databind index for property 'cause' (reported by @nilswieber) (fix by Joo-Hyuk K) -#4844: Fix wrapped array hanlding wrt `null` by `StdDeserializer` +#4844: Fix wrapped array handling wrt `null` by `StdDeserializer` (fix by Stanislav S) #4848: Avoid type pollution in `StringCollectionDeserializer` (contributed by Jonas K) 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 f73c29177b..643baceb42 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -1798,7 +1798,7 @@ protected boolean _replaceCreatorProperty(List creatorPrope POJOPropertyBuilder prop) { final AnnotatedParameter ctorParam = prop.getConstructorParameter(); - if (creatorProperties != null) { + if (creatorProperties != null && ctorParam != null) { for (int i = 0, len = creatorProperties.size(); i < len; ++i) { POJOPropertyBuilder cprop = creatorProperties.get(i); if (cprop != null) { diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java index 4fb02ee485..5ea8ed5fde 100644 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java @@ -61,12 +61,6 @@ public void testSerializeJsonIgnoreAndJsonPropertyRecord() throws Exception { assertEquals("{\"id\":123}", json); } - @Test - public void testDeserializeJsonIgnoreAndJsonPropertyRecord() throws Exception { - RecordWithIgnoreJsonProperty value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnoreJsonProperty.class); - assertEquals(new RecordWithIgnoreJsonProperty(123, "Bob"), value); - } - /* /********************************************************************** /* Test methods, JsonIgnore accessor diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithReadOnlyTest.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithReadOnlyTest.java index 589e90e808..fe99355b21 100644 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithReadOnlyTest.java +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithReadOnlyTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class RecordWithReadOnlyTest extends DatabindTestUtil { @@ -50,6 +52,27 @@ public RecordWithReadOnlyAllAndNoArgConstructor() { } } + + static class ReadOnly5049Pojo + { + protected String a, b; + + ReadOnly5049Pojo( + @JsonProperty(value = "a", access = JsonProperty.Access.READ_ONLY) String a, + @JsonProperty(value = "b", access = JsonProperty.Access.READ_ONLY) String b) { + this.a = a; + this.b = b; + } + + public String getA() { return a; } + public String getB() { return b; } + } + + record ReadOnly5049Record( + @JsonProperty(value = "a", access = JsonProperty.Access.READ_ONLY) String a, + @JsonProperty(value = "b", access = JsonProperty.Access.READ_ONLY) String b) { + } + private final ObjectMapper MAPPER = newJsonMapper(); /* @@ -82,14 +105,31 @@ public void testSerializeReadOnlyNamedProperty() throws Exception { assertEquals(a2q("{'id':123,'name':'Bob'}"), json); } - /** - * Currently documents a bug where a property was NOT ignored during deserialization if given an explicit name. - * Also reproducible in 2.14.x. - */ + @Test public void testDeserializeReadOnlyNamedProperty() throws Exception { RecordWithReadOnlyNamedProperty value = MAPPER.readValue(a2q("{'id':123,'name':'Bob'}"), RecordWithReadOnlyNamedProperty.class); - assertEquals(new RecordWithReadOnlyNamedProperty(123, "Bob"), value); // BUG: should be `null` instead of "Bob" + assertEquals(new RecordWithReadOnlyNamedProperty(123, null), value); + } + + @Test + void testRoundtripPOJO() throws Exception + { + String json = MAPPER.writeValueAsString(new ReadOnly5049Pojo("hello", "world")); + ReadOnly5049Pojo pojo = MAPPER.readerFor(ReadOnly5049Pojo.class).readValue(json); + assertNotNull(pojo); + assertNull(pojo.a); + assertNull(pojo.b); + } + + @Test + void testRoundtripRecord() throws Exception + { + String json = MAPPER.writeValueAsString(new ReadOnly5049Record("hello", "world")); + ReadOnly5049Record record = MAPPER.readValue(json, ReadOnly5049Record.class); + assertNotNull(record); + assertNull(record.a()); + assertNull(record.b()); } /* diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java new file mode 100644 index 0000000000..5592c0eb47 --- /dev/null +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java @@ -0,0 +1,39 @@ +package com.fasterxml.jackson.databind.tofix; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// [databind#4628] @JsonIgnore is ignored with read access +public class RecordWIthJsonIgnoreAndValue4628Test + extends DatabindTestUtil { + + record RecordWithIgnoreJsonProperty(int id, @JsonIgnore @JsonProperty("name") String name) { + } + + record RecordWithIgnoreJsonPropertyDifferentName(int id, @JsonIgnore @JsonProperty("name2") String name) { + } + + private final ObjectMapper MAPPER = newJsonMapper(); + + + // passing normally given different name is used + @Test + public void testDeserializeJsonIgnoreRecordWithDifferentName() throws Exception { + RecordWithIgnoreJsonPropertyDifferentName value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnoreJsonPropertyDifferentName.class); + assertEquals(new RecordWithIgnoreJsonPropertyDifferentName(123, null), value); + } + + @Test + @JacksonTestFailureExpected + public void testDeserializeJsonIgnoreAndJsonPropertyRecord() throws Exception { + RecordWithIgnoreJsonProperty value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnoreJsonProperty.class); + assertEquals(new RecordWithIgnoreJsonProperty(123, null), value); // should be null, actual "bob" + } + +} diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithReadOnly5049Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithReadOnly5049Test.java deleted file mode 100644 index 0e38e82258..0000000000 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithReadOnly5049Test.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.fasterxml.jackson.databind.tofix; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; -import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class RecordWithReadOnly5049Test extends DatabindTestUtil -{ - record ReadOnly5049Record( - @JsonProperty(value = "a", access = JsonProperty.Access.READ_ONLY) String a, - @JsonProperty(value = "b", access = JsonProperty.Access.READ_ONLY) String b) { - } - - static class ReadOnly5049Pojo - { - protected String a, b; - - ReadOnly5049Pojo( - @JsonProperty(value = "a", access = JsonProperty.Access.READ_ONLY) String a, - @JsonProperty(value = "b", access = JsonProperty.Access.READ_ONLY) String b) { - this.a = a; - this.b = b; - } - - public String getA() { return a; } - public String getB() { return b; } - } - - private final ObjectMapper MAPPER = newJsonMapper(); - - @Test - void testRoundtripPOJO() throws Exception - { - String json = MAPPER.writeValueAsString(new ReadOnly5049Pojo("hello", "world")); - //System.err.println("JSON/pojo: "+json); - ReadOnly5049Pojo pojo = MAPPER.readerFor(ReadOnly5049Pojo.class).readValue(json); - assertNotNull(pojo); - assertNull(pojo.a); - assertNull(pojo.b); - } - - @JacksonTestFailureExpected - @Test - void testRoundtripRecord() throws Exception - { - // json = MAPPER.writeValueAsString(new ReadOnly5049Record("hello", "world")); - String json = "{\"a\":\"hello\",\"b\":\"world\"}"; - ReadOnly5049Record record = MAPPER.readValue(json, ReadOnly5049Record.class); - assertNotNull(record); - assertNull(record.a()); - assertNull(record.b()); - } -} From e89d0960e9e81399cb9a562f530170b0002852e0 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 6 Apr 2025 15:17:10 -0700 Subject: [PATCH 09/87] Fix a minor Github CI issue --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0c322a220c..68b8d85ce4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -75,7 +75,7 @@ jobs: uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./target/site/jacoco/jacoco.xml + files: ./target/site/jacoco/jacoco.xml flags: unittests trigger-dep-build-v2: From 9da0c3565bc1967a1f4bbb79dccb3ee7016ef189 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Mon, 7 Apr 2025 13:31:26 +0530 Subject: [PATCH 10/87] Automatically detect class to deserialize #5064 --- .../fasterxml/jackson/databind/ObjectReader.java | 16 +++++++++++++++- .../jackson/databind/ObjectReaderTest.java | 7 +++++++ .../databind/deser/jdk/LocaleDeserTest.java | 9 --------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index 4dec0a2131..b2c7245ea5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -777,6 +777,20 @@ public ObjectReader forType(JavaType valueType) _valueToUpdate, _schema, _injectableValues, det); } + /** + * Method for constructing a new reader instance. + */ + public ObjectReader readerFor(T... reified) { + // Detect class type + Class clazz = getClassOf(reified); + + // Convert class to JavaType + JavaType valueType = _config.getTypeFactory().constructType(clazz); + + return forType(valueType); + } + + /** * Method for constructing a new reader instance that is configured * to data bind into specified type. @@ -1492,7 +1506,7 @@ public void writeTree(JsonGenerator g, TreeNode rootNode) { * @param src Source to read content from */ @SuppressWarnings("unchecked") - private T readValue(InputStream src) throws IOException + public T readValue(InputStream src) throws IOException { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false); diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index 987c8a5577..edfbe8e659 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -291,6 +291,13 @@ public void testDeprecatedSettings() throws Exception assertSame(newR, newR.withRootName(PropertyName.construct("foo"))); } + @Test + public void testAutoDetectForReader() throws Exception + { + Number n = MAPPER.reader().readerFor().readValue("123 "); + assertEquals(Integer.valueOf(123), n); + } + @Test public void testNoPrefetch() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java index 71048da278..a65bc137a7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java @@ -268,15 +268,6 @@ public void testLocaleFuzz47034() throws Exception assertNotNull(loc); } - @Test - public void testLocaleFuzz47034WithAutoDetectClss() throws Exception - { - Locale loc = MAPPER.reader() - .without(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) - .readValue(getClass().getResourceAsStream("/fuzz/oss-fuzz-47034.json")); - assertNotNull(loc); - } - // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47036 // @since 2.14 @Test From 08251b9d75f9d5105f2888b66b6b163c811f0383 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 10:29:52 -0700 Subject: [PATCH 11/87] Fixes #5063: generate distinct names (type ids) if `Version` has no artifact id (#5076) --- release-notes/VERSION-2.x | 2 ++ .../jackson/databind/module/SimpleModule.java | 26 +++++++++++++++---- .../databind/module/SimpleModuleTest.java | 24 +++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 903d912d76..f80c0aa013 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -83,6 +83,8 @@ Project: jackson-databind (contributed by @pjfanning) #5052: Minor bug in `FirstCharBasedValidator.forFirstNameRule()`: returns `null` in non-default case +#5063: `SimpleModule` not registered due to `getTypeId()` returning an empty string + (reported by @seadbrane) #5069: Add copy-constructor for `MappingIterator` (contributed by @wrongwrong) diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java index 262f618c2a..e5d9551a5b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java @@ -130,14 +130,19 @@ public SimpleModule() { // note: generate different name for direct instantiation, sub-classing; // this to avoid collision in former case while still addressing // [databind#3110] - _name = (getClass() == SimpleModule.class) - ? "SimpleModule-"+MODULE_ID_SEQ.getAndIncrement() - : getClass().getName(); + _name = _generateName(getClass()); _version = Version.unknownVersion(); // 07-Jun-2021, tatu: [databind#3110] Not passed explicitly so... _hasExplicitName = false; } + // @since 2.19 + private static String _generateName(Class cls) { + return (cls == SimpleModule.class) + ? "SimpleModule-"+MODULE_ID_SEQ.getAndIncrement() + : cls.getName(); + } + /** * Convenience constructor that will default version to * {@link Version#unknownVersion()}. @@ -148,10 +153,21 @@ public SimpleModule(String name) { /** * Convenience constructor that will use specified Version, - * including name from {@link Version#getArtifactId()}. + * including name from {@link Version#getArtifactId()} + * (if available -- if not, will generate). */ public SimpleModule(Version version) { - this(version.getArtifactId(), version); + // 07-Apr-2025, tatu: [databind#5063]: check that we actually have a name; + // if not, generate it + String maybeName = version.getArtifactId(); + // Only considered explicit if name is non-null and not empty (i.e. not generated) + _hasExplicitName = (maybeName != null) && !maybeName.isEmpty(); + if (_hasExplicitName) { + _name = maybeName; + } else { + _name = _generateName(getClass()); + } + _version = Version.unknownVersion(); } /** diff --git a/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java index 58cd7418a6..5198c232d9 100644 --- a/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java @@ -235,6 +235,19 @@ public void serialize(Test3787Bean value, JsonGenerator gen, SerializerProvider } } + // For [databind#5063] + static class Module5063A extends SimpleModule { + public Module5063A() { + super(Version.unknownVersion()); + } + } + + static class Module5063B extends SimpleModule { + public Module5063B() { + super(Version.unknownVersion()); + } + } + /* /********************************************************** /* Unit tests; first, verifying need for custom handlers @@ -624,4 +637,15 @@ public void testAddModuleWithDeserializerTwiceThenOnlyLatestIsKept_reverseOrder( assertEquals("I am A", result.value); } + + // For [databind#5063] + @Test + public void testDuplicateModules5063() { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new Module5063A()) + .addModule(new Module5063B()) + .build(); + Set modules = mapper.getRegisteredModuleIds(); + assertEquals(2, modules.size()); + } } From 470f4e9527c6e0df5de3c6d6f59bccdf2339e5c9 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 10:49:49 -0700 Subject: [PATCH 12/87] Fix one bug introduced by #5063 fix --- .../com/fasterxml/jackson/databind/module/SimpleModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java index e5d9551a5b..64d07e7c79 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java @@ -167,7 +167,7 @@ public SimpleModule(Version version) { } else { _name = _generateName(getClass()); } - _version = Version.unknownVersion(); + _version = version; } /** From 1af5bed0a034f94c301676c7192227e4a69b4e67 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 14:56:39 -0700 Subject: [PATCH 13/87] Minor javadoc improvement wrt #5063 --- .../com/fasterxml/jackson/databind/Module.java | 2 +- .../jackson/databind/ObjectMapper.java | 4 ++-- .../jackson/databind/module/SimpleModule.java | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/Module.java b/src/main/java/com/fasterxml/jackson/databind/Module.java index 15d8441db9..a044e2da0c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/Module.java +++ b/src/main/java/com/fasterxml/jackson/databind/Module.java @@ -53,7 +53,7 @@ public abstract class Module * instances are considered to be of same type, for purpose of preventing * multiple registrations of "same type of" module * (see {@link com.fasterxml.jackson.databind.MapperFeature#IGNORE_DUPLICATE_MODULE_REGISTRATIONS}) - * If `null` is returned, every instance is considered unique. + * If {@code null} is returned, every instance is considered unique. * If non-null value is returned, equality of id Objects is used to check whether * modules should be considered to be "of same type" *

diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 140872e85f..354fdd3ece 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -658,7 +658,7 @@ protected ObjectMapper(ObjectMapper src, JsonFactory factory) if (reg == null) { _registeredModuleTypes = null; } else { - _registeredModuleTypes = new LinkedHashSet(reg); + _registeredModuleTypes = new LinkedHashSet<>(reg); } } @@ -896,7 +896,7 @@ public ObjectMapper registerModule(Module module) if (_registeredModuleTypes == null) { // plus let's keep them in order too, easier to debug or expose // in registration order if that matter - _registeredModuleTypes = new LinkedHashSet(); + _registeredModuleTypes = new LinkedHashSet<>(); } // try adding; if already had it, should skip if (!_registeredModuleTypes.add(typeId)) { diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java index 64d07e7c79..8116c361e0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java @@ -15,8 +15,8 @@ import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; /** - * Vanilla {@link com.fasterxml.jackson.databind.Module} implementation that allows registration - * of serializers and deserializers, bean serializer + * Simple {@link com.fasterxml.jackson.databind.Module} implementation that allows + * registration of serializers and deserializers, serializer * and deserializer modifiers, registration of subtypes and mix-ins * as well as some other commonly * needed aspects (addition of custom {@link AbstractTypeResolver}s, @@ -24,10 +24,11 @@ *

* NOTE: that [de]serializers are registered as "default" [de]serializers. * As a result, they will have lower priority than the ones indicated through annotations on - * both Class and property-associated annotations -- for example, + * both {@code Class} and property-associated annotations -- for example, * {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize}.
- * In cases where both module-based [de]serializers and annotation-based [de]serializers are registered, - * the [de]serializer specified by the annotation will take precedence. + * In cases where both module-based [de]serializers and annotation-based + * [de]serializers are registered, the [de]serializer specified by annotations + * will take precedence. *

* NOTE: although it is not expected that sub-types should need to * override {@link #setupModule(SetupContext)} method, if they choose @@ -35,7 +36,7 @@ * to ensure that registration works as expected. *

* WARNING: when registering {@link JsonSerializer}s and {@link JsonDeserializer}s, - * only type erased {@code Class} is compared: this means that usually you should + * only type-erased {@code Class} is compared: this means that usually you should * NOT use this implementation for registering structured types such as * {@link java.util.Collection}s or {@link java.util.Map}s: this because parametric * type information will not be considered and you may end up having "wrong" handler @@ -229,7 +230,7 @@ public SimpleModule(String name, Version version, @Override public Object getTypeId() { - // 07-Jun-2021, tatu: [databind#3110] Return Type Id if name was + // 07-Jun-2021, tatu: [databind#3110] Return name as Type Id if name was // explicitly given if (_hasExplicitName) { return _name; @@ -243,7 +244,8 @@ public Object getTypeId() return _name; } // And for what it is worth, this should usually do the same and we could - // in fact always just return `_name`. But leaving as-is for now. + // in fact always just return `_name`. But leaving as-is for now: + // will essentially return {@code getClass().getName()}. return super.getTypeId(); } From c68e30ee918ac9d3fd535376f1bd68d0bdc26c68 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 15:04:19 -0700 Subject: [PATCH 14/87] Minor addition to #4628 test --- .../RecordWIthJsonIgnoreAndValue4628Test.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java index 5592c0eb47..0fe5b02ae2 100644 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; // [databind#4628] @JsonIgnore is ignored with read access public class RecordWIthJsonIgnoreAndValue4628Test @@ -19,6 +20,13 @@ record RecordWithIgnoreJsonProperty(int id, @JsonIgnore @JsonProperty("name") St record RecordWithIgnoreJsonPropertyDifferentName(int id, @JsonIgnore @JsonProperty("name2") String name) { } + static class Pojo4628 { + public int id; + @JsonIgnore @JsonProperty("name") public String name; + + public Pojo4628() { } + } + private final ObjectMapper MAPPER = newJsonMapper(); @@ -36,4 +44,12 @@ public void testDeserializeJsonIgnoreAndJsonPropertyRecord() throws Exception { assertEquals(new RecordWithIgnoreJsonProperty(123, null), value); // should be null, actual "bob" } + // But second case works for POJOs + + @Test + public void deserializeJsonIgnoreAndJsonPropertyPojo() throws Exception { + Pojo4628 value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", Pojo4628.class); + assertEquals(123, value.id); + assertNull(value.name); + } } From 7e7b6f7bd2853abfcfe6bfef94179be4837786de Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 17:09:27 -0700 Subject: [PATCH 15/87] Prep for 2.19.0-rc1 --- pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bf16259dfa..3cf279af49 100644 --- a/pom.xml +++ b/pom.xml @@ -9,12 +9,11 @@ com.fasterxml.jackson jackson-base - 2.19.0-SNAPSHOT + 2.19.0-rc1-SNAPSHOT - com.fasterxml.jackson.core jackson-databind - 2.19.0-SNAPSHOT + 2.19.0-rc1-SNAPSHOT jackson-databind jar General data-binding functionality for Jackson: works on core streaming API From 6337820aaa6b02210c0f2a8e7042570d8778530f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 17:46:15 -0700 Subject: [PATCH 16/87] Prep for 2.19.0-rc1 release --- pom.xml | 2 +- release-notes/VERSION-2.x | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3cf279af49..b3c387fe5b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.19.0-rc1-SNAPSHOT + 2.19.0-rc1 com.fasterxml.jackson.core jackson-databind diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index f80c0aa013..35afadb0cd 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,7 +4,7 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ -2.19.0 (not yet released) +2.19.0-rc1 (07-Apr-2025) #1467: Support `@JsonUnwrapped` with `@JsonCreator` (implementation by Liam F) From 70eb5e7bd5bf4e446e93179aff028a5c88113fb2 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 18:10:14 -0700 Subject: [PATCH 17/87] Prep for 2.19.0-rc2 (after botched rc1) --- pom.xml | 4 ++-- release-notes/VERSION-2.x | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index b3c387fe5b..762e2a319d 100644 --- a/pom.xml +++ b/pom.xml @@ -9,11 +9,11 @@ com.fasterxml.jackson jackson-base - 2.19.0-rc1 + 2.19.0-rc2 com.fasterxml.jackson.core jackson-databind - 2.19.0-rc1-SNAPSHOT + 2.19.0-rc2-SNAPSHOT jackson-databind jar General data-binding functionality for Jackson: works on core streaming API diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 35afadb0cd..b9da383e7e 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,7 +4,7 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ -2.19.0-rc1 (07-Apr-2025) +2.19.0-rc2 (07-Apr-2025) #1467: Support `@JsonUnwrapped` with `@JsonCreator` (implementation by Liam F) From 6a585de7482f70234c637a85c5613fa63ca64d9a Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 18:22:00 -0700 Subject: [PATCH 18/87] Disable jacoco, fails on release --- pom.xml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 762e2a319d..bb05dc4fc5 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - HEAD + jackson-databind-2.19.0-rc2 @@ -54,7 +54,11 @@ 1.15.10 4.11.0 - true + + + false com.fasterxml.jackson.databind.*;version=${project.version} @@ -69,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2024-09-27T01:57:15Z + 2025-04-08T01:18:28Z @@ -263,8 +267,8 @@ - https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-annotations/2.18.2> - https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.18.2> + https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-annotations/2.18.4 + https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.18.4 From b6ff25b21793e245dcecb43a76a573468fe01238 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 18:22:56 -0700 Subject: [PATCH 19/87] [maven-release-plugin] prepare release jackson-databind-2.19.0-rc2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bb05dc4fc5..f2c8dbe2b6 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.0-rc2-SNAPSHOT + 2.19.0-rc2 jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-04-08T01:18:28Z + 2025-04-08T01:22:37Z From 8276a5b263b8417f97e85b6c9be2548eb933762f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 18:22:58 -0700 Subject: [PATCH 20/87] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f2c8dbe2b6..16dd25ac47 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.0-rc2 + 2.19.0-SNAPSHOT jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-04-08T01:22:37Z + 2025-04-08T01:22:58Z From 5c215b1dfb1eb20e2d259f157dda6d9b54f88ec1 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 18:25:07 -0700 Subject: [PATCH 21/87] Back to snapshot dep --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 16dd25ac47..c3620b8a75 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.19.0-rc2 + 2.19.0-SNAPSHOT com.fasterxml.jackson.core jackson-databind From 2e169c6422966fcb4fae64a52a13a713c3983f2b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 20:36:49 -0700 Subject: [PATCH 22/87] Add cascading rebuild v2 for Hibernate module --- .github/workflows/trigger_dep_builds_v2.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/trigger_dep_builds_v2.yml b/.github/workflows/trigger_dep_builds_v2.yml index 2c31657de3..9802f146e2 100644 --- a/.github/workflows/trigger_dep_builds_v2.yml +++ b/.github/workflows/trigger_dep_builds_v2.yml @@ -23,13 +23,14 @@ jobs: - 'FasterXML/jackson-dataformat-xml' - 'FasterXML/jackson-datatypes-collections' - 'FasterXML/jackson-datatypes-misc' + - 'FasterXML/jackson-datatype-hibernate' - 'FasterXML/jackson-datatype-joda' - 'FasterXML/jackson-module-kotlin' - 'FasterXML/jackson-module-scala' - - 'FasterXML/jackson-jaxrs-providers' - 'FasterXML/jackson-jakarta-rs-providers' - - 'FasterXML/jackson-integration-tests' + - 'FasterXML/jackson-jaxrs-providers' - 'FasterXML/jackson-benchmarks' + - 'FasterXML/jackson-integration-tests' steps: - name: Repository dispatch @@ -41,5 +42,5 @@ jobs: # Could push information on what was built but not yet client-payload: '{"version": "N/A" }' - name: Delay between dispatches - run: sleep 10s + run: sleep 8s shell: bash From 759b89e4ab6e44f1a0d7e73c1f6281b737529eed Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 7 Apr 2025 20:41:00 -0700 Subject: [PATCH 23/87] Reduce wait b/w cascading trigger rebuilds --- .github/workflows/trigger_dep_builds_v3.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trigger_dep_builds_v3.yml b/.github/workflows/trigger_dep_builds_v3.yml index 575bca646e..e41edac7e1 100644 --- a/.github/workflows/trigger_dep_builds_v3.yml +++ b/.github/workflows/trigger_dep_builds_v3.yml @@ -41,5 +41,5 @@ jobs: # Could push information on what was built but not yet client-payload: '{"version": "N/A" }' - name: Delay between dispatches - run: sleep 10s + run: sleep 7s shell: bash From 8caf21ba6100e7e24a2eed0285ddbd133d90f05b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 8 Apr 2025 14:47:13 -0700 Subject: [PATCH 24/87] Start "master"->"3.x" rename (JSTEP-12) --- .github/workflows/dep_build_v3.yml | 2 +- .github/workflows/main.yml | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dep_build_v3.yml b/.github/workflows/dep_build_v3.yml index 84767f14a0..a827809c7c 100644 --- a/.github/workflows/dep_build_v3.yml +++ b/.github/workflows/dep_build_v3.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: master + ref: 3.x - name: Set up JDK uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 68b8d85ce4..74fef940da 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,16 +2,14 @@ name: Build and Deploy Snapshot on: push: branches: - - master - - "3.0" + - 3.x - "2.19" paths-ignore: - "README.md" - "release-notes/*" pull_request: branches: - - master - - "3.0" + - 3.x - "2.19" paths-ignore: - "README.md" @@ -90,8 +88,8 @@ jobs: trigger-dep-build-v3: name: Trigger v3 dep builds needs: [build] - # Only for pushes to default branch - if: ${{ github.event_name == 'push' && github.ref_name == 'master' }} + # Only for pushes to 3.x branch + if: ${{ github.event_name == 'push' && github.ref_name == '3.x' }} uses: ./.github/workflows/trigger_dep_builds_v3.yml secrets: token: ${{ secrets.REPO_DISPATCH }} From dcd5ec02d045be436b6618ee60714e4c03993af1 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 8 Apr 2025 15:30:45 -0700 Subject: [PATCH 25/87] Try simplifying branch matches --- .github/workflows/main.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 74fef940da..178d8d46bb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,16 +1,12 @@ name: Build and Deploy Snapshot on: - push: - branches: - - 3.x - - "2.19" + push: + branches: [2.*] paths-ignore: - "README.md" - "release-notes/*" - pull_request: - branches: - - 3.x - - "2.19" + pull_request: + branches: [2.*] paths-ignore: - "README.md" - "release-notes/*" From a82699112bf8d7342fbb9e81035309ec619e234f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 9 Apr 2025 13:22:07 -0700 Subject: [PATCH 26/87] Fixes #3343: make `BeanPropertyWriter.get()` non-final (#5082) --- release-notes/VERSION-2.x | 2 ++ .../databind/ser/BeanPropertyWriter.java | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index b9da383e7e..a8fef93cbb 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -18,6 +18,8 @@ Project: jackson-databind server and client side (requested by @qianlong) (contributed by Geoffrey G) +#3343: Allow BeanPropertyWriter Sub-classes to Override `get()` (remove `final`) + (requested by @alzimmermsft) #4388: Allow using `@JsonPropertyOrder` with "any" (`@JsonAnyGetter`) properties (fix by Joo-Hyuk K) #4650: `PrimitiveArrayDeserializers` should deal with single String value if diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java index 829d7c36e4..3ad85a4c3e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java @@ -759,7 +759,8 @@ public void serializeAsOmittedField(Object bean, JsonGenerator gen, */ @Override public void serializeAsElement(Object bean, JsonGenerator gen, - SerializerProvider prov) throws Exception { + SerializerProvider prov) throws Exception + { // inlined 'get()' final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean, (Object[]) null); @@ -818,7 +819,8 @@ public void serializeAsElement(Object bean, JsonGenerator gen, */ @Override public void serializeAsPlaceholder(Object bean, JsonGenerator gen, - SerializerProvider prov) throws Exception { + SerializerProvider prov) throws Exception + { if (_nullSerializer != null) { _nullSerializer.serialize(null, gen, prov); } else { @@ -835,7 +837,8 @@ public void serializeAsPlaceholder(Object bean, JsonGenerator gen, // Also part of BeanProperty implementation @Override public void depositSchemaProperty(JsonObjectFormatVisitor v, - SerializerProvider provider) throws JsonMappingException { + SerializerProvider provider) throws JsonMappingException + { if (v != null) { if (isRequired()) { v.property(this); @@ -861,7 +864,8 @@ public void depositSchemaProperty(JsonObjectFormatVisitor v, @Override @Deprecated public void depositSchemaProperty(ObjectNode propertiesNode, - SerializerProvider provider) throws JsonMappingException { + SerializerProvider provider) throws JsonMappingException + { JavaType propType = getSerializationType(); // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas // it doesn't... @@ -891,7 +895,8 @@ public void depositSchemaProperty(ObjectNode propertiesNode, protected JsonSerializer _findAndAddDynamic( PropertySerializerMap map, Class type, - SerializerProvider provider) throws JsonMappingException { + SerializerProvider provider) throws JsonMappingException + { PropertySerializerMap.SerializerAndMapResult result; if (_nonTrivialBaseType != null) { JavaType t = provider.constructSpecializedType(_nonTrivialBaseType, @@ -914,8 +919,11 @@ protected JsonSerializer _findAndAddDynamic( * Note: method is final as it should not need to be overridden -- rather, * calling method(s) ({@link #serializeAsField}) should be overridden to * change the behavior + *

+ * NOTE: was {@code final} until Jackson 2.19 */ - public final Object get(Object bean) throws Exception { + public Object get(Object bean) throws Exception + { return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod .invoke(bean, (Object[]) null); } From e33c7643656cfc3c3cd7c586913f54facf4839a0 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 9 Apr 2025 13:41:19 -0700 Subject: [PATCH 27/87] Post-merge #3343 fix to Javadoc --- .../jackson/databind/ser/BeanPropertyWriter.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java index 3ad85a4c3e..1530ba939e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java @@ -915,17 +915,14 @@ protected JsonSerializer _findAndAddDynamic( /** * Method that can be used to access value of the property this Object * describes, from given bean instance. - *

- * Note: method is final as it should not need to be overridden -- rather, - * calling method(s) ({@link #serializeAsField}) should be overridden to - * change the behavior *

* NOTE: was {@code final} until Jackson 2.19 */ public Object get(Object bean) throws Exception { - return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod - .invoke(bean, (Object[]) null); + return (_accessorMethod == null) + ? _field.get(bean) + : _accessorMethod.invoke(bean, (Object[]) null); } /** From 15d0309b745caf9226f9ab40f6dc3d8b5fef5ec9 Mon Sep 17 00:00:00 2001 From: Fawzi Essam Date: Fri, 11 Apr 2025 02:21:18 +0200 Subject: [PATCH 28/87] Fixing records deserialization issue when JsonIgnore is used with same json property name (#5089) --- release-notes/CREDITS-2.x | 7 +++- release-notes/VERSION-2.x | 4 ++ .../introspect/POJOPropertiesCollector.java | 6 +++ .../records/RecordJsonSerDeser188Test.java | 2 + .../records/RecordWithJsonIgnoreTest.java | 23 +++++++++-- .../RecordWIthJsonIgnoreAndValue4628Test.java | 39 ------------------- ...assedThrowableDeserialization4827Test.java | 2 +- 7 files changed, 38 insertions(+), 45 deletions(-) delete mode 100644 src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 92ea213b63..9e8ab4bd4a 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1886,7 +1886,10 @@ Zhen Lin Low (@zhenlin-pay2) when collecting bean properties, breaking AsExternalTypeDeserializer (2.18.3) -Fawzi Essam (@iifawz) +Fawzi Essam (@iifawzi) + * Contributed fix or #4628: `@JsonIgnore` and `@JsonProperty.access=READ_ONLY` + on Record property + (2.18.4) * Contributed fix for #5049: Duplicate creator property "b" (index 0 vs 1) on simple java record - (2.18.3) + (2.18.4) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 56796ffe1e..0f9a87d834 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -6,6 +6,10 @@ Project: jackson-databind 2.18.4 (not yet released) +#4628: `@JsonIgnore` and `@JsonProperty.access=READ_ONLY` on Record property + ignored for deserialization + (reported by Sim Y-T) + (fix contributed by Fawzi E) #5049: Duplicate creator property "b" (index 0 vs 1) on simple java record (reported by @richard-melvin) (fix contributed by Fawzi E) 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 643baceb42..05d9521dcc 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -1380,6 +1380,12 @@ protected void _renameProperties(Map props) Map.Entry entry = it.next(); POJOPropertyBuilder prop = entry.getValue(); + // 10-Apr-2025: [databind#4628] skip properties that are marked to be ignored + // TODO: we are using implicit name, is that ok? + if (_ignoredPropertyNames != null && _ignoredPropertyNames.contains(prop.getName())) { + continue; + } + Collection l = prop.findExplicitNames(); // no explicit names? Implicit one is fine as is diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java index 1b64e4d0d9..6407074c59 100644 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java @@ -50,6 +50,8 @@ public void serialize(String value, JsonGenerator jgen, SerializerProvider provi static class PrefixStringDeserializer extends StdScalarDeserializer { + private static final long serialVersionUID = 1L; + protected PrefixStringDeserializer() { super(String.class); } diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java index 5ea8ed5fde..fd13fedf82 100644 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordWithJsonIgnoreTest.java @@ -16,8 +16,10 @@ record RecordWithIgnore(int id, @JsonIgnore String name) { record RecordWithIgnoreJsonProperty(int id, @JsonIgnore @JsonProperty("name") String name) { } + record RecordWithIgnoreJsonPropertyDifferentName(int id, @JsonIgnore @JsonProperty("name2") String name) { + } + record RecordWithIgnoreAccessor(int id, String name) { - @JsonIgnore @Override public String name() { @@ -61,6 +63,20 @@ public void testSerializeJsonIgnoreAndJsonPropertyRecord() throws Exception { assertEquals("{\"id\":123}", json); } + @Test + public void testDeserializeJsonIgnoreAndJsonPropertyRecord() throws Exception { + RecordWithIgnoreJsonProperty value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", + RecordWithIgnoreJsonProperty.class); + assertEquals(new RecordWithIgnoreJsonProperty(123, null), value); + } + + @Test + public void testDeserializeJsonIgnoreRecordWithDifferentName() throws Exception { + RecordWithIgnoreJsonPropertyDifferentName value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", + RecordWithIgnoreJsonPropertyDifferentName.class); + assertEquals(new RecordWithIgnoreJsonPropertyDifferentName(123, null), value); + } + /* /********************************************************************** /* Test methods, JsonIgnore accessor @@ -69,10 +85,11 @@ public void testSerializeJsonIgnoreAndJsonPropertyRecord() throws Exception { @Test public void testSerializeJsonIgnoreAccessorRecord() throws Exception { - String json = MAPPER.writeValueAsString(new RecordWithIgnoreAccessor(123, "Bob")); - assertEquals("{\"id\":123}", json); + assertEquals("{\"id\":123}", + MAPPER.writeValueAsString(new RecordWithIgnoreAccessor(123, "Bob"))); } + // [databind#4628] @Test public void testDeserializeJsonIgnoreAccessorRecord() throws Exception { RecordWithIgnoreAccessor expected = new RecordWithIgnoreAccessor(123, null); diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java deleted file mode 100644 index 5592c0eb47..0000000000 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWIthJsonIgnoreAndValue4628Test.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.fasterxml.jackson.databind.tofix; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; -import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -// [databind#4628] @JsonIgnore is ignored with read access -public class RecordWIthJsonIgnoreAndValue4628Test - extends DatabindTestUtil { - - record RecordWithIgnoreJsonProperty(int id, @JsonIgnore @JsonProperty("name") String name) { - } - - record RecordWithIgnoreJsonPropertyDifferentName(int id, @JsonIgnore @JsonProperty("name2") String name) { - } - - private final ObjectMapper MAPPER = newJsonMapper(); - - - // passing normally given different name is used - @Test - public void testDeserializeJsonIgnoreRecordWithDifferentName() throws Exception { - RecordWithIgnoreJsonPropertyDifferentName value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnoreJsonPropertyDifferentName.class); - assertEquals(new RecordWithIgnoreJsonPropertyDifferentName(123, null), value); - } - - @Test - @JacksonTestFailureExpected - public void testDeserializeJsonIgnoreAndJsonPropertyRecord() throws Exception { - RecordWithIgnoreJsonProperty value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnoreJsonProperty.class); - assertEquals(new RecordWithIgnoreJsonProperty(123, null), value); // should be null, actual "bob" - } - -} diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java b/src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java index c3bc887a58..53169b8ce6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java @@ -15,7 +15,7 @@ public class SubclassedThrowableDeserialization4827Test extends DatabindTestUtil { - + @SuppressWarnings("serial") public static class SubclassedExceptionJava extends Exception { @JsonCreator public SubclassedExceptionJava( From 73dc9e5b44457decd4bd6bc93ad39ed5ba244e85 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sat, 12 Apr 2025 09:56:10 +0530 Subject: [PATCH 29/87] Review comments fixes. Removed Test cases of removed code. #5064 --- .../fasterxml/jackson/databind/ObjectReader.java | 15 --------------- .../jackson/databind/ObjectReaderTest.java | 3 --- 2 files changed, 18 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index b2c7245ea5..00850145a1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -1529,12 +1529,6 @@ public T readValue(InputStream src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } - @SuppressWarnings("unchecked") - public T readValue(InputStream src, T... reified) throws IOException - { - return forType(getClassOf(reified)).readValue(src); - } - /** * Method that binds content read from given input source, * using configuration of this reader. @@ -1605,15 +1599,6 @@ public T readValue(String src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } - @SuppressWarnings("unchecked") - public T readValue(String src, T... reified) throws IOException { - if (reified.length > 0) { - throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); - } - - return readValue(src, getClassOf(reified)); - } - /** * Utility method to get the class type from a varargs array. * diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index edfbe8e659..b7fe167184 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -461,9 +461,6 @@ public void testCanPassResultToOverloadedMethod() throws Exception { ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller"); process(reader.readValue(source, POJO.class)); - - POJO pojo = reader.readValue(source); - process(pojo); } void process(POJO pojo) { From f018ed429ad8230b741f026bf181ef6df4f67a5f Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sat, 12 Apr 2025 10:03:34 +0530 Subject: [PATCH 30/87] Review comments fixes. Removed Test cases of removed code. #5064 --- .../jackson/databind/ObjectReader.java | 25 ------------------- .../databind/BoundsChecksForInputTest.java | 3 --- .../jackson/databind/ObjectReaderTest.java | 7 ------ 3 files changed, 35 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index 00850145a1..fa45a4029c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -777,20 +777,6 @@ public ObjectReader forType(JavaType valueType) _valueToUpdate, _schema, _injectableValues, det); } - /** - * Method for constructing a new reader instance. - */ - public ObjectReader readerFor(T... reified) { - // Detect class type - Class clazz = getClassOf(reified); - - // Convert class to JavaType - JavaType valueType = _config.getTypeFactory().constructType(clazz); - - return forType(valueType); - } - - /** * Method for constructing a new reader instance that is configured * to data bind into specified type. @@ -1599,17 +1585,6 @@ public T readValue(String src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } - /** - * Utility method to get the class type from a varargs array. - * - * @param the generic type - * @param array the varargs array - * @return the class of the array component - */ - private static Class getClassOf(T[] array) { - return (Class) array.getClass().getComponentType(); - } - /** * Method that binds content read from given byte array, * using configuration of this reader. diff --git a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java index 89a0f11d85..493fd4c482 100644 --- a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java @@ -45,9 +45,6 @@ public void testBoundsWithByteArrayInput() throws Exception { final JavaType TYPE = MAPPER.constructType(String.class); _testBoundsWithByteArrayInput( (data,offset,len)->MAPPER.readValue(data, offset, len, TYPE)); - - _testBoundsWithByteArrayInput( - (data,offset,len)->MAPPER.readValue(data, offset, len)); } private void _testBoundsWithByteArrayInput(ByteBackedCreation creator) throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index b7fe167184..5c5c16931d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -291,13 +291,6 @@ public void testDeprecatedSettings() throws Exception assertSame(newR, newR.withRootName(PropertyName.construct("foo"))); } - @Test - public void testAutoDetectForReader() throws Exception - { - Number n = MAPPER.reader().readerFor().readValue("123 "); - assertEquals(Integer.valueOf(123), n); - } - @Test public void testNoPrefetch() throws Exception { From 3824b115c9e93842d3fad5284ff4dcb73535d080 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 16 Apr 2025 18:33:43 -0700 Subject: [PATCH 31/87] Update CI to latest Ubuntu --- .github/workflows/dep_build_v2.yml | 3 +-- .github/workflows/dep_build_v3.yml | 3 +-- .github/workflows/main.yml | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dep_build_v2.yml b/.github/workflows/dep_build_v2.yml index efe2bbc377..7de7ac82b6 100644 --- a/.github/workflows/dep_build_v2.yml +++ b/.github/workflows/dep_build_v2.yml @@ -11,12 +11,11 @@ permissions: jobs: build: # Do we want wide matrix build? For now, limited - runs-on: ${{ matrix.os }} + runs-on: 'ubuntu-latest' strategy: fail-fast: false matrix: java_version: ['8', '17', '21'] - os: ['ubuntu-22.04'] env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: diff --git a/.github/workflows/dep_build_v3.yml b/.github/workflows/dep_build_v3.yml index 9962ea8a60..28d4dd65ab 100644 --- a/.github/workflows/dep_build_v3.yml +++ b/.github/workflows/dep_build_v3.yml @@ -11,12 +11,11 @@ permissions: jobs: build: # Do we want wide matrix build? For now, limited - runs-on: ${{ matrix.os }} + runs-on: 'ubuntu-latest' strategy: fail-fast: false matrix: java_version: ['8', '17', '21'] - os: ['ubuntu-22.04'] env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8585f94f40..2d53699513 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,10 +26,10 @@ jobs: fail-fast: false matrix: java_version: ['8', '11', '17', '21', '22'] - os: ['ubuntu-22.04'] + os: ['ubuntu-24.04'] include: - java_version: '8' - os: 'ubuntu-22.04' + os: 'ubuntu-24.04' release_build: 'R' - java_version: '8' os: 'windows-latest' @@ -55,7 +55,7 @@ jobs: - name: Extract project Maven version id: projectVersion if: ${{ !matrix.is_windows }} - run: echo "version=$(./mvnw org.apache.maven.plugins:maven-help-plugin:3.4.1:evaluate -DforceStdout -Dexpression=project.version -q)" >> $GITHUB_OUTPUT + run: echo "version=$(./mvnw org.apache.maven.plugins:maven-help-plugin:3.5.1:evaluate -DforceStdout -Dexpression=project.version -q)" >> $GITHUB_OUTPUT - name: Verify Android SDK Compatibility if: ${{ matrix.release_build }} run: ./mvnw -B -q -ff -ntp -DskipTests animal-sniffer:check From 3ff375f6cafa9696f43c5ba6f1d607cbd4ea20da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=EC=84=A0=EA=B6=8C?= Date: Thu, 17 Apr 2025 11:14:28 +0900 Subject: [PATCH 32/87] modify ObjectReader._parserFactory comment (#5097) --- src/main/java/com/fasterxml/jackson/databind/ObjectReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index fa45a4029c..4a9335882d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -66,7 +66,7 @@ public class ObjectReader protected final DefaultDeserializationContext _context; /** - * Factory used for constructing {@link JsonGenerator}s + * Factory used for constructing {@link JsonParser}s */ protected final JsonFactory _parserFactory; From 21a11f4fe8b85a7c7fd69465c13f0c5805689201 Mon Sep 17 00:00:00 2001 From: Ryan Schmitt Date: Tue, 22 Apr 2025 14:22:46 -0700 Subject: [PATCH 33/87] Fix regression in `ObjectNode.with()` (#5099) --- release-notes/CREDITS-2.x | 4 ++++ .../jackson/databind/node/ObjectNode.java | 3 ++- .../jackson/databind/node/ObjectNodeTest.java | 23 +++++++++++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index ce90d619b3..51af56a9d2 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1930,3 +1930,7 @@ Joren Inghelbrecht (@jin-harmoney) Will Paul (@dropofwill) * Contributed #4979: Allow default enums with `@JsonCreator` (2.19.0) + +Ryan Schmitt (@rschmitt) + * Contributed #5099: Fix regression in `ObjectNode.with()` + (2.19.0) diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java index 6cf1ed2f49..bd6234279e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java @@ -87,7 +87,8 @@ public ObjectNode with(String exprOrProperty) { .getClass().getName() + "`)"); } ObjectNode result = objectNode(); - return _put(exprOrProperty, result); + _put(exprOrProperty, result); + return result; } @Override diff --git a/src/test/java/com/fasterxml/jackson/databind/node/ObjectNodeTest.java b/src/test/java/com/fasterxml/jackson/databind/node/ObjectNodeTest.java index e82f7a2e4a..f5ceafdc28 100644 --- a/src/test/java/com/fasterxml/jackson/databind/node/ObjectNodeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/node/ObjectNodeTest.java @@ -291,11 +291,11 @@ public void testNullKeyChecking() assertThrows(NullPointerException.class, () -> src.set(null, BooleanNode.TRUE)); assertThrows(NullPointerException.class, () -> src.replace(null, BooleanNode.TRUE)); - + assertThrows(NullPointerException.class, () -> src.setAll(Collections.singletonMap(null, MAPPER.createArrayNode()))); } - + @Test public void testRemove() { @@ -334,6 +334,25 @@ public void testValidWithObject() throws Exception assertEquals("{\"prop\":{}}", MAPPER.writeValueAsString(root)); } + // for [databind#5099] + @Test + public void testValidWith() throws Exception + { + ObjectNode root = MAPPER.createObjectNode(); + assertEquals("{}", MAPPER.writeValueAsString(root)); + + @SuppressWarnings("deprecation") + ObjectNode withResult = root.with( "with" ); + withResult.put( "key", "value" ); + + ObjectNode withObjectResult = root.withObject( "withObject" ); + withObjectResult.put( "key", "value" ); + + assertEquals("{\"key\":\"value\"}", MAPPER.writeValueAsString(withObjectResult)); + assertEquals("{\"key\":\"value\"}", MAPPER.writeValueAsString(withResult)); + assertEquals(withResult, withObjectResult); + } + @Test public void testValidWithArray() throws Exception { From 707c3c19a3adb7eebfc6eeee4e7fce7b0c235518 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Fri, 25 Apr 2025 02:12:02 +0900 Subject: [PATCH 34/87] Fix #4533, add `MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES` (#5105) --- release-notes/CREDITS-2.x | 3 + release-notes/VERSION-2.x | 4 + .../jackson/databind/MapperFeature.java | 16 ++- .../jackson/databind/util/BeanUtil.java | 9 +- .../interop/Java8Datetime4533Test.java | 119 ++++++++++++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/interop/Java8Datetime4533Test.java diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 51af56a9d2..2fc62f6951 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -321,6 +321,9 @@ Michal Letynski (mletynski@github) Jeff Schnitzer (stickfigure@github) * Suggested #504: Add `DeserializationFeature.USE_LONG_FOR_INTS` (2.6.0) + * Requested #4533: Add `MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES` to disable + the "Java 8 date/time XYZ not supported by default" error + (2.19.0) Jerry Yang (islanderman@github) * Contributed #820: Add new method for `ObjectReader`, to bind from JSON Pointer position diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 7f6c420eae..39420174a1 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -22,6 +22,10 @@ Project: jackson-databind (requested by @alzimmermsft) #4388: Allow using `@JsonPropertyOrder` with "any" (`@JsonAnyGetter`) properties (fix by Joo-Hyuk K) +#4533: Add `MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES` to disable the + "Java 8 date/time XYZ not supported by default" error + (requested by Jeff S) + (fix by Joo-Hyuk K) #4650: `PrimitiveArrayDeserializers` should deal with single String value if `DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY` enabled (reported, fix suggested by @eeren-bm) diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java index 75ee3eebd4..3a053193c8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java @@ -661,7 +661,21 @@ public enum MapperFeature implements ConfigFeature * * @since 2.19 */ - REQUIRE_HANDLERS_FOR_JAVA8_OPTIONALS(true) + REQUIRE_HANDLERS_FOR_JAVA8_OPTIONALS(true), + + /** + * Feature that determines what happens if Java 8 {@link java.time} (and + * other related Java 8 date/time types) are to be serialized or deserialized, but there + * are no registered handlers for them. + * If enabled, an exception is thrown (to indicate problem, a solution for which is + * to register {@code jackson-datatype-jsr310} module); if disabled, the value is + * serialized and/or deserialized using regular POJO ("Bean") (de)serialization. + *

+ * Feature is enabled by default. + * + * @since 2.19 + */ + REQUIRE_HANDLERS_FOR_JAVA8_TIMES(true), ; private final boolean _defaultState; diff --git a/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java index 6763ee4fd4..19086fae19 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java @@ -295,6 +295,8 @@ public static String stdManglePropertyName(final String basename, final int offs * "well-known" types for which there would be a datatype module; and if so, * return appropriate failure message to give to caller. * + * @return error message to use, or null if failure is not needed. + * * @since 2.19 */ public static String checkUnsupportedType(MapperConfig config, JavaType type) { @@ -312,6 +314,11 @@ public static String checkUnsupportedType(MapperConfig config, JavaType type) if (type.isTypeOrSubTypeOf(Throwable.class)) { return null; } + failFeature = MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES; + final boolean fail = (config == null) || config.isEnabled(failFeature); + if (!fail) { + return null; + } typeName = "Java 8 date/time"; moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"; } else if (isJodaTimeClass(className)) { @@ -332,7 +339,7 @@ public static String checkUnsupportedType(MapperConfig config, JavaType type) typeName, ClassUtil.getTypeDescription(type), moduleName); if (failFeature != null) { str = String.format("%s (or disable `MapperFeature.%s`)", - str, MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_OPTIONALS.name()); + str, failFeature.name()); } return str; } diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/Java8Datetime4533Test.java b/src/test/java/com/fasterxml/jackson/databind/interop/Java8Datetime4533Test.java new file mode 100644 index 0000000000..a317cf3258 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/interop/Java8Datetime4533Test.java @@ -0,0 +1,119 @@ +package com.fasterxml.jackson.databind.interop; + +import java.time.LocalDate; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +public class Java8Datetime4533Test + extends DatabindTestUtil +{ + private final ObjectMapper MAPPER = newJsonMapper(); + + private final ObjectMapper LENIENT_MAPPER = JsonMapper.builder() + .disable(MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES) + .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) + .build(); + + @Test + public void testPreventSerialization() throws Exception + { + // [databind#4533]: prevent accidental serialization of Java 8 date/time types + // as POJOs, without Java 8 date/time module: + _testPreventSerialization(java.time.LocalDateTime.now()); + _testPreventSerialization(java.time.LocalDate.now()); + _testPreventSerialization(java.time.LocalTime.now()); + _testPreventSerialization(java.time.OffsetDateTime.now()); + _testPreventSerialization(java.time.ZonedDateTime.now()); + } + + private void _testPreventSerialization(Object value) throws Exception + { + try { + String json = MAPPER.writeValueAsString(value); + fail("Should not pass, wrote out as\n: "+json); + } catch (com.fasterxml.jackson.databind.exc.InvalidDefinitionException e) { + verifyException(e, "Java 8 date/time type `"+value.getClass().getName() + +"` not supported by default"); + verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\""); + verifyException(e, "(or disable `MapperFeature.%s`)", + MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES.name()); + } + } + + @Test + public void testPreventDeserialization() throws Exception + { + // [databind#4533]: prevent accidental deserialization of Java 8 date/time types + // as POJOs, without Java 8 date/time module: + _testPreventDeserialization(java.time.LocalDateTime.class); + _testPreventDeserialization(java.time.LocalDate.class); + _testPreventDeserialization(java.time.LocalTime.class); + _testPreventDeserialization(java.time.OffsetDateTime.class); + _testPreventDeserialization(java.time.ZonedDateTime.class); + } + + private void _testPreventDeserialization(Class value) throws Exception + { + try { + Object result = MAPPER.readValue(" 0 ", value); + fail("Not expecting to pass, resulted in: "+result); + } catch (com.fasterxml.jackson.databind.exc.InvalidDefinitionException e) { + verifyException(e, "Java 8 date/time type `"+value.getName() + +"` not supported by default"); + verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\""); + verifyException(e, "(or disable `MapperFeature.%s`)", + MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES.name()); + } + } + + @Test + public void testLenientSerailization() throws Exception + { + _testLenientSerialization(java.time.LocalDateTime.now()); + _testLenientSerialization(java.time.LocalDate.now()); + _testLenientSerialization(java.time.LocalTime.now()); + _testLenientSerialization(java.time.OffsetDateTime.now()); + + // Except ZonedDateTime serialization fails with ... + try { + _testLenientSerialization(java.time.ZonedDateTime.now()); + fail("Should not pass, wrote out as\n: "+java.time.ZonedDateTime.now()); + } catch (JsonMappingException e) { + verifyException(e, "Class com.fasterxml.jackson.databind.ser.BeanPropertyWriter"); + verifyException(e, "with modifiers \"public\""); + } + } + + private void _testLenientSerialization(Object value) throws Exception + { + String json = LENIENT_MAPPER.writeValueAsString(value); + assertThat(json).isNotNull(); + } + + @Test + public void testAllowDeserializationWithFeature() throws Exception + { + _testAllowDeserializationLenient(java.time.LocalDateTime.class); + _testAllowDeserializationLenient(java.time.LocalDate.class); + _testAllowDeserializationLenient(java.time.LocalTime.class); + _testAllowDeserializationLenient(java.time.OffsetDateTime.class); + _testAllowDeserializationLenient(java.time.ZonedDateTime.class); + } + + private void _testAllowDeserializationLenient(Class target) throws Exception { + JacksonException e = assertThrows(JacksonException.class, () -> + LENIENT_MAPPER.readValue("{}", target)); + Assertions.assertThat(e).hasMessageContaining("Cannot construct instance of `"+target.getName()+"`"); + } + +} From eb43405e12a4fce3ea1b7accde9e6ebf775b24d6 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 24 Apr 2025 16:40:14 -0700 Subject: [PATCH 35/87] Prep for 2.19.0 --- pom.xml | 2 +- release-notes/VERSION-2.x | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c3620b8a75..716aa465a2 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.19.0-SNAPSHOT + 2.19.0 com.fasterxml.jackson.core jackson-databind diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 39420174a1..9d7e64aae3 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,7 +4,7 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ -2.19.0-rc2 (07-Apr-2025) +2.19.0 (24-Apr-2025) #1467: Support `@JsonUnwrapped` with `@JsonCreator` (implementation by Liam F) From f644a920f4ac768797edf7709070411837c83ef8 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 24 Apr 2025 16:41:04 -0700 Subject: [PATCH 36/87] [maven-release-plugin] prepare release jackson-databind-2.19.0 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 716aa465a2..209c93df21 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.0-SNAPSHOT + 2.19.0 jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.19.0-rc2 + jackson-databind-2.19.0 @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-04-08T01:22:58Z + 2025-04-24T23:40:43Z From a84f99a7dee56670bb69ae2526356ed963a2f63c Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 24 Apr 2025 16:41:07 -0700 Subject: [PATCH 37/87] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 209c93df21..76be4065f5 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.0 + 2.19.1-SNAPSHOT jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.19.0 + jackson-databind-2.19.0-rc2 @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-04-24T23:40:43Z + 2025-04-24T23:41:06Z From 1928b88fd1f21a5f722052d90ca4c02c0b0d1d38 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 24 Apr 2025 16:43:05 -0700 Subject: [PATCH 38/87] Back to snapshot dep --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76be4065f5..76bfb3f12f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.19.0 + 2.19.1-SNAPSHOT com.fasterxml.jackson.core jackson-databind From 119e7cdf44c1a7aeb5908b88ba7c927234d30563 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 27 Apr 2025 17:28:02 -0700 Subject: [PATCH 39/87] Minor javadoc fixes --- .../java/com/fasterxml/jackson/databind/JsonNode.java | 8 ++++---- .../jackson/databind/introspect/AnnotatedClass.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java index f3652f9ac5..b535f29832 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java @@ -753,10 +753,10 @@ public long asLong(long defaultValue) { /** * Method that will try to convert value of this node to a Java double. * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) - * and 1.0 (true), and Strings are parsed using default Java language integer + * and 1.0 (true), and Strings are parsed using default Java language {@code double} * parsing rules. *

- * If representation cannot be converted to an int (including structured types + * If representation cannot be converted to a {@code double} (including structured types * like Objects and Arrays), * default value of 0.0 will be returned; no exceptions are thrown. */ @@ -767,10 +767,10 @@ public double asDouble() { /** * Method that will try to convert value of this node to a Java double. * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) - * and 1.0 (true), and Strings are parsed using default Java language integer + * and 1.0 (true), and Strings are parsed using default Java language {@code double} * parsing rules. *

- * If representation cannot be converted to an int (including structured types + * If representation cannot be converted to a {@code double} (including structured types * like Objects and Arrays), * specified defaultValue will be returned; no exceptions are thrown. */ 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 aef8152da6..2f04c84dae 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -77,7 +77,7 @@ public final class AnnotatedClass final protected Class _primaryMixIn; /** - * Flag that indicates whether (fulll) annotation resolution should + * Flag that indicates whether (full) annotation resolution should * occur: starting with 2.11 is disabled for JDK container types. * * @since 2.11 From 4924007e66f62485201313371b28c76d0928b120 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sat, 3 May 2025 10:25:49 -0700 Subject: [PATCH 40/87] Merge parts of #5140 first for easier code review of functional changes (#5141) --- .../deser/std/CollectionDeserializer.java | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) 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 5460cbc6f3..42434936f9 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 @@ -341,12 +341,10 @@ protected Collection _deserializeFromArray(JsonParser p, Deserialization // [databind#631]: Assign current value, to be accessible by custom serializers p.assignCurrentValue(result); - JsonDeserializer valueDes = _valueDeserializer; // Let's offline handling of values with Object Ids (simplifies code here) - if (valueDes.getObjectIdReader() != null) { + if (_valueDeserializer.getObjectIdReader() != null) { return _deserializeWithObjectId(p, ctxt, result); } - final TypeDeserializer typeDeser = _valueTypeDeserializer; JsonToken t; while ((t = p.nextToken()) != JsonToken.END_ARRAY) { try { @@ -356,10 +354,10 @@ protected Collection _deserializeFromArray(JsonParser p, Deserialization continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); + } else if (_valueTypeDeserializer == null) { + value = _valueDeserializer.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); } if (value == null) { _tryToAddNull(p, ctxt, result); @@ -400,9 +398,6 @@ protected final Collection handleNonArray(JsonParser p, DeserializationC if (!canWrap) { return (Collection) ctxt.handleUnexpectedToken(_containerType, p); } - JsonDeserializer valueDes = _valueDeserializer; - final TypeDeserializer typeDeser = _valueTypeDeserializer; - Object value; try { @@ -412,11 +407,12 @@ protected final Collection handleNonArray(JsonParser p, DeserializationC return result; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); + } else if (_valueTypeDeserializer == null) { + value = _valueDeserializer.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); } + // _skipNullValues is checked by _tryToAddNull. if (value == null) { _tryToAddNull(p, ctxt, result); return result; @@ -444,8 +440,6 @@ protected Collection _deserializeWithObjectId(JsonParser p, Deserializat // [databind#631]: Assign current value, to be accessible by custom serializers p.assignCurrentValue(result); - final JsonDeserializer valueDes = _valueDeserializer; - final TypeDeserializer typeDeser = _valueTypeDeserializer; CollectionReferringAccumulator referringAccumulator = new CollectionReferringAccumulator(_containerType.getContentType().getRawClass(), result); @@ -458,10 +452,10 @@ protected Collection _deserializeWithObjectId(JsonParser p, Deserializat continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); + } else if (_valueTypeDeserializer == null) { + value = _valueDeserializer.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); } if (value == null && _skipNullValues) { continue; From 08e6e131a00a53f48b74e7cc0d3f70278e877cdb Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 4 May 2025 03:51:18 +0900 Subject: [PATCH 41/87] Fixed problem in `CollectionDeserializer` where `_nullProvider` was not used if it was not `JsonToken.VALUE_NULL` (#5140) --- release-notes/CREDITS-2.x | 3 + release-notes/VERSION-2.x | 5 ++ .../deser/std/CollectionDeserializer.java | 66 +++++++++++++------ .../jdk/CollectionDeserializer5139Test.java | 54 +++++++++++++++ 4 files changed, 109 insertions(+), 19 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserializer5139Test.java diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 2fc62f6951..9ad9687ce8 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1860,6 +1860,9 @@ wrongwrong (@k163377) `@JsonDeserialize(keyUsing = ...)` is overwritten by the `KeyDeserializer` specified in the `ObjectMapper`. (2.18.3) + * Contributed fix for #5139: In `CollectionDeserializer`, `JsonSetter.contentNulls` + is sometimes ignored + (2.19.1) Bernd Ahlers (@bernd) * Reported #4742: Deserialization with Builder, External type id, `@JsonCreator` failing diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 9d7e64aae3..e723cd1cdf 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,6 +4,11 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ +2.19.1 (not yet released) + +#5139: In `CollectionDeserializer`, `JsonSetter.contentNulls` is sometimes ignored + (contributed by @wrongwrong) + 2.19.0 (24-Apr-2025) #1467: Support `@JsonUnwrapped` with `@JsonCreator` 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 42434936f9..882d2d90a2 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 @@ -353,16 +353,21 @@ protected Collection _deserializeFromArray(JsonParser p, Deserialization if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); - } else if (_valueTypeDeserializer == null) { - value = _valueDeserializer.deserialize(p, ctxt); + value = null; } else { - value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + value = _deserializeNoNullChecks(p, ctxt); } + if (value == null) { - _tryToAddNull(p, ctxt, result); - continue; + value = _nullProvider.getNullValue(ctxt); + + // _skipNullValues is checked by _tryToAddNull. + if (value == null) { + _tryToAddNull(p, ctxt, result); + continue; + } } + result.add(value); /* 17-Dec-2017, tatu: should not occur at this level... @@ -398,6 +403,7 @@ protected final Collection handleNonArray(JsonParser p, DeserializationC if (!canWrap) { return (Collection) ctxt.handleUnexpectedToken(_containerType, p); } + Object value; try { @@ -406,16 +412,19 @@ protected final Collection handleNonArray(JsonParser p, DeserializationC if (_skipNullValues) { return result; } - value = _nullProvider.getNullValue(ctxt); - } else if (_valueTypeDeserializer == null) { - value = _valueDeserializer.deserialize(p, ctxt); + value = null; } else { - value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + value = _deserializeNoNullChecks(p, ctxt); } - // _skipNullValues is checked by _tryToAddNull. + if (value == null) { - _tryToAddNull(p, ctxt, result); - return result; + value = _nullProvider.getNullValue(ctxt); + + // _skipNullValues is checked by _tryToAddNull. + if (value == null) { + _tryToAddNull(p, ctxt, result); + return result; + } } } catch (Exception e) { boolean wrap = ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); @@ -447,18 +456,21 @@ protected Collection _deserializeWithObjectId(JsonParser p, Deserializat while ((t = p.nextToken()) != JsonToken.END_ARRAY) { try { Object value; + if (t == JsonToken.VALUE_NULL) { if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); - } else if (_valueTypeDeserializer == null) { - value = _valueDeserializer.deserialize(p, ctxt); + value = null; } else { - value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + value = _deserializeNoNullChecks(p, ctxt); } - if (value == null && _skipNullValues) { - continue; + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + if (value == null && _skipNullValues) { + continue; + } } referringAccumulator.add(value); } catch (UnresolvedForwardReference reference) { @@ -475,6 +487,22 @@ protected Collection _deserializeWithObjectId(JsonParser p, Deserializat return result; } + /** + * Deserialize the content of the collection. + * If _valueTypeDeserializer is null, use _valueDeserializer.deserialize; if non-null, + * use _valueDeserializer.deserializeWithType to deserialize value. + * This method only performs deserialization and does not consider _skipNullValues, _nullProvider, etc. + * @since 2.19.1 + */ + protected Object _deserializeNoNullChecks(JsonParser p,DeserializationContext ctxt) + throws IOException + { + if (_valueTypeDeserializer == null) { + return _valueDeserializer.deserialize(p, ctxt); + } + return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + } + /** * {@code java.util.TreeSet} (and possibly other {@link Collection} types) does not * allow addition of {@code null} values, so isolate handling here. diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserializer5139Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserializer5139Test.java new file mode 100644 index 0000000000..725e57ce7a --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserializer5139Test.java @@ -0,0 +1,54 @@ +package com.fasterxml.jackson.databind.deser.jdk; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidNullException; +import com.fasterxml.jackson.databind.json.JsonMapper; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +// For [databind#5139] +public class CollectionDeserializer5139Test +{ + static class Dst { + private List list; + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + } + + @Test + public void nullsFailTest() { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + + assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"list\":[\"\"]}", new TypeReference(){}) + ); + } + + @Test + public void nullsSkipTest() throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"list\":[\"\"]}", new TypeReference() {}); + + assertTrue(dst.getList().isEmpty()); + } +} From c0afd56f173bd722bdf162a4680a59c44ee7d85b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sat, 3 May 2025 12:55:16 -0700 Subject: [PATCH 42/87] Remove branch limits from main CI workflow (since wildcards don't work) --- .github/workflows/main.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2d53699513..45dc70f4ac 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,18 +1,10 @@ name: Build and Deploy Snapshot on: push: - branches: - - master - - "3.0" - - "2.18" paths-ignore: - "README.md" - "release-notes/*" pull_request: - branches: - - master - - "3.0" - - "2.18" paths-ignore: - "README.md" - "release-notes/*" From 75dc91ff60dde0d6c3876c3ee9d8104b2e919198 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 6 May 2025 17:07:43 -0700 Subject: [PATCH 43/87] Prep for 2.18.4 --- pom.xml | 2 +- release-notes/VERSION-2.x | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9e915d77ac..d59d9d244c 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.18.4-SNAPSHOT + 2.18.4 com.fasterxml.jackson.core jackson-databind diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 0f9a87d834..f0cd5e658b 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,7 +4,7 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ -2.18.4 (not yet released) +2.18.4 (06-May-2025) #4628: `@JsonIgnore` and `@JsonProperty.access=READ_ONLY` on Record property ignored for deserialization From 4b66d3fbc506de350213b0d9091baa3e216c16a9 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 6 May 2025 17:28:42 -0700 Subject: [PATCH 44/87] [maven-release-plugin] prepare release jackson-databind-2.18.4 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d59d9d244c..188ad23a17 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.18.4-SNAPSHOT + 2.18.4 jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.18.2 + jackson-databind-2.18.4 @@ -68,7 +68,7 @@ com.fasterxml.jackson.databind.cfg - 2025-02-28T23:37:39Z + 2025-05-07T00:28:21Z From f3b2c4461618f611a6f3d55a3d17e76628c2f4c6 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 6 May 2025 17:28:45 -0700 Subject: [PATCH 45/87] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 188ad23a17..0d86affc47 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.18.4 + 2.18.5-SNAPSHOT jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.18.4 + jackson-databind-2.18.2 @@ -68,7 +68,7 @@ com.fasterxml.jackson.databind.cfg - 2025-05-07T00:28:21Z + 2025-05-07T00:28:45Z From 8065c4fd97ebb92a6c5adf81ac8bffc8023fbb90 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 6 May 2025 17:31:04 -0700 Subject: [PATCH 46/87] back to snapshot dep --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0d86affc47..d2886cdac2 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.18.4 + 2.18.5-SNAPSHOT com.fasterxml.jackson.core jackson-databind From 05f2ff4c9a98ac20610c034967a68106151d70c8 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 6 May 2025 20:27:00 -0700 Subject: [PATCH 47/87] Try fixing CI branch matching --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 45dc70f4ac..3bc764f905 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,7 @@ name: Build and Deploy Snapshot on: push: + branches: ['2.*'] paths-ignore: - "README.md" - "release-notes/*" From de41882c89a88ca5de0db74e8decf8bb2c2326a6 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 7 May 2025 20:47:34 -0700 Subject: [PATCH 48/87] Add failing test for #4656 (#5147) --- ...DeserializationProblemHandler4656Test.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/tofix/DeserializationProblemHandler4656Test.java diff --git a/src/test/java/com/fasterxml/jackson/databind/tofix/DeserializationProblemHandler4656Test.java b/src/test/java/com/fasterxml/jackson/databind/tofix/DeserializationProblemHandler4656Test.java new file mode 100644 index 0000000000..63f1451296 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/tofix/DeserializationProblemHandler4656Test.java @@ -0,0 +1,61 @@ +package com.fasterxml.jackson.databind.tofix; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class DeserializationProblemHandler4656Test extends DatabindTestUtil +{ + // For [databind#4656] + static class Person4656 { + public String id; + public String name; + public Long age; + } + + static class ProblemHandler4656 extends DeserializationProblemHandler + { + protected static final String NUMBER_LONG_KEY = "$numberLong"; + + @Override + public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, + JsonToken t, JsonParser p, String failureMsg) throws IOException + { + if (targetType.getRawClass().equals(Long.class) && t == JsonToken.START_OBJECT) { + JsonNode tree = p.readValueAsTree(); + if (tree.get(NUMBER_LONG_KEY) != null) { + try { + return Long.parseLong(tree.get(NUMBER_LONG_KEY).asText()); + } catch (NumberFormatException e) { } + } + } + return NOT_HANDLED; + } + } + + // For [databind#4656] + @JacksonTestFailureExpected + @Test + public void testIssue4656() throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addHandler(new ProblemHandler4656()) + .build(); + final String json = "{\"id\": \"12ab\", \"name\": \"Bob\", \"age\": {\"$numberLong\": \"10\"}}"; + Person4656 person = mapper.readValue(json, Person4656.class); + assertNotNull(person); + assertEquals("12ab", person.id); + assertEquals("Bob", person.name); + assertEquals(10L, person.age); + } +} From 6f326175837c0b7d0e17dc258a4679a87e00dbac Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 14 May 2025 19:33:55 -0700 Subject: [PATCH 49/87] Minor javadoc add --- .../com/fasterxml/jackson/databind/PropertyNamingStrategy.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java index e8482e2024..e5f4d69e2e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java +++ b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java @@ -187,6 +187,8 @@ public String nameForConstructorParameter(MapperConfig config, AnnotatedParam */ /** + * Replaced by {@link PropertyNamingStrategies.NamingBase}. + * * @deprecated Since 2.12 deprecated. See * databind#2715 * for reasons for deprecation. From 46f0168448ac3c27f24b14ba4c42b298a7bb5e7a Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 27 May 2025 09:23:49 -0700 Subject: [PATCH 50/87] Minor pom fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2886cdac2..cfd31c4b5a 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.18.2 + HEAD From de7155b12f2b37689c2648b30264bac7982ad77f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 27 May 2025 09:28:36 -0700 Subject: [PATCH 51/87] Fix CI snapshot publishing --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3bc764f905..5dd4fd00a5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,7 +37,7 @@ jobs: distribution: 'temurin' java-version: ${{ matrix.java_version }} cache: 'maven' - server-id: sonatype-nexus-snapshots + server-id: central-snapshots server-username: CI_DEPLOY_USERNAME server-password: CI_DEPLOY_PASSWORD # See https://github.com/actions/setup-java/blob/v2/docs/advanced-usage.md#Publishing-using-Apache-Maven @@ -55,8 +55,8 @@ jobs: - name: Deploy snapshot if: ${{ github.event_name != 'pull_request' && matrix.release_build && endsWith(steps.projectVersion.outputs.version, '-SNAPSHOT') }} env: - CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} - CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} + CI_DEPLOY_USERNAME: ${{ secrets.CENTRAL_DEPLOY_USERNAME }} + CI_DEPLOY_PASSWORD: ${{ secrets.CENTRAL_DEPLOY_PASSWORD }} # MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} run: ./mvnw -B -q -ff -DskipTests -ntp source:jar deploy - name: Generate code coverage From 000243a07edd20f14f7b3f5ec61e93169a06860b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 27 May 2025 09:30:01 -0700 Subject: [PATCH 52/87] Switch snapshot access to central portal --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cfd31c4b5a..eeb7b6d060 100644 --- a/pom.xml +++ b/pom.xml @@ -159,10 +159,11 @@ + - sonatype-nexus-snapshots - Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots + central-snapshots + Sonatype Central Portal (snapshots) + https://central.sonatype.com/repository/maven-snapshots false true From 686b5dca9eb571e544993f3e2dc983ca1a31c4c6 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 27 May 2025 09:31:01 -0700 Subject: [PATCH 53/87] Manual merge of pom.xml --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 76bfb3f12f..68a06024c7 100644 --- a/pom.xml +++ b/pom.xml @@ -180,10 +180,11 @@ + - sonatype-nexus-snapshots - Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots + central-snapshots + Sonatype Central Portal (snapshots) + https://central.sonatype.com/repository/maven-snapshots false true From 97eead568c35cbe9c1dc480d8e00ee1d89f5470a Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Tue, 10 Jun 2025 12:33:30 +0900 Subject: [PATCH 54/87] Add test against #5115 (#5183) --- .../tofix/RecordUnwrapped5115Test.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordUnwrapped5115Test.java diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordUnwrapped5115Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordUnwrapped5115Test.java new file mode 100644 index 0000000000..f8533def53 --- /dev/null +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordUnwrapped5115Test.java @@ -0,0 +1,82 @@ +package com.fasterxml.jackson.databind.tofix; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// [databind#5115] @JsonUnwrapped can't handle name collision #5115 +public class RecordUnwrapped5115Test + extends DatabindTestUtil +{ + record FooRecord5115(int a, int b) { } + record BarRecordFail5115(@JsonUnwrapped FooRecord5115 a, int c) { } + record BarRecordPass5115(@JsonUnwrapped FooRecord5115 foo, int c) { } + + static class FooPojo5115 { + public int a; + public int b; + } + + static class BarPojo5115 { + @JsonUnwrapped + public FooPojo5115 a; + public int c; + } + + private final ObjectMapper MAPPER = newJsonMapper(); + + @Test + void unwrappedPojoShouldRoundTrip() throws Exception + { + BarPojo5115 input = new BarPojo5115(); + input.a = new FooPojo5115(); + input.c = 4; + input.a.a = 1; + input.a.b = 2; + + String json = MAPPER.writeValueAsString(input); + BarPojo5115 output = MAPPER.readValue(json, BarPojo5115.class); + + assertEquals(4, output.c); + assertEquals(1, output.a.a); + assertEquals(2, output.a.b); + } + + @Test + void unwrappedRecordShouldRoundTripPass() throws Exception + { + BarRecordPass5115 input = new BarRecordPass5115(new FooRecord5115(1, 2), 3); + + // Serialize + String json = MAPPER.writeValueAsString(input); + + // Deserialize (currently fails) + BarRecordPass5115 output = MAPPER.readValue(json, BarRecordPass5115.class); + + // Should match after bug is fixed + assertEquals(input, output); + } + + @JacksonTestFailureExpected + @Test + void unwrappedRecordShouldRoundTrip() throws Exception + { + BarRecordFail5115 input = new BarRecordFail5115(new FooRecord5115(1, 2), 3); + + // Serialize + String json = MAPPER.writeValueAsString(input); + + // Once the bug is fixed, this assertion will pass and the + // @JacksonTestFailureExpected annotation can be removed. + BarRecordFail5115 output = MAPPER.readValue(json, BarRecordFail5115.class); + + // Should match after bug is fixed + assertEquals(input, output); + } + +} From 272ef122871292a43272360064a6f13ae10e6099 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 9 Jun 2025 20:48:04 -0700 Subject: [PATCH 55/87] JDK 22 -> 23 in CI --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5dd4fd00a5..cee86729ac 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - java_version: ['8', '11', '17', '21', '22'] + java_version: ['8', '11', '17', '21', '23'] os: ['ubuntu-24.04'] include: - java_version: '8' From 30e4b7ffdd8531b666de0ea6932bf8b4129affb7 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Tue, 10 Jun 2025 12:49:35 +0900 Subject: [PATCH 56/87] Add test for #5184 (#5185) --- .../RecordWithJsonIgnoredMethod5184Test.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithJsonIgnoredMethod5184Test.java diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithJsonIgnoredMethod5184Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithJsonIgnoredMethod5184Test.java new file mode 100644 index 0000000000..d9199d3140 --- /dev/null +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/tofix/RecordWithJsonIgnoredMethod5184Test.java @@ -0,0 +1,91 @@ +package com.fasterxml.jackson.databind.tofix; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class RecordWithJsonIgnoredMethod5184Test + extends DatabindTestUtil +{ + record TestData5184(@JsonProperty("test_property") String value) { + @JsonIgnore + public Optional getValue() { + return Optional.ofNullable(value); + } + } + + record TestData5184Alternate(@JsonProperty("test_property") String value) { + @JsonIgnore + public Optional optionalValue() { + return Optional.ofNullable(value); + } + } + + static final class TestData5184Class { + private final String value; + + public TestData5184Class(@JsonProperty("test_property") String value) { + this.value = value; + } + + @JsonIgnore + public Optional getValue() { + return Optional.ofNullable(value); + } + } + + private static final ObjectMapper MAPPER = newJsonMapper(); + + @JacksonTestFailureExpected + @Test + void should_deserialize_json_to_test_data() throws Exception { + String json = """ + {"test_property":"test value"} + """; + + var testData = MAPPER.readValue(json, TestData5184.class); + + assertThat(testData.value()).isEqualTo("test value"); + } + + @Test + void should_deserialize_json_to_test_data_class() throws Exception { + String json = """ + {"test_property":"test value"} + """; + + var testData = MAPPER.readValue(json, TestData5184Class.class); + + assertThat(testData.getValue()).contains("test value"); + } + + @Test + void should_deserialize_json_to_test_data_alternate() throws Exception { + String json = """ + {"test_property":"test value"} + """; + + var testData = MAPPER.readValue(json, TestData5184Alternate.class); + + assertThat(testData.value()).isEqualTo("test value"); + } + + @Test + void should_not_deserialize_wrong_json_model_to_test_data() throws Exception { + String json = """ + {"value":"test value"} + """; + + TestData5184 testData = MAPPER.readValue(json, TestData5184.class); + + assertThat(testData.value()).isNull(); + } +} From 82421749e29b8a47a4f86123d011f89368226b47 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 10 Jun 2025 15:18:05 -0700 Subject: [PATCH 57/87] Remove JDK 23 from 2.18 CI due to Mockito incompatibility --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cee86729ac..2a7e6a7168 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,8 @@ jobs: strategy: fail-fast: false matrix: - java_version: ['8', '11', '17', '21', '23'] + # 10-Jun-2025, tatu: JDK 23 fails for Mockito, don't use + java_version: ['8', '11', '17', '21'] os: ['ubuntu-24.04'] include: - java_version: '8' From eb302c9a3a7b77f8b1db647fb025aebd4b8dc0db Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 13 Jun 2025 18:10:10 -0700 Subject: [PATCH 58/87] Prep for 2.19.1 --- pom.xml | 2 +- release-notes/VERSION-2.x | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 68a06024c7..6435b8f92c 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.19.1-SNAPSHOT + 2.19.1 com.fasterxml.jackson.core jackson-databind diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 1cdae1d7a4..2aa4fe9539 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,7 +4,7 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ -2.19.1 (not yet released) +2.19.1 (13-Jun-2025) #5139: In `CollectionDeserializer`, `JsonSetter.contentNulls` is sometimes ignored (contributed by @wrongwrong) From cb317b071a86038b81f15c094686afaa87810d9d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 13 Jun 2025 18:13:21 -0700 Subject: [PATCH 59/87] [maven-release-plugin] prepare release jackson-databind-2.19.1 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6435b8f92c..6e635c1b46 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.1-SNAPSHOT + 2.19.1 jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.19.0-rc2 + jackson-databind-2.19.1 @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-04-24T23:41:06Z + 2025-06-14T01:13:03Z From cdcb0b192339fec2be8a6365414700e006f7c9e9 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 13 Jun 2025 18:13:24 -0700 Subject: [PATCH 60/87] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6e635c1b46..5c43dd3d07 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.1 + 2.19.2-SNAPSHOT jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.19.1 + jackson-databind-2.19.0-rc2 @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-06-14T01:13:03Z + 2025-06-14T01:13:24Z From b8f8c541d56c579521f4571c060cf9b8a3ff20de Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 13 Jun 2025 18:15:32 -0700 Subject: [PATCH 61/87] back to snapshot dep --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c43dd3d07..1932482f48 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.19.1 + 2.19.2-SNAPSHOT com.fasterxml.jackson.core jackson-databind From 7cc79d3503f5e73977e9aa205750dc59688122ee Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 15 Jun 2025 06:08:51 +0900 Subject: [PATCH 62/87] Refactor for MapDeserializer and EnumMapDeserializer (#5189) --- .../deser/std/EnumMapDeserializer.java | 27 +++++--- .../databind/deser/std/MapDeserializer.java | 68 +++++++++---------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java index 260cb6e746..52cc7aa04b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java @@ -269,9 +269,6 @@ public EnumMap deserialize(JsonParser p, DeserializationContext ctxt, // [databind#631]: Assign current value, to be accessible by custom deserializers p.assignCurrentValue(result); - final JsonDeserializer valueDes = _valueDeserializer; - final TypeDeserializer typeDeser = _valueTypeDeserializer; - String keyStr; if (p.isExpectedStartObjectToken()) { keyStr = p.nextFieldName(); @@ -312,10 +309,8 @@ public EnumMap deserialize(JsonParser p, DeserializationContext ctxt, continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } } catch (Exception e) { return wrapAndThrow(ctxt, e, result, keyStr); @@ -406,10 +401,8 @@ public EnumMap _deserializeUsingProperties(JsonParser p, DeserializationCon continue; } value = _nullProvider.getNullValue(ctxt); - } else if (_valueTypeDeserializer == null) { - value = _valueDeserializer.deserialize(p, ctxt); } else { - value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + value = _deserializeNoNullChecks(p, ctxt); } } catch (Exception e) { wrapAndThrow(ctxt, e, _containerType.getRawClass(), keyName); @@ -426,4 +419,20 @@ public EnumMap _deserializeUsingProperties(JsonParser p, DeserializationCon return null; } } + + /** + * Deserialize the content of the map. + * If _valueTypeDeserializer is null, use _valueDeserializer.deserialize; if non-null, + * use _valueDeserializer.deserializeWithType to deserialize value. + * This method only performs deserialization and does not consider _skipNullValues, _nullProvider, etc. + * @since 2.19.2 + */ + protected Object _deserializeNoNullChecks(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_valueTypeDeserializer == null) { + return _valueDeserializer.deserialize(p, ctxt); + } + return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java index 97010cea70..4d4d561ab9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java @@ -343,9 +343,11 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, if (ignorals != null) { Set ignoresToAdd = ignorals.findIgnoredForDeserialization(); if (!ignoresToAdd.isEmpty()) { - ignored = (ignored == null) ? new HashSet() : new HashSet(ignored); - for (String str : ignoresToAdd) { - ignored.add(str); + if (ignored == null) { + ignored = new HashSet<>(ignoresToAdd); + } else { + ignored = new HashSet(ignored); + ignored.addAll(ignoresToAdd); } } } @@ -500,8 +502,6 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, @SuppressWarnings("unchecked") public final Class getMapClass() { return (Class>) _containerType.getRawClass(); } - @Override public JavaType getValueType() { return _containerType; } - /* /********************************************************** /* Internal methods, non-merging deserialization @@ -511,12 +511,8 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, protected final Map _readAndBind(JsonParser p, DeserializationContext ctxt, Map result) throws IOException { - final KeyDeserializer keyDes = _keyDeserializer; - final JsonDeserializer valueDes = _valueDeserializer; - final TypeDeserializer typeDeser = _valueTypeDeserializer; - MapReferringAccumulator referringAccumulator = null; - boolean useObjectId = valueDes.getObjectIdReader() != null; + boolean useObjectId = _valueDeserializer.getObjectIdReader() != null; if (useObjectId) { referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result); @@ -537,7 +533,7 @@ protected final Map _readAndBind(JsonParser p, DeserializationCon } for (; keyStr != null; keyStr = p.nextFieldName()) { - Object key = keyDes.deserializeKey(keyStr, ctxt); + Object key = _keyDeserializer.deserializeKey(keyStr, ctxt); // And then the value... JsonToken t = p.nextToken(); if ((_inclusionChecker != null) && _inclusionChecker.shouldIgnore(keyStr)) { @@ -552,10 +548,8 @@ protected final Map _readAndBind(JsonParser p, DeserializationCon continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } if (useObjectId) { referringAccumulator.put(key, value); @@ -582,10 +576,8 @@ protected final Map _readAndBind(JsonParser p, DeserializationCon protected final Map _readAndBindStringKeyMap(JsonParser p, DeserializationContext ctxt, Map result) throws IOException { - final JsonDeserializer valueDes = _valueDeserializer; - final TypeDeserializer typeDeser = _valueTypeDeserializer; MapReferringAccumulator referringAccumulator = null; - boolean useObjectId = (valueDes.getObjectIdReader() != null); + boolean useObjectId = (_valueDeserializer.getObjectIdReader() != null); if (useObjectId) { referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result); } @@ -618,10 +610,8 @@ protected final Map _readAndBindStringKeyMap(JsonParser p, Deseri continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } if (useObjectId) { referringAccumulator.put(key, value); @@ -649,9 +639,6 @@ public Map _deserializeUsingCreator(JsonParser p, Deserialization // null -> no ObjectIdReader for Maps (yet?) PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null); - final JsonDeserializer valueDes = _valueDeserializer; - final TypeDeserializer typeDeser = _valueTypeDeserializer; - String key; if (p.isExpectedStartObjectToken()) { key = p.nextFieldName(); @@ -693,10 +680,8 @@ public Map _deserializeUsingCreator(JsonParser p, Deserialization continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } } catch (Exception e) { wrapAndThrow(ctxt, e, _containerType.getRawClass(), key); @@ -726,7 +711,6 @@ public Map _deserializeUsingCreator(JsonParser p, Deserialization protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt, Map result) throws IOException { - final KeyDeserializer keyDes = _keyDeserializer; final JsonDeserializer valueDes = _valueDeserializer; final TypeDeserializer typeDeser = _valueTypeDeserializer; @@ -748,7 +732,7 @@ protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt, } for (; keyStr != null; keyStr = p.nextFieldName()) { - Object key = keyDes.deserializeKey(keyStr, ctxt); + Object key = _keyDeserializer.deserializeKey(keyStr, ctxt); // And then the value... JsonToken t = p.nextToken(); if ((_inclusionChecker != null) && _inclusionChecker.shouldIgnore(keyStr)) { @@ -772,10 +756,8 @@ protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt, } else { value = valueDes.deserializeWithType(p, ctxt, typeDeser, old); } - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } if (value != old) { result.put(key, value); @@ -839,10 +821,8 @@ protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationCon } else { value = valueDes.deserializeWithType(p, ctxt, typeDeser, old); } - } else if (typeDeser == null) { - value = valueDes.deserialize(p, ctxt); } else { - value = valueDes.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } if (value != old) { result.put(key, value); @@ -853,6 +833,22 @@ protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationCon } } + /** + * Deserialize the content of the map. + * If _valueTypeDeserializer is null, use _valueDeserializer.deserialize; if non-null, + * use _valueDeserializer.deserializeWithType to deserialize value. + * This method only performs deserialization and does not consider _skipNullValues, _nullProvider, etc. + * @since 2.19.2 + */ + protected Object _deserializeNoNullChecks(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_valueTypeDeserializer == null) { + return _valueDeserializer.deserialize(p, ctxt); + } + return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + } + /** * @since 2.14 */ @@ -895,11 +891,11 @@ private void handleUnresolvedReference(DeserializationContext ctxt, private final static class MapReferringAccumulator { private final Class _valueType; - private Map _result; + private final Map _result; /** * A list of {@link MapReferring} to maintain ordering. */ - private List _accumulator = new ArrayList(); + private final List _accumulator = new ArrayList(); public MapReferringAccumulator(Class valueType, Map result) { _valueType = valueType; From 29d498a35926e41cb626af1c6e1e29cbde85d8e7 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 16 Jun 2025 10:32:01 +0900 Subject: [PATCH 63/87] Refactor for ObjectArrayDeserializer, StringArrayDeserializer, StringCollectionDeserializer and EnumSetDeserializer (#5190) --- .../deser/std/EnumSetDeserializer.java | 7 ++-- .../deser/std/ObjectArrayDeserializer.java | 34 +++++++++++-------- .../deser/std/StringArrayDeserializer.java | 12 +++---- .../std/StringCollectionDeserializer.java | 11 +++--- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java index 26521b58af..d69a3e1ef4 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java @@ -139,12 +139,9 @@ public EnumSetDeserializer withResolved(JsonDeserializer deser, @Override public boolean isCachable() { // One caveat: content deserializer should prevent caching - if (_enumType.getValueHandler() != null) { - return false; - } - return true; + return _enumType.getValueHandler() == null; } - + @Override // since 2.12 public LogicalType logicalType() { return LogicalType.Collection; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java index 20a728e751..61cc466335 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java @@ -201,7 +201,6 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) Object[] chunk = buffer.resetAndStart(); int ix = 0; JsonToken t; - final TypeDeserializer typeDeser = _elementTypeDeserializer; try { while ((t = p.nextToken()) != JsonToken.END_ARRAY) { @@ -213,10 +212,8 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = _elementDeserializer.deserialize(p, ctxt); } else { - value = _elementDeserializer.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); @@ -269,7 +266,6 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, int ix = intoValue.length; Object[] chunk = buffer.resetAndStart(intoValue, ix); JsonToken t; - final TypeDeserializer typeDeser = _elementTypeDeserializer; try { while ((t = p.nextToken()) != JsonToken.END_ARRAY) { @@ -280,10 +276,8 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, continue; } value = _nullProvider.getNullValue(ctxt); - } else if (typeDeser == null) { - value = _elementDeserializer.deserialize(p, ctxt); } else { - value = _elementDeserializer.deserializeWithType(p, ctxt, typeDeser); + value = _deserializeNoNullChecks(p, ctxt); } if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); @@ -320,7 +314,7 @@ protected Byte[] deserializeFromBase64(JsonParser p, DeserializationContext ctxt // But then need to convert to wrappers Byte[] result = new Byte[b.length]; for (int i = 0, len = b.length; i < len; ++i) { - result[i] = Byte.valueOf(b[i]); + result[i] = b[i]; } return result; } @@ -375,11 +369,7 @@ protected Object handleNonArray(JsonParser p, DeserializationContext ctxt) // if coercion failed, we can still add it to a list } - if (_elementTypeDeserializer == null) { - value = _elementDeserializer.deserialize(p, ctxt); - } else { - value = _elementDeserializer.deserializeWithType(p, ctxt, _elementTypeDeserializer); - } + value = _deserializeNoNullChecks(p, ctxt); } // Ok: bit tricky, since we may want T[], not just Object[] Object[] result; @@ -392,5 +382,21 @@ protected Object handleNonArray(JsonParser p, DeserializationContext ctxt) result[0] = value; return result; } + + /** + * Deserialize the content of the map. + * If _elementTypeDeserializer is null, use _elementDeserializer.deserialize; if non-null, + * use _elementDeserializer.deserializeWithType to deserialize value. + * This method only performs deserialization and does not consider _skipNullValues, _nullProvider, etc. + * @since 2.19.2 + */ + protected Object _deserializeNoNullChecks(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_elementTypeDeserializer == null) { + return _elementDeserializer.deserialize(p, ctxt); + } + return _elementDeserializer.deserializeWithType(p, ctxt, _elementTypeDeserializer); + } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java index 043ad2c1e1..864299d326 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java @@ -40,14 +40,14 @@ public final class StringArrayDeserializer /** * Value serializer to use, if not the standard one (which is inlined) */ - protected JsonDeserializer _elementDeserializer; + private final JsonDeserializer _elementDeserializer; /** * Handler we need for dealing with null values as elements * * @since 2.9 */ - protected final NullValueProvider _nullProvider; + private final NullValueProvider _nullProvider; /** * Specific override for this instance (from proper, or global per-type overrides) @@ -56,7 +56,7 @@ public final class StringArrayDeserializer * * @since 2.7 */ - protected final Boolean _unwrapSingle; + private final Boolean _unwrapSingle; /** * Marker flag set if the _nullProvider indicates that all null @@ -64,14 +64,14 @@ public final class StringArrayDeserializer * * @since 2.9 */ - protected final boolean _skipNullValues; + private final boolean _skipNullValues; public StringArrayDeserializer() { this(null, null, null); } @SuppressWarnings("unchecked") - protected StringArrayDeserializer(JsonDeserializer deser, + private StringArrayDeserializer(JsonDeserializer deser, NullValueProvider nuller, Boolean unwrapSingle) { super(String[].class); _elementDeserializer = (JsonDeserializer) deser; @@ -184,7 +184,7 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt) throws IO /** * Offlined version used when we do not use the default deserialization method. */ - protected final String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt, + private String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt, String[] old) throws IOException { final ObjectBuffer buffer = ctxt.leaseObjectBuffer(); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java index a008a6017c..d4984a084b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java @@ -37,20 +37,20 @@ public final class StringCollectionDeserializer * Value deserializer to use, if NOT the standard one * (if it is, will be null). */ - protected final JsonDeserializer _valueDeserializer; + private final JsonDeserializer _valueDeserializer; // // Instance construction settings: /** * Instantiator used in case custom handling is needed for creation. */ - protected final ValueInstantiator _valueInstantiator; + private final ValueInstantiator _valueInstantiator; /** * Deserializer that is used iff delegate-based creator is * to be used for deserializing from JSON Object. */ - protected final JsonDeserializer _delegateDeserializer; + private final JsonDeserializer _delegateDeserializer; // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties @@ -67,7 +67,7 @@ public StringCollectionDeserializer(JavaType collectionType, } @SuppressWarnings("unchecked") - protected StringCollectionDeserializer(JavaType collectionType, + private StringCollectionDeserializer(JavaType collectionType, ValueInstantiator valueInstantiator, JsonDeserializer delegateDeser, JsonDeserializer valueDeser, NullValueProvider nuller, Boolean unwrapSingle) @@ -78,7 +78,7 @@ protected StringCollectionDeserializer(JavaType collectionType, _delegateDeserializer = (JsonDeserializer) delegateDeser; } - protected StringCollectionDeserializer withResolved(JsonDeserializer delegateDeser, + private StringCollectionDeserializer withResolved(JsonDeserializer delegateDeser, JsonDeserializer valueDeser, NullValueProvider nuller, Boolean unwrapSingle) { @@ -107,6 +107,7 @@ public LogicalType logicalType() { /* Validation, post-processing /********************************************************** */ + @Override public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException From b29d3ab2fbdb04dfeb0c2a741f5d934b82802ecc Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 16 Jun 2025 10:45:20 +0900 Subject: [PATCH 64/87] Fix #5165 on MapDeserializer and EnumMapDeserializer (#5191) --- .../deser/std/EnumMapDeserializer.java | 20 ++++++- .../databind/deser/std/MapDeserializer.java | 50 +++++++++++++++- .../jdk/EnumMapDeserializer5165Test.java | 60 +++++++++++++++++++ .../deser/jdk/MapDeserializer5165Test.java | 54 +++++++++++++++++ 4 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializer5165Test.java create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializer5165Test.java diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java index 52cc7aa04b..e0e4bb2a47 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java @@ -308,10 +308,18 @@ public EnumMap deserialize(JsonParser p, DeserializationContext ctxt, if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } } catch (Exception e) { return wrapAndThrow(ctxt, e, result, keyStr); } @@ -400,10 +408,18 @@ public EnumMap _deserializeUsingProperties(JsonParser p, DeserializationCon if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } } catch (Exception e) { wrapAndThrow(ctxt, e, _containerType.getRawClass(), keyName); return null; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java index 4d4d561ab9..98875853a6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java @@ -547,10 +547,19 @@ protected final Map _readAndBind(JsonParser p, DeserializationCon if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + if (useObjectId) { referringAccumulator.put(key, value); } else { @@ -609,10 +618,19 @@ protected final Map _readAndBindStringKeyMap(JsonParser p, Deseri if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + if (useObjectId) { referringAccumulator.put(key, value); } else { @@ -679,10 +697,18 @@ public Map _deserializeUsingCreator(JsonParser p, Deserialization if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } } catch (Exception e) { wrapAndThrow(ctxt, e, _containerType.getRawClass(), key); return null; @@ -759,6 +785,15 @@ protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt, } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + if (value != old) { result.put(key, value); } @@ -824,6 +859,15 @@ protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationCon } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + if (value != old) { result.put(key, value); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializer5165Test.java new file mode 100644 index 0000000000..adf48cd02a --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializer5165Test.java @@ -0,0 +1,60 @@ +package com.fasterxml.jackson.databind.deser.jdk; + +import java.util.EnumMap; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidNullException; +import com.fasterxml.jackson.databind.json.JsonMapper; +import org.opentest4j.AssertionFailedError; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// For [databind#5165] +public class EnumMapDeserializer5165Test +{ + public enum MyEnum { + FOO + } + + static class Dst { + private EnumMap map; + + public EnumMap getMap() { + return map; + } + + public void setMap(EnumMap map) { + this.map = map; + } + } + + @Test + public void nullsFailTest() { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + + assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"map\":{\"FOO\":\"\"}}", new TypeReference(){}) + ); + } + + @Test + public void nullsSkipTest() throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"map\":{\"FOO\":\"\"}}", new TypeReference() {}); + + assertTrue(dst.getMap().isEmpty()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializer5165Test.java new file mode 100644 index 0000000000..82f1cec008 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializer5165Test.java @@ -0,0 +1,54 @@ +package com.fasterxml.jackson.databind.deser.jdk; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidNullException; +import com.fasterxml.jackson.databind.json.JsonMapper; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// For [databind#5165] +public class MapDeserializer5165Test +{ + static class Dst { + private Map map; + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + } + + @Test + public void nullsFailTest() { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + + assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"map\":{\"key\":\"\"}}", new TypeReference(){}) + ); + } + + @Test + public void nullsSkipTest() throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"map\":{\"key\":\"\"}}", new TypeReference() {}); + + assertTrue(dst.getMap().isEmpty()); + } +} From ea940e5b84c5ccaced3693040c21b114a89b1b18 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Thu, 10 Jul 2025 01:16:11 +0900 Subject: [PATCH 65/87] Fix serialization order change after #4775 (`@JsonAnyGetter` respects order) (#5216) --- .../introspect/POJOPropertiesCollector.java | 42 +++++++++++- .../introspect/POJOPropertyBuilder.java | 27 ++++++-- .../records/RecordJsonSerDeser188Test.java | 1 - .../ser/AnyGetterOrdering5215Test.java | 67 +++++++++++++++++++ 4 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterOrdering5215Test.java 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 2e1bc148c0..8ad07a53a8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -501,6 +501,40 @@ protected void collectAll() _collected = true; } + /** + * [databind#5215] JsonAnyGetter Serializer behavior change from 2.18.4 to 2.19.0 + * Put anyGetter in the end, before actual sorting further down {@link POJOPropertiesCollector#_sortProperties(Map)} + */ + private Map _putAnyGettersInTheEnd( + Map sortedProps) + { + AnnotatedMember anyAccessor; + + if (_anyGetters != null) { + anyAccessor = _anyGetters.getFirst(); + } else if (_anyGetterField != null) { + anyAccessor = _anyGetterField.getFirst(); + } else { + return sortedProps; + } + + // Here we'll use insertion-order preserving map, since possible alphabetic + // sorting already done earlier + Map newAll = new LinkedHashMap<>(sortedProps.size() * 2); + POJOPropertyBuilder anyGetterProp = null; + for (POJOPropertyBuilder prop : sortedProps.values()) { + if (prop.hasFieldOrGetter(anyAccessor)) { + anyGetterProp = prop; + } else { + newAll.put(prop.getName(), prop); + } + } + if (anyGetterProp != null) { + newAll.put(anyGetterProp.getName(), anyGetterProp); + } + return newAll; + } + /* /********************************************************************** /* Property introspection: Fields @@ -1588,14 +1622,16 @@ protected void _sortProperties(Map props) Map all; // Need to (re)sort alphabetically? if (sortAlpha) { - all = new TreeMap(); + all = new TreeMap<>(); } else { - all = new LinkedHashMap(size+size); + all = new LinkedHashMap<>(size+size); } - + // First, handle sorting caller expects: for (POJOPropertyBuilder prop : props.values()) { all.put(prop.getName(), prop); } + all = _putAnyGettersInTheEnd(all); + Map ordered = new LinkedHashMap<>(size+size); // Ok: primarily by explicit order if (propertyOrder != null) { diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java index e0c6f497df..088a7f8b30 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.databind.introspect; +import java.lang.reflect.Member; import java.util.*; import java.util.stream.Collectors; @@ -764,6 +765,24 @@ protected int _setterPriority(AnnotatedMethod m) return 2; } + // @since 2.19.2 + public boolean hasFieldOrGetter(AnnotatedMember member) { + return _hasAccessor(_fields, member) || _hasAccessor(_getters, member); + } + + private boolean _hasAccessor(Linked node, + AnnotatedMember memberToMatch) + { + // AnnotatedXxx are not canonical, but underlying JDK Members are: + final Member rawMemberToMatch = memberToMatch.getMember(); + for (; node != null; node = node.next) { + if (node.value.getMember() == rawMemberToMatch) { + return true; + } + } + return false; + } + /* /********************************************************** /* Implementations of refinement accessors @@ -877,19 +896,19 @@ public JsonProperty.Access withMember(AnnotatedMember member) { */ public void addField(AnnotatedField a, PropertyName name, boolean explName, boolean visible, boolean ignored) { - _fields = new Linked(a, _fields, name, explName, visible, ignored); + _fields = new Linked<>(a, _fields, name, explName, visible, ignored); } public void addCtor(AnnotatedParameter a, PropertyName name, boolean explName, boolean visible, boolean ignored) { - _ctorParameters = new Linked(a, _ctorParameters, name, explName, visible, ignored); + _ctorParameters = new Linked<>(a, _ctorParameters, name, explName, visible, ignored); } public void addGetter(AnnotatedMethod a, PropertyName name, boolean explName, boolean visible, boolean ignored) { - _getters = new Linked(a, _getters, name, explName, visible, ignored); + _getters = new Linked<>(a, _getters, name, explName, visible, ignored); } public void addSetter(AnnotatedMethod a, PropertyName name, boolean explName, boolean visible, boolean ignored) { - _setters = new Linked(a, _setters, name, explName, visible, ignored); + _setters = new Linked<>(a, _setters, name, explName, visible, ignored); } /** diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java index 73e6c14d3d..6407074c59 100644 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordJsonSerDeser188Test.java @@ -48,7 +48,6 @@ public void serialize(String value, JsonGenerator jgen, SerializerProvider provi } } - @SuppressWarnings("serial") static class PrefixStringDeserializer extends StdScalarDeserializer { private static final long serialVersionUID = 1L; diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterOrdering5215Test.java b/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterOrdering5215Test.java new file mode 100644 index 0000000000..0e38fca74a --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterOrdering5215Test.java @@ -0,0 +1,67 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; + +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// For [databind#5215]: Any-getter should be sorted last, by default +public class AnyGetterOrdering5215Test + extends DatabindTestUtil +{ + static class DynaBean { + public String l; + public String j; + public String a; + + protected Map extensions = new LinkedHashMap<>(); + + @JsonAnyGetter + public Map getExtensions() { + return extensions; + } + + @JsonAnySetter + public void addExtension(String name, Object value) { + extensions.put(name, value); + } + } + + /* + /********************************************************************** + /* Test methods + /********************************************************************** + */ + + private final ObjectMapper MAPPER = JsonMapper.builder() + .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) + .configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true) + .build(); + + @Test + public void testDynaBean() throws Exception + { + DynaBean b = new DynaBean(); + b.a = "1"; + b.j = "2"; + b.l = "3"; + b.addExtension("z", "5"); + b.addExtension("b", "4"); + assertEquals(a2q("{" + + "'a':'1'," + + "'j':'2'," + + "'l':'3'," + + "'b':'4'," + + "'z':'5'}"), MAPPER.writeValueAsString(b)); + } +} From dd27a218baf5aa83fd383114d9955e86e6d803de Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 9 Jul 2025 09:57:51 -0700 Subject: [PATCH 66/87] Update release notes wrt #5215 --- release-notes/CREDITS-2.x | 4 ++++ release-notes/VERSION-2.x | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 9ad9687ce8..5b6762f9d4 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1940,3 +1940,7 @@ Will Paul (@dropofwill) Ryan Schmitt (@rschmitt) * Contributed #5099: Fix regression in `ObjectNode.with()` (2.19.0) + +Eddú Meléndez Gonzales (@eddumelendez) + * Reported #5215: `@JsonAnyGetter` serialization order change from 2.18.4 to 2.19.0 + (2.19.2) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 2aa4fe9539..fb41822544 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,6 +4,12 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ +2.19.2 (not yet released) + +#5215: `@JsonAnyGetter` serialization order change from 2.18.4 to 2.19.0 + (reported by Eddú M) + (fix by Joo-Hyuk K) + 2.19.1 (13-Jun-2025) #5139: In `CollectionDeserializer`, `JsonSetter.contentNulls` is sometimes ignored From 1464997eb453dd5610fd90af0b0e9165da13d97b Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 10 Jul 2025 08:44:26 +0900 Subject: [PATCH 67/87] `JsonSetter.contentNulls` ignored for `Object[]`, `String[]` and `Collection` (#5202) --- release-notes/CREDITS-2.x | 3 + release-notes/VERSION-2.x | 3 + .../deser/std/ObjectArrayDeserializer.java | 34 ++++++- .../deser/std/StringArrayDeserializer.java | 29 +++++- .../std/StringCollectionDeserializer.java | 32 ++++++- .../jdk/ObjectArrayDeserializer5165Test.java | 45 +++++++++ .../jdk/StringArrayDeserializer5165Test.java | 81 ++++++++++++++++ .../StringCollectionDeserializer5165Test.java | 80 ++++++++++++++++ .../tofix/EnumSetDeserializer5165Test.java | 96 +++++++++++++++++++ 9 files changed, 393 insertions(+), 10 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java create mode 100644 src/test/java/com/fasterxml/jackson/databind/tofix/EnumSetDeserializer5165Test.java diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 5b6762f9d4..1e8dc2213f 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1863,6 +1863,9 @@ wrongwrong (@k163377) * Contributed fix for #5139: In `CollectionDeserializer`, `JsonSetter.contentNulls` is sometimes ignored (2.19.1) + * Contributed fix for #5202: #5202: `JsonSetter.contentNulls` ignored for `Object[]`, + `String[]` and `Collection` + (2.19.2) Bernd Ahlers (@bernd) * Reported #4742: Deserialization with Builder, External type id, `@JsonCreator` failing diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index fb41822544..ea5e567193 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -6,6 +6,9 @@ Project: jackson-databind 2.19.2 (not yet released) +#5202: `JsonSetter.contentNulls` ignored for `Object[]`, `String[]` + and `Collection` + (fix by @wrongwrong) #5215: `@JsonAnyGetter` serialization order change from 2.18.4 to 2.19.0 (reported by Eddú M) (fix by Joo-Hyuk K) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java index 61cc466335..d6e194c634 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java @@ -211,10 +211,19 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); ix = 0; @@ -275,10 +284,19 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, if (_skipNullValues) { continue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); ix = 0; @@ -346,7 +364,7 @@ protected Object handleNonArray(JsonParser p, DeserializationContext ctxt) if (_skipNullValues) { return _emptyValue; } - value = _nullProvider.getNullValue(ctxt); + value = null; } else { if (p.hasToken(JsonToken.VALUE_STRING)) { String textValue = p.getText(); @@ -371,6 +389,15 @@ protected Object handleNonArray(JsonParser p, DeserializationContext ctxt) value = _deserializeNoNullChecks(p, ctxt); } + + if (value == null) { + value = _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + return _emptyValue; + } + } + // Ok: bit tricky, since we may want T[], not just Object[] Object[] result; @@ -399,4 +426,3 @@ protected Object _deserializeNoNullChecks(JsonParser p, DeserializationContext c return _elementDeserializer.deserializeWithType(p, ctxt, _elementTypeDeserializer); } } - diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java index 864299d326..aafdc80230 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java @@ -162,10 +162,17 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt) throws IO if (_skipNullValues) { continue; } - value = (String) _nullProvider.getNullValue(ctxt); } else { value = _parseString(p, ctxt, _nullProvider); } + + if (value == null) { + value = (String) _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } } if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); @@ -219,13 +226,22 @@ private String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt, if (_skipNullValues) { continue; } - value = (String) _nullProvider.getNullValue(ctxt); + value = null; } else { value = deser.deserialize(p, ctxt); } } else { value = deser.deserialize(p, ctxt); } + + if (value == null) { + value = (String) _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); ix = 0; @@ -283,10 +299,17 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt, if (_skipNullValues) { return NO_STRINGS; } - value = (String) _nullProvider.getNullValue(ctxt); } else { value = _parseString(p, ctxt, _nullProvider); } + + if (value == null) { + value = (String) _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } } if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java index d4984a084b..acfb21f32a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java @@ -213,10 +213,18 @@ public Collection deserialize(JsonParser p, DeserializationContext ctxt, if (_skipNullValues) { continue; } - value = (String) _nullProvider.getNullValue(ctxt); } else { value = _parseString(p, ctxt, _nullProvider); } + + if (value == null) { + value = (String) _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + result.add(value); } } catch (Exception e) { @@ -246,13 +254,22 @@ private Collection deserializeUsingCustom(JsonParser p, DeserializationC if (_skipNullValues) { continue; } - value = (String) _nullProvider.getNullValue(ctxt); + value = null; } else { value = deser.deserialize(p, ctxt); } } else { value = deser.deserialize(p, ctxt); } + + if (value == null) { + value = (String) _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + continue; + } + } + result.add(value); } } catch (Exception e) { @@ -297,7 +314,7 @@ private final Collection handleNonArray(JsonParser p, DeserializationCon if (_skipNullValues) { return result; } - value = (String) _nullProvider.getNullValue(ctxt); + value = null; } else { if (p.hasToken(JsonToken.VALUE_STRING)) { String textValue = p.getText(); @@ -326,6 +343,15 @@ private final Collection handleNonArray(JsonParser p, DeserializationCon throw JsonMappingException.wrapWithPath(e, result, result.size()); } } + + if (value == null) { + value = (String) _nullProvider.getNullValue(ctxt); + + if (value == null && _skipNullValues) { + return result; + } + } + result.add(value); return result; } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java new file mode 100644 index 0000000000..7e77004fb1 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java @@ -0,0 +1,45 @@ +package com.fasterxml.jackson.databind.deser.jdk; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidNullException; +import com.fasterxml.jackson.databind.json.JsonMapper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// For [databind#5165] +public class ObjectArrayDeserializer5165Test +{ + static class Dst { + public Integer[] array; + } + + @Test + public void nullsFailTest() { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + + // NOTE! Relies on default coercion of "" into `null` for `Integer`s... + assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"array\":[\"\"]}", Dst.class) + ); + } + + @Test + public void nullsSkipTest() throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"array\":[\"\"]}", Dst.class); + // NOTE! Relies on default coercion of "" into `null` for `Integer`s... + assertEquals(0, dst.array.length, "Null values should be skipped"); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java new file mode 100644 index 0000000000..fd18fa68ba --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java @@ -0,0 +1,81 @@ +package com.fasterxml.jackson.databind.deser.jdk; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; + +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.InvalidNullException; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// For [databind#5165] +public class StringArrayDeserializer5165Test +{ + static class Dst { + public String[] array; + } + + // Custom deserializer that converts empty strings to null + static class EmptyStringToNullDeserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + public EmptyStringToNullDeserializer() { + super(String.class); + } + + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + if (value != null && value.isEmpty()) { + return null; + } + return value; + } + } + + private ObjectMapper createMapperWithCustomDeserializer() { + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); + + return JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + } + + @Test + public void nullsFailTest() { + ObjectMapper mapper = createMapperWithCustomDeserializer(); + + assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"array\":[\"\"]}", Dst.class) + ); + } + + @Test + public void nullsSkipTest() throws Exception { + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); + + ObjectMapper mapper = JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"array\":[\"\"]}", Dst.class); + + assertEquals(0, dst.array.length, "Null values should be skipped"); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java new file mode 100644 index 0000000000..11cbf28229 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java @@ -0,0 +1,80 @@ +package com.fasterxml.jackson.databind.deser.jdk; + +import java.io.IOException; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.InvalidNullException; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +// For [databind#5165] +public class StringCollectionDeserializer5165Test +{ + static class Dst { + public List list; + } + + // Custom deserializer that converts empty strings to null + static class EmptyStringToNullDeserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + public EmptyStringToNullDeserializer() { + super(String.class); + } + + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + if (value != null && value.isEmpty()) { + return null; + } + return value; + } + } + + private ObjectMapper createMapperWithCustomDeserializer() { + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); + + return JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + } + + @Test + public void nullsFailTest() { + ObjectMapper mapper = createMapperWithCustomDeserializer(); + + assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"list\":[\"\"]}", Dst.class) + ); + } + + @Test + public void nullsSkipTest() throws Exception { + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); + + ObjectMapper mapper = JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"list\":[\"\"]}", Dst.class); + + assertTrue(dst.list.isEmpty(), "Null values should be skipped"); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/tofix/EnumSetDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/tofix/EnumSetDeserializer5165Test.java new file mode 100644 index 0000000000..49542266a6 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/tofix/EnumSetDeserializer5165Test.java @@ -0,0 +1,96 @@ +package com.fasterxml.jackson.databind.tofix; + +import java.io.IOException; +import java.util.EnumSet; + +import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.InvalidNullException; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +// For [databind#5165] +public class EnumSetDeserializer5165Test +{ + public enum MyEnum { + FOO + } + + static class Dst { + private EnumSet set; + + public EnumSet getSet() { + return set; + } + + public void setSet(EnumSet set) { + this.set = set; + } + } + + // Custom deserializer that converts empty strings to null + static class EmptyStringToNullDeserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + public EmptyStringToNullDeserializer() { + super(MyEnum.class); + } + + @Override + public MyEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + if (value != null && value.isEmpty()) { + return null; + } + return MyEnum.valueOf(value); + } + } + + private ObjectMapper createMapperWithCustomDeserializer() { + SimpleModule module = new SimpleModule(); + module.addDeserializer(MyEnum.class, new EmptyStringToNullDeserializer()); + + return JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + } + + @JacksonTestFailureExpected + @Test + public void nullsFailTest() { + ObjectMapper mapper = createMapperWithCustomDeserializer(); + + assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"set\":[\"\"]}", new TypeReference(){}) + ); + } + + @JacksonTestFailureExpected + @Test + public void nullsSkipTest() throws Exception { + SimpleModule module = new SimpleModule(); + module.addDeserializer(MyEnum.class, new EmptyStringToNullDeserializer()); + + ObjectMapper mapper = JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"set\":[\"FOO\",\"\"]}", new TypeReference() {}); + + assertTrue(dst.getSet().isEmpty(), "Null values should be skipped"); + } +} From aaaceb87361245bccc4b5199798ad0c6cd23e6c2 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 10 Jul 2025 21:27:29 -0700 Subject: [PATCH 68/87] Trigger CI for 2.19 --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 1932482f48..00437eb57a 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ jackson-base 2.19.2-SNAPSHOT + com.fasterxml.jackson.core jackson-databind 2.19.2-SNAPSHOT From bcb78acdc7c7657f3af83b12a3016df2da551780 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 18 Jul 2025 10:44:04 -0700 Subject: [PATCH 69/87] Prep for 2.19.2 --- pom.xml | 3 +-- release-notes/VERSION-2.x | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 00437eb57a..47a7763e13 100644 --- a/pom.xml +++ b/pom.xml @@ -9,9 +9,8 @@ com.fasterxml.jackson jackson-base - 2.19.2-SNAPSHOT + 2.19.2 - com.fasterxml.jackson.core jackson-databind 2.19.2-SNAPSHOT diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index ea5e567193..712b5f84b7 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,7 +4,7 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ -2.19.2 (not yet released) +2.19.2 (18-Jul-2025) #5202: `JsonSetter.contentNulls` ignored for `Object[]`, `String[]` and `Collection` From 1a506043e0644c9cf60c0905474459605fc751d0 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 18 Jul 2025 10:49:22 -0700 Subject: [PATCH 70/87] [maven-release-plugin] prepare release jackson-databind-2.19.2 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 47a7763e13..a7b906be78 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.2-SNAPSHOT + 2.19.2 jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.19.0-rc2 + jackson-databind-2.19.2 @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-06-14T01:13:24Z + 2025-07-18T17:49:02Z From fe5a013cb9babd1a6ebf9c6b793a772038e82b2d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 18 Jul 2025 10:49:25 -0700 Subject: [PATCH 71/87] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index a7b906be78..630e60ac94 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.fasterxml.jackson.core jackson-databind - 2.19.2 + 2.19.3-SNAPSHOT jackson-databind jar General data-binding functionality for Jackson: works on core streaming API @@ -31,7 +31,7 @@ scm:git:git@github.com:FasterXML/jackson-databind.git scm:git:git@github.com:FasterXML/jackson-databind.git https://github.com/FasterXML/jackson-databind - jackson-databind-2.19.2 + jackson-databind-2.19.0-rc2 @@ -73,7 +73,7 @@ com.fasterxml.jackson.databind.cfg - 2025-07-18T17:49:02Z + 2025-07-18T17:49:25Z From 443989823bd16e2fb74d71cb85427cc85f2e04ca Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 18 Jul 2025 10:50:47 -0700 Subject: [PATCH 72/87] Back to snapshot dep --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 630e60ac94..22bb420b94 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fasterxml.jackson jackson-base - 2.19.2 + 2.19.3-SNAPSHOT com.fasterxml.jackson.core jackson-databind From 6babf5c189c7885d8e6c16bfdc2eff42c3dfdded Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 1 Aug 2025 10:31:15 -0700 Subject: [PATCH 73/87] Backport junit5 fix from 2.x to 2.19 --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 22bb420b94..876a0428d9 100644 --- a/pom.xml +++ b/pom.xml @@ -166,13 +166,12 @@ org.junit.platform junit-platform-suite-engine - 1.10.2 test io.micronaut.test micronaut-test-type-pollution - 4.6.2 + 4.8.1 test From 92799657d8ba8b7c026d178f8babd8da0a4e575f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 4 Aug 2025 20:01:29 -0700 Subject: [PATCH 74/87] Add NPE handling wrt DeserializationContext.getParser() (#5245) --- .../databind/deser/std/StdDeserializer.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java index 77a8e76e27..4fa863bee1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java @@ -763,7 +763,7 @@ protected final int _parseIntPrimitive(DeserializationContext ctxt, String text) { try { if (text.length() > 9) { - ctxt.getParser().streamReadConstraints().validateIntegerLength(text.length()); + _streamReadConstraints(ctxt).validateIntegerLength(text.length()); long l = NumberInput.parseLong(text); if (_intOverflow(l)) { Number v = (Number) ctxt.handleWeirdStringValue(Integer.TYPE, text, @@ -837,7 +837,7 @@ protected final Integer _parseInteger(DeserializationContext ctxt, String text) { try { if (text.length() > 9) { - ctxt.getParser().streamReadConstraints().validateIntegerLength(text.length()); + _streamReadConstraints(ctxt).validateIntegerLength(text.length()); long l = NumberInput.parseLong(text); if (_intOverflow(l)) { return (Integer) ctxt.handleWeirdStringValue(Integer.class, text, @@ -916,7 +916,7 @@ protected final long _parseLongPrimitive(JsonParser p, DeserializationContext ct */ protected final long _parseLongPrimitive(DeserializationContext ctxt, String text) throws IOException { - ctxt.getParser().streamReadConstraints().validateIntegerLength(text.length()); + _streamReadConstraints(ctxt).validateIntegerLength(text.length()); try { return NumberInput.parseLong(text); } catch (IllegalArgumentException iae) { } @@ -982,7 +982,7 @@ protected final Long _parseLong(JsonParser p, DeserializationContext ctxt, */ protected final Long _parseLong(DeserializationContext ctxt, String text) throws IOException { - ctxt.getParser().streamReadConstraints().validateIntegerLength(text.length()); + _streamReadConstraints(ctxt).validateIntegerLength(text.length()); try { return NumberInput.parseLong(text); } catch (IllegalArgumentException iae) { } @@ -1069,7 +1069,7 @@ protected final float _parseFloatPrimitive(DeserializationContext ctxt, String t { // 09-Dec-2023, tatu: To avoid parser having to validate input, pre-validate: if (NumberInput.looksLikeValidNumber(text)) { - ctxt.getParser().streamReadConstraints().validateFPLength(text.length()); + _streamReadConstraints(ctxt).validateFPLength(text.length()); try { return NumberInput.parseFloat(text, false); } catch (IllegalArgumentException iae) { } @@ -1087,7 +1087,7 @@ protected final float _parseFloatPrimitive(JsonParser p, DeserializationContext { // 09-Dec-2023, tatu: To avoid parser having to validate input, pre-validate: if (NumberInput.looksLikeValidNumber(text)) { - ctxt.getParser().streamReadConstraints().validateFPLength(text.length()); + p.streamReadConstraints().validateFPLength(text.length()); try { return NumberInput.parseFloat(text, p.isEnabled(StreamReadFeature.USE_FAST_DOUBLE_PARSER)); } catch (IllegalArgumentException iae) { } @@ -2328,4 +2328,11 @@ protected Number _nonNullNumber(Number n) { } return n; } + + // @since 2.19.3: NPE check for older code that doesn't get JsonParser + protected StreamReadConstraints _streamReadConstraints(DeserializationContext ctxt) { + JsonParser p = ctxt.getParser(); + // 29-Jun-2020, tatu: Should not be null, but let's be defensive + return (p == null) ? StreamReadConstraints.defaults() : p.streamReadConstraints(); + } } From d90d157da7c254d10b957d33525205a72a49cc74 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 4 Aug 2025 20:25:27 -0700 Subject: [PATCH 75/87] Javadoc improvements wrt #5211 --- .../jackson/databind/ObjectMapper.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 431e68d013..22476b66dc 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -1502,19 +1502,25 @@ public ObjectMapper setSerializerProvider(DefaultSerializerProvider p) { } /** - * Accessor for the "blueprint" (or, factory) instance, from which instances - * are created by calling {@link DefaultSerializerProvider#createInstance}. - * Note that returned instance cannot be directly used as it is not properly - * configured: to get a properly configured instance to call, use - * {@link #getSerializerProviderInstance()} instead. + * Internal {@link SerializerProvider} accessor used by databind package to get the "blueprint" + * (or, factory) instance, from which actual instances are created by calling {@link DefaultSerializerProvider#createInstance}. + * NOT TO BE USED BY APPLICATION CODE directly: only databind-managed {@code SerializerProvider} + * instances should be used. + *

+ * Implementation note: returned instance cannot be directly used as it is not properly + * configured or initialized. */ public SerializerProvider getSerializerProvider() { return _serializerProvider; } /** - * Accessor for constructing and returning a {@link SerializerProvider} - * instance that may be used for accessing serializers. This is same as + * Internal {@link SerializerProvider} accessor used by databind package to get a + * properly initialized instance to pass to various handlers. + * NOT TO BE USED BY APPLICATION CODE directly: only databind-managed {@code SerializerProvider} + * instances should be used. + *

+ * Implementation note: this is same as * calling {@link #getSerializerProvider}, and calling {@code createInstance()} * on it. * From 49633742b448529e98b6c45ce4087bb1153bd43e Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 4 Aug 2025 21:07:12 -0700 Subject: [PATCH 76/87] Javadoc improvements wrt #5211 --- .../jackson/databind/ObjectMapper.java | 2 ++ .../databind/cfg/ContextAttributes.java | 35 ++++++++++++++++++- .../jackson/databind/cfg/MapperBuilder.java | 2 ++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 22476b66dc..0c85a61f38 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -2549,6 +2549,8 @@ public ObjectMapper setTimeZone(TimeZone tz) { * construction, see {@link com.fasterxml.jackson.databind.json.JsonMapper#builder} * (and {@link MapperBuilder#defaultAttributes}). * + * @see ContextAttributes for details on setting default attributes. + * * @since 2.13 */ public ObjectMapper setDefaultAttributes(ContextAttributes attrs) { diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java index b4715fae55..9e682baa00 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java @@ -5,6 +5,7 @@ /** * Helper class used for storing and accessing per-call attributes. * Storage is two-layered: at higher precedence, we have actual per-call + * ({@code ObjectMapper.readValue} or {@code ObjectMapper.writeValue}) * attributes; and at lower precedence, default attributes that may be * defined for Object readers and writers. *

@@ -15,11 +16,24 @@ * sharing, by creating new copies instead of modifying state. * This allows sharing of default values without per-call copying, but * requires two-level lookup on access. + *

+ * To set default attributes, use {@link #withSharedAttributes(Map)} + * or {@link #withSharedAttribute(Object, Object)}, starting with + * "empty" instance (see {@link #getEmpty()}). For example: + *

+ *   ContextAttributes attrs = ContextAttributes.getEmpty()
+ *     .withSharedAttribute("foo", "bar")
+ *     .withSharedAttribute("attr2", "value2");
+ *
* * @since 2.3 */ public abstract class ContextAttributes { + /** + * Accessor for an empty instance of {@link ContextAttributes}: usable + * as-is, or as a starting point for building up a set of default attributes. + */ public static ContextAttributes getEmpty() { return Impl.getEmpty(); } @@ -30,8 +44,27 @@ public static ContextAttributes getEmpty() { /********************************************************** */ + /** + * Fluent factory method for creating a new instance with an additional + * shared attribute. + * + * @param key Name of the attribute to add + * @param value Value of the attribute to add; may be null, in which case + * attribute will be removed if it already exists. + * + * @return New instance of {@link ContextAttributes} that has specified change. + */ public abstract ContextAttributes withSharedAttribute(Object key, Object value); + /** + * Fluent factory method for creating a new instance with specified set of + * shared attributes. + * Any shared attributes that already exist will be replaced + * + * @param attributes Map of shared attributes to add, replacing any existing ones. + * + * @return New instance of {@link ContextAttributes} that has specified shared attributes. + */ public abstract ContextAttributes withSharedAttributes(Map attributes); public abstract ContextAttributes withoutSharedAttribute(Object key); @@ -115,7 +148,7 @@ public ContextAttributes withSharedAttribute(Object key, Object value) Map m; // need to cover one special case, since EMPTY uses Immutable map: if (this == EMPTY) { - m = new HashMap(8); + m = new HashMap<>(8); } else { m = _copy(_shared); } diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java index 04dd3bac55..fe8df72414 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java @@ -452,6 +452,8 @@ public B annotationIntrospector(AnnotationIntrospector intr) { * @param attrs Default instance to use: may not be {@code null}. * * @return This Builder instance to allow call chaining + * + * @seee ContextAttributes for details on constructing default attributes. */ public B defaultAttributes(ContextAttributes attrs) { _mapper.setDefaultAttributes(attrs); From cc54617c744ba455b74f85edeec8e75f981ba350 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 4 Aug 2025 21:12:17 -0700 Subject: [PATCH 77/87] 3 javadoc fixes --- src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java | 2 +- .../java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java | 2 +- .../com/fasterxml/jackson/databind/module/SimpleModule.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 0c85a61f38..b6e3dabd68 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -2318,7 +2318,7 @@ public ObjectMapper setConstructorDetector(ConstructorDetector cd) { /** * Method for specifying {@link CacheProvider} instance, to provide Cache instances to be used in components downstream. * - * @cacheProvider Cache provider for this mapper to use + * @param cacheProvider Cache provider for this mapper to use * * @throws IllegalArgumentException if given provider is null * diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java index fe8df72414..088ad48296 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java @@ -453,7 +453,7 @@ public B annotationIntrospector(AnnotationIntrospector intr) { * * @return This Builder instance to allow call chaining * - * @seee ContextAttributes for details on constructing default attributes. + * @see ContextAttributes for details on constructing default attributes. */ public B defaultAttributes(ContextAttributes attrs) { _mapper.setDefaultAttributes(attrs); diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java index bd110ba9dc..262f618c2a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java +++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java @@ -25,7 +25,7 @@ * NOTE: that [de]serializers are registered as "default" [de]serializers. * As a result, they will have lower priority than the ones indicated through annotations on * both Class and property-associated annotations -- for example, - * {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize}.
+ * {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize}.
* In cases where both module-based [de]serializers and annotation-based [de]serializers are registered, * the [de]serializer specified by the annotation will take precedence. *

From 09b82dce595a2cec9dfd21960d3eba57c7996501 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 25 Aug 2025 16:26:13 -0700 Subject: [PATCH 78/87] Fix parts of 2.16 CI (#5278) Alas, publishing of snapshots for deps would be required for full working --- .github/workflows/main.yml | 2 +- pom.xml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8be3ae1f97..b8f308c377 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: java_version: ['8', '11', '17', '21'] - os: ['ubuntu-20.04'] + os: ['ubuntu-latest'] env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: diff --git a/pom.xml b/pom.xml index 7c15108414..1508348701 100644 --- a/pom.xml +++ b/pom.xml @@ -147,10 +147,11 @@ + - sonatype-nexus-snapshots - Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots + central-snapshots + Sonatype Central Portal (snapshots) + https://central.sonatype.com/repository/maven-snapshots false true From 449509b89ddcc2bfb7c865f780e0b26f56e9528d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Aug 2025 09:44:23 -0700 Subject: [PATCH 79/87] Backport #5280 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1508348701..913a32d151 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ com.google.guava guava-testlib - 31.1-jre + 32.0.1-jre test From f1a80bf475c61b128912c6133a0abbbea452f20c Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Aug 2025 09:44:48 -0700 Subject: [PATCH 80/87] Manual backport of #5280 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 737ba70cd1..23851a6353 100644 --- a/pom.xml +++ b/pom.xml @@ -107,7 +107,7 @@ com.google.guava guava-testlib - 31.1-jre + 32.0.1-jre test From 814ce0806292f8421cad809d39af2cb91873836c Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Aug 2025 09:45:13 -0700 Subject: [PATCH 81/87] Manual backport of #5280 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eeb7b6d060..80cbee0de9 100644 --- a/pom.xml +++ b/pom.xml @@ -123,7 +123,7 @@ com.google.guava guava-testlib - 31.1-jre + 32.0.1-jre test From 6980e2e5728bdef37361f80f06c815657a991a51 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Aug 2025 09:45:50 -0700 Subject: [PATCH 82/87] Manual backport of #5280 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 876a0428d9..1003d96021 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ com.google.guava guava-testlib - 31.1-jre + 32.0.1-jre test From ee9240cc325d9f9871d8574c1fbabdd885082459 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Aug 2025 17:52:25 -0700 Subject: [PATCH 83/87] Minor test improvement --- .../fasterxml/jackson/databind/ser/EnumAsMapKeyTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/EnumAsMapKeyTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/EnumAsMapKeyTest.java index f04bffc48a..59b3d771e8 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/EnumAsMapKeyTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/EnumAsMapKeyTest.java @@ -47,6 +47,7 @@ static enum MyEnum594 { static class MyStuff594 { public Map stuff = new EnumMap(MyEnum594.class); + protected MyStuff594() { } public MyStuff594(String value) { stuff.put(MyEnum594.VALUE_WITH_A_REALLY_LONG_NAME_HERE, value); } @@ -136,8 +137,11 @@ public void testCustomEnumMapKeySerializer() throws Exception { // [databind#594] @Test public void testJsonValueForEnumMapKey() throws Exception { - assertEquals(a2q("{'stuff':{'longValue':'foo'}}"), - MAPPER.writeValueAsString(new MyStuff594("foo"))); + final String JSON = a2q("{'stuff':{'longValue':'foo'}}"); + assertEquals(JSON, MAPPER.writeValueAsString(new MyStuff594("foo"))); + MyStuff594 result = MAPPER.readValue(JSON, MyStuff594.class); + assertNotNull(result); + assertEquals("foo", result.stuff.get(MyEnum594.VALUE_WITH_A_REALLY_LONG_NAME_HERE)); } // [databind#2129] From 88401156ab7d459b4ce3cbc1c454869a0bfb86d8 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sun, 31 Aug 2025 09:01:12 +0900 Subject: [PATCH 84/87] Add more documentation (#5284) --- src/main/java/com/fasterxml/jackson/databind/JsonNode.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java index 19fe1ef6dc..290a9057ef 100644 --- a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java @@ -491,7 +491,9 @@ public boolean canConvertToExactIntegral() { * Does NOT do any conversions for non-String value nodes; * for non-String values (ones for which {@link #isTextual} returns * false) null will be returned. - * For String values, null is never returned (but empty Strings may be) + * For String values, null is never returned (but empty Strings may be). + * + * IMPORTANT : Starting Jackson 3.0, this method will be named as {@code stringValue()}. * * @return Textual value this node contains, iff it is a textual * JSON node (comes from JSON String value entry) From 1595f0b8a84d2785bbeead7038f371813f51fa0d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 2 Sep 2025 12:01:45 -0700 Subject: [PATCH 85/87] Update release notes wrt #3328 / [CVE-2021-46877] link --- release-notes/VERSION-2.x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 5ef5db9941..9c094a4735 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -29,7 +29,7 @@ Project: jackson-databind #3305: ObjectMapper serializes `CharSequence` subtypes as POJO instead of as String (JDK 15+) (reported by stevenupton@github; fix suggested by Sergey C) -#3328: Possible DoS if using JDK serialization to serialize JsonNode +#3328: Possible DoS if using JDK serialization to serialize JsonNode [CVE-2021-46877] 2.12.5 (27-Aug-2021) From 7ea33722f7d16bc89a1ce676c87e9e850d473aca Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Wed, 3 Sep 2025 12:36:51 +0900 Subject: [PATCH 86/87] Reproduce 5281 (#5286) --- .../tofix/ReaderForUpdating5281Test.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/tofix/ReaderForUpdating5281Test.java diff --git a/src/test/java/com/fasterxml/jackson/databind/tofix/ReaderForUpdating5281Test.java b/src/test/java/com/fasterxml/jackson/databind/tofix/ReaderForUpdating5281Test.java new file mode 100644 index 0000000000..7b86af890f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/tofix/ReaderForUpdating5281Test.java @@ -0,0 +1,46 @@ +package com.fasterxml.jackson.databind.tofix; + +import com.fasterxml.jackson.annotation.JsonMerge; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import static org.assertj.core.api.Assertions.assertThat; + +// [databind#5281] Reading into existing instance uses creator property setup instead of accessor #5281 +public class ReaderForUpdating5281Test + extends DatabindTestUtil +{ + public static class ArrayListHolder { + // Works when annotated with... + // @JsonMerge + Collection values; + + public ArrayListHolder(String... values) { + this.values = new ArrayList<>(); + this.values.addAll(Arrays.asList(values)); + } + + public void setValues(Collection values) { + this.values = values; + } + } + + @JacksonTestFailureExpected + @Test + public void readsIntoCreator() throws Exception { + ObjectMapper mapper = JsonMapper.builder().build(); + + ArrayListHolder holder = mapper.readerForUpdating(new ArrayListHolder("A")) + .readValue("{ \"values\" : [ \"A\", \"B\" ]}"); + + assertThat(holder.values).hasSize(3) + .containsAll(Arrays.asList("A", "A", "B")); + } +} From 184e3834dc1f5cb6617c603009176ebba8e1ffea Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Tue, 9 Sep 2025 12:50:25 +0530 Subject: [PATCH 87/87] Renamed method to readObject for 3.x --- .../fasterxml/jackson/databind/ObjectMapper.java | 13 ++----------- .../jackson/databind/ObjectMapperTest.java | 2 +- .../databind/deser/JacksonTypesDeserTest.java | 10 +++++----- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index abf009feef..2c2dee418b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -3072,7 +3072,7 @@ public T readValue(JsonParser p, Class valueType) * @return the deserialized object * @throws JsonProcessingException if there is a problem processing the JSON */ - public T readValue(JsonParser p, T... reified) throws IOException { + public T readObject(JsonParser p, T... reified) throws IOException { if (reified.length > 0) { throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); } @@ -3900,7 +3900,7 @@ private static Class getClassOf(T[] array) { * @return the deserialized object * @throws JsonProcessingException if there is a problem processing the JSON */ - public T readValue(String content, T... reified) throws JsonProcessingException { + public T readObject(String content, T... reified) throws JsonProcessingException { if (reified.length > 0) { throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); } @@ -4010,15 +4010,6 @@ public T readValue(byte[] src, int offset, int len, return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType)); } - @SuppressWarnings("unchecked") - public T readValue(byte[] src, int offset, int len, T... reified) throws IOException { - if (reified.length > 0) { - throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); - } - - return readValue(src, offset, len, _typeFactory.constructType(getClassOf(reified))); - } - @SuppressWarnings({ "unchecked" }) public T readValue(byte[] src, TypeReference valueTypeRef) throws IOException, StreamReadException, DatabindException diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java index ef5198e84a..3dd48db6db 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java @@ -391,7 +391,7 @@ public void testAutoDetectClasses() throws Exception ObjectMapper m = new ObjectMapper(); final String JSON = "{ \"x\" : 3 }"; - Bean bean = m.readValue(JSON); + Bean bean = m.readObject(JSON); assertNotNull(bean); } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java index a55440f1d9..45b3cc8ce2 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java @@ -94,7 +94,7 @@ public void testTokenBufferWithSequenceWithAutoDetectClass() throws Exception assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); - TokenBuffer buf = mapper.readValue(p); + TokenBuffer buf = mapper.readObject(p); // check manually... JsonParser bufParser = buf.asParser(); @@ -103,7 +103,7 @@ public void testTokenBufferWithSequenceWithAutoDetectClass() throws Exception assertNull(bufParser.nextToken()); // then bind to another - buf = mapper.readValue(p); + buf = mapper.readObject(p); bufParser = buf.asParser(); assertToken(JsonToken.START_ARRAY, bufParser.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken()); @@ -112,13 +112,13 @@ public void testTokenBufferWithSequenceWithAutoDetectClass() throws Exception assertNull(bufParser.nextToken()); // third one, with automatic binding - buf = mapper.readValue(p); - String str = mapper.readValue(buf.asParser()); + buf = mapper.readObject(p); + String str = mapper.readObject(buf.asParser()); assertEquals("abc", str); // and ditto for last one buf = mapper.readValue(p, TokenBuffer.class); - Map map = mapper.readValue(buf.asParser()); + Map map = mapper.readObject(buf.asParser()); assertEquals(1, map.size()); assertEquals(Boolean.TRUE, map.get("a"));