Skip to content

Commit 3ddbc97

Browse files
authored
Fix #4580: add MapperFeature.SORT_CREATOR_PROPERTIES_BY_DECLARATION_ORDER (#4587)
1 parent 7e98b36 commit 3ddbc97

File tree

6 files changed

+70
-10
lines changed

6 files changed

+70
-10
lines changed

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Project: jackson-databind
4848
`@JsonProperty` and javac `-parameters`
4949
(reported by Alexandre J)
5050
#4570: Deprecate `ObjectMapper.canDeserialize()`/`ObjectMapper.canSerialize()`
51+
#4580: Add `MapperFeature.SORT_CREATOR_PROPERTIES_BY_DECLARATION_ORDER` to use
52+
Creator properties' declaration order for sorting
5153

5254
2.17.2 (not yet released)
5355

src/main/java/com/fasterxml/jackson/databind/MapperFeature.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,23 @@ public enum MapperFeature implements ConfigFeature
417417
*/
418418
SORT_CREATOR_PROPERTIES_FIRST(true),
419419

420+
/**
421+
* Feature that defines whether Creator properties (ones passed through
422+
* constructor or static factory method) should be sorted in their declaration
423+
* order if {@link #SORT_CREATOR_PROPERTIES_FIRST} is also enabled.
424+
* This is usually used to prevent alphabetic sorting for
425+
* Creator properties even if {@link #SORT_PROPERTIES_ALPHABETICALLY} is
426+
* enabled for other types of properties.
427+
*<p>
428+
* NOTE: if {@link #SORT_CREATOR_PROPERTIES_FIRST} is disabled, this feature
429+
* has no effect.
430+
*<p>
431+
* Feature is disabled by default (for backwards compatibility)
432+
*
433+
* @since 2.18
434+
*/
435+
SORT_CREATOR_PROPERTIES_BY_DECLARATION_ORDER(false),
436+
420437
/*
421438
/******************************************************
422439
/* Name-related features

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,11 @@ protected void _sortProperties(Map<String, POJOPropertyBuilder> props)
15601560
* so creator properties still fully predate non-creator ones.
15611561
*/
15621562
Collection<POJOPropertyBuilder> cr;
1563-
if (sortAlpha) {
1563+
// 18-Jun-2024, tatu: [databind#4580] We may want to retain declaration
1564+
// order regardless
1565+
boolean sortCreatorPropsByAlpha = sortAlpha
1566+
&& !_config.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_BY_DECLARATION_ORDER);
1567+
if (sortCreatorPropsByAlpha) {
15641568
TreeMap<String, POJOPropertyBuilder> sorted =
15651569
new TreeMap<String,POJOPropertyBuilder>();
15661570
for (POJOPropertyBuilder prop : _creatorProperties) {

src/test-jdk17/java/com/fasterxml/jackson/databind/jdk17/RecordPrivate4175Test.java renamed to src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordPrivate4175Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.databind.jdk17;
1+
package com.fasterxml.jackson.databind.records;
22

33
import java.util.Collections;
44

src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordSerializationOrderTest.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
11
package com.fasterxml.jackson.databind.records;
22

3+
import org.junit.jupiter.api.Test;
4+
35
import com.fasterxml.jackson.annotation.JsonProperty;
46
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
7+
import com.fasterxml.jackson.databind.MapperFeature;
58
import com.fasterxml.jackson.databind.ObjectMapper;
69
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
7-
import org.junit.jupiter.api.Test;
810

911
import static org.junit.jupiter.api.Assertions.assertEquals;
1012

1113
public class RecordSerializationOrderTest extends DatabindTestUtil
1214
{
1315
record NestedRecordOne(String id, String email, NestedRecordTwo nestedRecordTwo) {}
1416
record NestedRecordOneWithJsonProperty(String id, String email,
15-
@JsonProperty("nestedProperty") NestedRecordTwo nestedRecordTwo) {}
17+
@JsonProperty("nestedProperty") NestedRecordTwo nestedRecordTwo) {}
1618
record NestedRecordOneWithJsonPropertyIndex(@JsonProperty(index = 2) String id,
17-
@JsonProperty(index = 0) String email,
18-
@JsonProperty(value = "nestedProperty", index = 1) NestedRecordTwo nestedRecordTwo) {}
19+
@JsonProperty(index = 0) String email,
20+
@JsonProperty(value = "nestedProperty", index = 1) NestedRecordTwo nestedRecordTwo) {}
1921

2022
@JsonPropertyOrder({"email", "nestedProperty", "id"})
2123
record NestedRecordOneWithJsonPropertyOrder(String id,
22-
String email,
23-
@JsonProperty(value = "nestedProperty") NestedRecordTwo nestedRecordTwo) {}
24+
String email,
25+
@JsonProperty(value = "nestedProperty") NestedRecordTwo nestedRecordTwo) {}
2426

2527
record NestedRecordTwo(String id, String passport) {}
2628

29+
record CABRecord(String c, String a, String b) {}
30+
2731
private final ObjectMapper MAPPER = newJsonMapper();
2832

2933
/*
@@ -70,4 +74,25 @@ public void testSerializationOrderWithJsonPropertyOrder() throws Exception {
7074
final String expected = "{\"email\":\"[email protected]\",\"nestedProperty\":{\"id\":\"2\",\"passport\":\"111110\"},\"id\":\"1\"}";
7175
assertEquals(expected, output);
7276
}
77+
78+
// [databind#4580]
79+
@Test
80+
public void testSerializationOrderWrtCreatorAlphabetic() throws Exception {
81+
// By default, in Creator property order
82+
assertEquals(a2q("{'c':'c','a':'a','b':'b'}"),
83+
MAPPER.writeValueAsString(new CABRecord("c", "a", "b")));
84+
// But alphabetic sorting affects Creator, without other settings
85+
assertEquals(a2q("{'a':'a','b':'b','c':'c'}"),
86+
jsonMapperBuilder()
87+
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
88+
.build()
89+
.writeValueAsString(new CABRecord("c", "a", "b")));
90+
// Except if we tell it not to:
91+
assertEquals(a2q("{'c':'c','a':'a','b':'b'}"),
92+
jsonMapperBuilder()
93+
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
94+
.enable(MapperFeature.SORT_CREATOR_PROPERTIES_BY_DECLARATION_ORDER)
95+
.build()
96+
.writeValueAsString(new CABRecord("c", "a", "b")));
97+
}
7398
}

src/test/java/com/fasterxml/jackson/databind/ser/SerializationOrderTest.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,20 @@ public void testCreatorVsExplicitOrdering() throws Exception
205205
@Test
206206
public void testAlphaAndCreatorOrdering() throws Exception
207207
{
208-
String json = ALPHA_MAPPER.writeValueAsString(new BeanForGH311(2, 1));
209-
assertEquals("{\"a\":1,\"b\":2}", json);
208+
assertEquals(a2q("{'a':1,'b':2}"),
209+
ALPHA_MAPPER.writeValueAsString(new BeanForGH311(2, 1)));
210+
}
211+
212+
// [databind#4580]
213+
@Test
214+
public void testAlphaAndCreatorDeclarationOrdering() throws Exception
215+
{
216+
final ObjectMapper mapper = jsonMapperBuilder()
217+
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
218+
.enable(MapperFeature.SORT_CREATOR_PROPERTIES_BY_DECLARATION_ORDER)
219+
.build();
220+
assertEquals(a2q("{'b':2,'a':1}"),
221+
mapper.writeValueAsString(new BeanForGH311(2, 1)));
210222
}
211223

212224
// [databind#2555]

0 commit comments

Comments
 (0)