From 40c97391ba61773922c6d6f8ea21e5fe59c20cf6 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 15 May 2023 23:32:27 +0100 Subject: [PATCH] Json property affects Record field serialization order (#3929) --- .../introspect/POJOPropertiesCollector.java | 7 +- .../records/RecordSerializationOrderTest.java | 66 +++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.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 a2893650f0..40df20e693 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -437,17 +437,18 @@ protected void collectAll() LinkedHashMap props = new LinkedHashMap(); // First: gather basic data - + final boolean isRecord = isRecordType(); // 15-Jan-2023, tatu: [databind#3736] Let's avoid detecting fields of Records // altogether (unless we find a good reason to detect them) // 17-Apr-2023: Need Records' fields for serialization for cases like [databind#3895] & [databind#3628] - if (!isRecordType() || _forSerialization) { + if (!isRecord || _forSerialization) { _addFields(props); // note: populates _fieldRenameMappings } _addMethods(props); // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static // inner classes, see [databind#1502] - if (!_classDef.isNonStaticInnerClass()) { + // 13-May-2023, PJ: Need to avoid adding creators for Records when serializing [databind#3925] + if (!_classDef.isNonStaticInnerClass() && !(_forSerialization && isRecord)) { _addCreators(props); } diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java new file mode 100644 index 0000000000..494904c3e9 --- /dev/null +++ b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.records; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class RecordSerializationOrderTest extends BaseMapTest +{ + record NestedRecordOne(String id, String email, NestedRecordTwo nestedRecordTwo) {} + record NestedRecordOneWithJsonProperty(String id, String email, + @JsonProperty("nestedProperty") NestedRecordTwo nestedRecordTwo) {} + record NestedRecordOneWithJsonPropertyIndex(@JsonProperty(index = 2) String id, + @JsonProperty(index = 0) String email, + @JsonProperty(value = "nestedProperty", index = 1) NestedRecordTwo nestedRecordTwo) {} + + @JsonPropertyOrder({"email", "nestedProperty", "id"}) + record NestedRecordOneWithJsonPropertyOrder(String id, + String email, + @JsonProperty(value = "nestedProperty") NestedRecordTwo nestedRecordTwo) {} + + record NestedRecordTwo(String id, String passport) {} + + private final ObjectMapper MAPPER = newJsonMapper(); + + /* + /********************************************************************** + /* Test methods, alternate constructors + /********************************************************************** + */ + + public void testSerializationOrder() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOne nestedRecordOne = new NestedRecordOne("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"id\":\"1\",\"email\":\"test@records.com\",\"nestedRecordTwo\":{\"id\":\"2\",\"passport\":\"111110\"}}"; + assertEquals(expected, output); + } + + public void testSerializationOrderWithJsonProperty() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOneWithJsonProperty nestedRecordOne = + new NestedRecordOneWithJsonProperty("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"id\":\"1\",\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"}}"; + assertEquals(expected, output); + } + + public void testSerializationOrderWithJsonPropertyIndexes() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOneWithJsonPropertyIndex nestedRecordOne = + new NestedRecordOneWithJsonPropertyIndex("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"},\"id\":\"1\"}"; + assertEquals(expected, output); + } + + public void testSerializationOrderWithJsonPropertyOrder() throws Exception { + NestedRecordTwo nestedRecordTwo = new NestedRecordTwo("2", "111110"); + NestedRecordOneWithJsonPropertyOrder nestedRecordOne = + new NestedRecordOneWithJsonPropertyOrder("1", "test@records.com", nestedRecordTwo); + final String output = MAPPER.writeValueAsString(nestedRecordOne); + final String expected = "{\"email\":\"test@records.com\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"},\"id\":\"1\"}"; + assertEquals(expected, output); + } +}