From 4e7257c8dfbda526d52a617384195ec81b1a8408 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 21 Jun 2025 20:51:17 +0900 Subject: [PATCH 01/10] Add failing tests for ObjectArrayDeserializer wrt #5165 --- .../jdk/ObjectArrayDeserializer5165Test.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java 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..898796e201 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java @@ -0,0 +1,57 @@ +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.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.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// For [databind#5165] +public class ObjectArrayDeserializer5165Test +{ + static class Dst { + private Integer[] array; + + public Integer[] getArray() { + return array; + } + + public void setArray(Integer[] array) { + this.array = array; + } + } + + @Test + public void nullsFailTest() { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL)) + .build(); + + assertThrows( + AssertionFailedError.class, + () -> assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) + ), + "databind#5165 for ObjectArrayDeserializer is fixed" + ); + } + + @Test + public void nullsSkipTest() throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"array\":[\"\"]}", new TypeReference() {}); + + assertNotEquals(0, dst.getArray().length, "databind#5165 for ObjectArrayDeserializer is fixed"); + } +} From 7d74e719e54b7564bbf83e1bf9430abf4ecdcfdd Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 21 Jun 2025 21:22:28 +0900 Subject: [PATCH 02/10] Fix #5165 on ObjectArrayDeserializer ref: https://github.com/FasterXML/jackson-databind/pull/5140/files#diff-692175612209d50652f1d14ffd325eb70cfdd282a6277f994207bfc63c0db515 --- .../deser/std/ObjectArrayDeserializer.java | 34 ++++++++++++++++--- .../jdk/ObjectArrayDeserializer5165Test.java | 13 +++---- 2 files changed, 34 insertions(+), 13 deletions(-) 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/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java index 898796e201..c4e007eaef 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java @@ -8,9 +8,8 @@ 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.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; // For [databind#5165] @@ -35,12 +34,8 @@ public void nullsFailTest() { .build(); assertThrows( - AssertionFailedError.class, - () -> assertThrows( - InvalidNullException.class, - () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) - ), - "databind#5165 for ObjectArrayDeserializer is fixed" + InvalidNullException.class, + () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) ); } @@ -52,6 +47,6 @@ public void nullsSkipTest() throws Exception { Dst dst = mapper.readValue("{\"array\":[\"\"]}", new TypeReference() {}); - assertNotEquals(0, dst.getArray().length, "databind#5165 for ObjectArrayDeserializer is fixed"); + assertEquals(0, dst.getArray().length, "Null values should be skipped"); } } From d1b4ba332ebd128e1b6360361d3bce87b7b82bfe Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 21 Jun 2025 21:08:19 +0900 Subject: [PATCH 03/10] Add failing tests for StringArrayDeserializer wrt #5165 --- .../jdk/StringArrayDeserializer5165Test.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java 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..9b4cd04357 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java @@ -0,0 +1,93 @@ +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.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 org.opentest4j.AssertionFailedError; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// For [databind#5165] +public class StringArrayDeserializer5165Test +{ + static class Dst { + private String[] array; + + public String[] getArray() { + return array; + } + + public void setArray(String[] array) { + this.array = 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(); + module.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( + AssertionFailedError.class, + () -> assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) + ), + "databind#5165 for StringArrayDeserializer is fixed" + ); + } + + @Test + public void nullsSkipTest() throws Exception { + SimpleModule module = new SimpleModule(); + module.addDeserializer(String.class, new EmptyStringToNullDeserializer()); + + ObjectMapper mapper = JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"array\":[\"\"]}", new TypeReference() {}); + + assertNotEquals(0, dst.getArray().length, "databind#5165 for StringArrayDeserializer is fixed"); + } +} From bdc4df0ef5fb2395d8dad36625f60b8686098b63 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 21 Jun 2025 21:24:18 +0900 Subject: [PATCH 04/10] Fix #5165 on StringArrayDeserializer ref: https://github.com/FasterXML/jackson-databind/pull/5140/files#diff-692175612209d50652f1d14ffd325eb70cfdd282a6277f994207bfc63c0db515 --- .../deser/std/StringArrayDeserializer.java | 29 +++++++++++++++++-- .../jdk/StringArrayDeserializer5165Test.java | 13 +++------ 2 files changed, 30 insertions(+), 12 deletions(-) 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/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java index 9b4cd04357..8d65e86c86 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java @@ -14,9 +14,8 @@ import com.fasterxml.jackson.databind.exc.InvalidNullException; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import org.opentest4j.AssertionFailedError; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; // For [databind#5165] @@ -67,12 +66,8 @@ public void nullsFailTest() { ObjectMapper mapper = createMapperWithCustomDeserializer(); assertThrows( - AssertionFailedError.class, - () -> assertThrows( - InvalidNullException.class, - () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) - ), - "databind#5165 for StringArrayDeserializer is fixed" + InvalidNullException.class, + () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) ); } @@ -88,6 +83,6 @@ public void nullsSkipTest() throws Exception { Dst dst = mapper.readValue("{\"array\":[\"\"]}", new TypeReference() {}); - assertNotEquals(0, dst.getArray().length, "databind#5165 for StringArrayDeserializer is fixed"); + assertEquals(0, dst.getArray().length, "Null values should be skipped"); } } From 6f98b24ca1a0a28ac07f423da9be2620c6ed1eaa Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 21 Jun 2025 21:19:13 +0900 Subject: [PATCH 05/10] Add failing tests for StringArrayDeserializer wrt #5165 --- .../StringCollectionDeserializer5165Test.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java 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..b0aaf8441f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java @@ -0,0 +1,94 @@ +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.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 org.opentest4j.AssertionFailedError; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// For [databind#5165] +public class StringCollectionDeserializer5165Test +{ + static class Dst { + private List list; + + public List getList() { + return list; + } + + public void setList(List list) { + this.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(); + module.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( + AssertionFailedError.class, + () -> assertThrows( + InvalidNullException.class, + () -> mapper.readValue("{\"list\":[\"\"]}", new TypeReference(){}) + ), + "databind#5165 for StringCollectionDeserializer is fixed" + ); + } + + @Test + public void nullsSkipTest() throws Exception { + SimpleModule module = new SimpleModule(); + module.addDeserializer(String.class, new EmptyStringToNullDeserializer()); + + ObjectMapper mapper = JsonMapper.builder() + .addModule(module) + .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .build(); + + Dst dst = mapper.readValue("{\"list\":[\"\"]}", new TypeReference() {}); + + assertFalse(dst.getList().isEmpty(), "databind#5165 for StringCollectionDeserializer is fixed"); + } +} From 0cff12356463b07eee47e3bed37dfc9fa2b100cf Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 21 Jun 2025 21:28:51 +0900 Subject: [PATCH 06/10] Fix #5165 on StringCollectionDeserializer ref: https://github.com/FasterXML/jackson-databind/pull/5140/files#diff-692175612209d50652f1d14ffd325eb70cfdd282a6277f994207bfc63c0db515 --- .../std/StringCollectionDeserializer.java | 32 +++++++++++++++++-- .../StringCollectionDeserializer5165Test.java | 13 +++----- 2 files changed, 33 insertions(+), 12 deletions(-) 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/StringCollectionDeserializer5165Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java index b0aaf8441f..8a93eb2fc9 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java @@ -15,10 +15,9 @@ import com.fasterxml.jackson.databind.exc.InvalidNullException; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import org.opentest4j.AssertionFailedError; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; // For [databind#5165] public class StringCollectionDeserializer5165Test @@ -68,12 +67,8 @@ public void nullsFailTest() { ObjectMapper mapper = createMapperWithCustomDeserializer(); assertThrows( - AssertionFailedError.class, - () -> assertThrows( - InvalidNullException.class, - () -> mapper.readValue("{\"list\":[\"\"]}", new TypeReference(){}) - ), - "databind#5165 for StringCollectionDeserializer is fixed" + InvalidNullException.class, + () -> mapper.readValue("{\"list\":[\"\"]}", new TypeReference(){}) ); } @@ -89,6 +84,6 @@ public void nullsSkipTest() throws Exception { Dst dst = mapper.readValue("{\"list\":[\"\"]}", new TypeReference() {}); - assertFalse(dst.getList().isEmpty(), "databind#5165 for StringCollectionDeserializer is fixed"); + assertTrue(dst.getList().isEmpty(), "Null values should be skipped"); } } From 6b0c5f2f1ff5eeba1f951c317430cf128aa0773a Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 21 Jun 2025 21:33:13 +0900 Subject: [PATCH 07/10] Add failing tests for EnumSetDeserializer wrt #5165 --- .../tofix/EnumSetDeserializer5165Test.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/tofix/EnumSetDeserializer5165Test.java 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 9a819be82938038c076c7b53a344cdf7202d2924 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 9 Jul 2025 16:35:12 -0700 Subject: [PATCH 08/10] Minor simplification of tests --- .../jdk/ObjectArrayDeserializer5165Test.java | 18 +++++------------- .../jdk/StringArrayDeserializer5165Test.java | 19 ++++++------------- .../StringCollectionDeserializer5165Test.java | 17 ++++------------- 3 files changed, 15 insertions(+), 39 deletions(-) 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 index c4e007eaef..d80734bfc8 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java @@ -4,7 +4,7 @@ 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; @@ -16,15 +16,7 @@ public class ObjectArrayDeserializer5165Test { static class Dst { - private Integer[] array; - - public Integer[] getArray() { - return array; - } - - public void setArray(Integer[] array) { - this.array = array; - } + public Integer[] array; } @Test @@ -35,7 +27,7 @@ public void nullsFailTest() { assertThrows( InvalidNullException.class, - () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) + () -> mapper.readValue("{\"array\":[\"\"]}", Dst.class) ); } @@ -45,8 +37,8 @@ public void nullsSkipTest() throws Exception { .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) .build(); - Dst dst = mapper.readValue("{\"array\":[\"\"]}", new TypeReference() {}); + Dst dst = mapper.readValue("{\"array\":[\"\"]}", Dst.class); - assertEquals(0, dst.getArray().length, "Null values should be skipped"); + 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 index 8d65e86c86..294f4b9c4a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java @@ -6,8 +6,9 @@ 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; @@ -22,15 +23,7 @@ public class StringArrayDeserializer5165Test { static class Dst { - private String[] array; - - public String[] getArray() { - return array; - } - - public void setArray(String[] array) { - this.array = array; - } + public String[] array; } // Custom deserializer that converts empty strings to null @@ -67,7 +60,7 @@ public void nullsFailTest() { assertThrows( InvalidNullException.class, - () -> mapper.readValue("{\"array\":[\"\"]}", new TypeReference(){}) + () -> mapper.readValue("{\"array\":[\"\"]}", Dst.class) ); } @@ -81,8 +74,8 @@ public void nullsSkipTest() throws Exception { .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) .build(); - Dst dst = mapper.readValue("{\"array\":[\"\"]}", new TypeReference() {}); + Dst dst = mapper.readValue("{\"array\":[\"\"]}", Dst.class); - assertEquals(0, dst.getArray().length, "Null values should be skipped"); + 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 index 8a93eb2fc9..f328f05e29 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java @@ -8,7 +8,6 @@ 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; @@ -23,15 +22,7 @@ public class StringCollectionDeserializer5165Test { static class Dst { - private List list; - - public List getList() { - return list; - } - - public void setList(List list) { - this.list = list; - } + public List list; } // Custom deserializer that converts empty strings to null @@ -68,7 +59,7 @@ public void nullsFailTest() { assertThrows( InvalidNullException.class, - () -> mapper.readValue("{\"list\":[\"\"]}", new TypeReference(){}) + () -> mapper.readValue("{\"list\":[\"\"]}", Dst.class) ); } @@ -82,8 +73,8 @@ public void nullsSkipTest() throws Exception { .defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) .build(); - Dst dst = mapper.readValue("{\"list\":[\"\"]}", new TypeReference() {}); + Dst dst = mapper.readValue("{\"list\":[\"\"]}", Dst.class); - assertTrue(dst.getList().isEmpty(), "Null values should be skipped"); + assertTrue(dst.list.isEmpty(), "Null values should be skipped"); } } From a77672226c510014cf4ae266b772053bf2f0aa1d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 9 Jul 2025 16:38:00 -0700 Subject: [PATCH 09/10] Add comments, streamline --- .../deser/jdk/ObjectArrayDeserializer5165Test.java | 3 ++- .../deser/jdk/StringArrayDeserializer5165Test.java | 8 ++++---- .../deser/jdk/StringCollectionDeserializer5165Test.java | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) 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 index d80734bfc8..7e77004fb1 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/ObjectArrayDeserializer5165Test.java @@ -25,6 +25,7 @@ public void nullsFailTest() { .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) @@ -38,7 +39,7 @@ public void nullsSkipTest() throws Exception { .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 index 294f4b9c4a..fd18fa68ba 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringArrayDeserializer5165Test.java @@ -45,8 +45,8 @@ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx } private ObjectMapper createMapperWithCustomDeserializer() { - SimpleModule module = new SimpleModule(); - module.addDeserializer(String.class, new EmptyStringToNullDeserializer()); + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); return JsonMapper.builder() .addModule(module) @@ -66,8 +66,8 @@ public void nullsFailTest() { @Test public void nullsSkipTest() throws Exception { - SimpleModule module = new SimpleModule(); - module.addDeserializer(String.class, new EmptyStringToNullDeserializer()); + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); ObjectMapper mapper = JsonMapper.builder() .addModule(module) 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 index f328f05e29..11cbf28229 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/StringCollectionDeserializer5165Test.java @@ -44,8 +44,8 @@ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx } private ObjectMapper createMapperWithCustomDeserializer() { - SimpleModule module = new SimpleModule(); - module.addDeserializer(String.class, new EmptyStringToNullDeserializer()); + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); return JsonMapper.builder() .addModule(module) @@ -65,8 +65,8 @@ public void nullsFailTest() { @Test public void nullsSkipTest() throws Exception { - SimpleModule module = new SimpleModule(); - module.addDeserializer(String.class, new EmptyStringToNullDeserializer()); + SimpleModule module = new SimpleModule() + .addDeserializer(String.class, new EmptyStringToNullDeserializer()); ObjectMapper mapper = JsonMapper.builder() .addModule(module) From 9b7c4ea6d9e5eccb7635c988edcd6c91dd13853d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 9 Jul 2025 16:42:52 -0700 Subject: [PATCH 10/10] Add release notes --- release-notes/CREDITS-2.x | 3 +++ release-notes/VERSION-2.x | 3 +++ 2 files changed, 6 insertions(+) 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)