From 5bedc10cdc0ad5da50d9f7e42a7f726cf3ab4e38 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 6 Oct 2025 14:46:59 +0100 Subject: [PATCH 1/5] Create RecordWithOptionalParamTest.java --- .../records/RecordWithOptionalParamTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java diff --git a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java new file mode 100644 index 0000000000..c8188aa7c0 --- /dev/null +++ b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java @@ -0,0 +1,57 @@ +package tools.jackson.databind.records; + +import org.junit.jupiter.api.Test; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.testutil.DatabindTestUtil; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; + +// [databind#53355] When deserializing records, java.util.Optional parameters should be not be +// deserialized as null (Optional.empty() should be used instead) +public class RecordWithOptionalParamTest + extends DatabindTestUtil +{ + record RecordWithOptionalParam(String name, Optional optional) { } + + private final ObjectMapper MAPPER = newJsonMapper(); + + @Test + void serialize() throws Exception + { + RecordWithOptionalParam input = new RecordWithOptionalParam("v1", Optional.of("v2")); + String json = MAPPER.writeValueAsString(input); + String expected = a2q("{'name':'v1','optional':'v2'}"); + assertEquals(expected, json); + } + + @Test + void serializeEmpty() throws Exception + { + RecordWithOptionalParam input = new RecordWithOptionalParam("v1", Optional.empty()); + String json = MAPPER.writeValueAsString(input); + String expected = a2q("{'name':'v1','optional':null}"); + assertEquals(expected, json); + } + + @Test + void deserializeNonEmpty() throws Exception + { + String json = a2q("{'name':'v1','optional':'v2'}"); + RecordWithOptionalParam output = MAPPER.readValue(json, RecordWithOptionalParam.class); + assertEquals("v1", output.name()); + assertEquals(Optional.of("v2"), output.optional()); + } + + @Test + void deserializeEmpty() throws Exception + { + String json = a2q("{'name':'v1','optional':null}"); + RecordWithOptionalParam output = MAPPER.readValue(json, RecordWithOptionalParam.class); + assertEquals("v1", output.name()); + assertNotNull(output.optional()); + assertEquals(Optional.empty(), output.optional()); + } +} From 9e28a23e9e6eb37c5f50d66dafb5fad0111e7762 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 6 Oct 2025 14:47:42 +0100 Subject: [PATCH 2/5] Update RecordWithOptionalParamTest.java --- .../jackson/databind/records/RecordWithOptionalParamTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java index c8188aa7c0..8d050af1fb 100644 --- a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java +++ b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertEquals; -// [databind#53355] When deserializing records, java.util.Optional parameters should be not be +// [databind#5335] When deserializing records, java.util.Optional parameters should be not be // deserialized as null (Optional.empty() should be used instead) public class RecordWithOptionalParamTest extends DatabindTestUtil From 2a4477d96bd38a595aaf35b35c37ff7394ff8b3c Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 6 Oct 2025 14:56:38 +0100 Subject: [PATCH 3/5] Update RecordWithOptionalParamTest.java --- .../records/RecordWithOptionalParamTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java index 8d050af1fb..c00fda643c 100644 --- a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java +++ b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java @@ -1,7 +1,9 @@ package tools.jackson.databind.records; +import com.fasterxml.jackson.annotation.JsonInclude; import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.testutil.DatabindTestUtil; import java.util.Optional; @@ -54,4 +56,19 @@ void deserializeEmpty() throws Exception assertNotNull(output.optional()); assertEquals(Optional.empty(), output.optional()); } + + @Test + void deserializeIssue5335() throws Exception + { + String json = a2q("{'name':'v1','optional':null}"); + JsonMapper mapper = JsonMapper.builder() + .changeDefaultPropertyInclusion( + v -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS)) + .build(); + RecordWithOptionalParam output = mapper.readValue(json, RecordWithOptionalParam.class); + assertEquals("v1", output.name()); + assertNotNull(output.optional()); + assertEquals(Optional.empty(), output.optional()); + } + } From d96278dead50b34a1595984c1a011c9f76f30c95 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 6 Oct 2025 15:51:55 +0100 Subject: [PATCH 4/5] Update RecordWithOptionalParamTest.java --- .../records/RecordWithOptionalParamTest.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java index c00fda643c..61ae5e0770 100644 --- a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java +++ b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java @@ -5,6 +5,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.testutil.DatabindTestUtil; +import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import java.util.Optional; @@ -48,7 +49,7 @@ void deserializeNonEmpty() throws Exception } @Test - void deserializeEmpty() throws Exception + void deserializeExplicitNull() throws Exception { String json = a2q("{'name':'v1','optional':null}"); RecordWithOptionalParam output = MAPPER.readValue(json, RecordWithOptionalParam.class); @@ -57,10 +58,22 @@ void deserializeEmpty() throws Exception assertEquals(Optional.empty(), output.optional()); } + @JacksonTestFailureExpected // [databind#5335] @Test - void deserializeIssue5335() throws Exception + void deserializeMissing() throws Exception { - String json = a2q("{'name':'v1','optional':null}"); + String json = a2q("{'name':'v1'}"); + RecordWithOptionalParam output = MAPPER.readValue(json, RecordWithOptionalParam.class); + assertEquals("v1", output.name()); + assertNotNull(output.optional()); + assertEquals(Optional.empty(), output.optional()); + } + + @JacksonTestFailureExpected // [databind#5335] + @Test + void deserializeIssue5335Config() throws Exception + { + String json = a2q("{'name':'v1'}"); JsonMapper mapper = JsonMapper.builder() .changeDefaultPropertyInclusion( v -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS)) From c94c1aef7182e3ff5dc2e811aff576ec30c03f13 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 13 Oct 2025 17:50:05 -0700 Subject: [PATCH 5/5] Simplify test a bit (remove serialization-side config) --- .../records/RecordWithOptionalParamTest.java | 36 +++++-------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java index 61ae5e0770..eb55e437d7 100644 --- a/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java +++ b/src/test/java/tools/jackson/databind/records/RecordWithOptionalParamTest.java @@ -1,14 +1,12 @@ package tools.jackson.databind.records; -import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.Optional; + import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.testutil.DatabindTestUtil; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; -import java.util.Optional; - import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -25,18 +23,16 @@ record RecordWithOptionalParam(String name, Optional optional) { } void serialize() throws Exception { RecordWithOptionalParam input = new RecordWithOptionalParam("v1", Optional.of("v2")); - String json = MAPPER.writeValueAsString(input); - String expected = a2q("{'name':'v1','optional':'v2'}"); - assertEquals(expected, json); + assertEquals(a2q("{'name':'v1','optional':'v2'}"), + MAPPER.writeValueAsString(input)); } @Test void serializeEmpty() throws Exception { RecordWithOptionalParam input = new RecordWithOptionalParam("v1", Optional.empty()); - String json = MAPPER.writeValueAsString(input); - String expected = a2q("{'name':'v1','optional':null}"); - assertEquals(expected, json); + assertEquals(a2q("{'name':'v1','optional':null}"), + MAPPER.writeValueAsString(input)); } @Test @@ -62,26 +58,10 @@ void deserializeExplicitNull() throws Exception @Test void deserializeMissing() throws Exception { - String json = a2q("{'name':'v1'}"); - RecordWithOptionalParam output = MAPPER.readValue(json, RecordWithOptionalParam.class); - assertEquals("v1", output.name()); - assertNotNull(output.optional()); - assertEquals(Optional.empty(), output.optional()); - } - - @JacksonTestFailureExpected // [databind#5335] - @Test - void deserializeIssue5335Config() throws Exception - { - String json = a2q("{'name':'v1'}"); - JsonMapper mapper = JsonMapper.builder() - .changeDefaultPropertyInclusion( - v -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS)) - .build(); - RecordWithOptionalParam output = mapper.readValue(json, RecordWithOptionalParam.class); + RecordWithOptionalParam output = MAPPER.readValue(a2q("{'name':'v1'}"), + RecordWithOptionalParam.class); assertEquals("v1", output.name()); assertNotNull(output.optional()); assertEquals(Optional.empty(), output.optional()); } - }