Skip to content

Commit 10ab566

Browse files
committed
Refactor #2879 slightly, feature now MapperFeature.SORT_CREATOR_PROPERTIES_FIRST
1 parent 75bd749 commit 10ab566

File tree

7 files changed

+78
-64
lines changed

7 files changed

+78
-64
lines changed

release-notes/CREDITS-2.x

+5
Original file line numberDiff line numberDiff line change
@@ -1239,3 +1239,8 @@ Siavash Soleymani (siavashsoleymani@github)
12391239
Ilya Golovin (ilgo0413@github)
12401240
* Contributed #2873: `MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS` should work for enum as keys
12411241
(2.12.0)
1242+
1243+
Sergiy Yevtushenko (siy@github)
1244+
* Contributed #2879: Add support for disabling special handling of "Creator properties" wrt
1245+
alphabetic property ordering
1246+
(2.12.0)

release-notes/VERSION-2.x

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Project: jackson-databind
2121
#2855: Add `JsonNode.canConvertToExactIntegral()` to indicate whether floating-point/BigDecimal
2222
values could be converted to integers losslessly
2323
(requested by Oguzhan U; implementation contributed by Siavash S)
24+
#2879: Add support for disabling special handling of "Creator properties" wrt
25+
alphabetic property ordering
26+
(contributed by Sergiy Y)
2427

2528
2.12.0-rc1 (12-Oct-2020)
2629

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

+17-12
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,8 @@ public enum MapperFeature implements ConfigFeature
360360

361361
/**
362362
* Feature that defines default property serialization order used
363-
* for POJO fields (note: does <b>not</b> apply to {@link java.util.Map}
364-
* serialization!):
365-
* if enabled, default ordering is alphabetic (similar to
363+
* for POJO properties.
364+
* If enabled, default ordering is alphabetic (similar to
366365
* how {@link com.fasterxml.jackson.annotation.JsonPropertyOrder#alphabetic()}
367366
* works); if disabled, order is unspecified (based on what JDK gives
368367
* us, which may be declaration order, but is not guaranteed).
@@ -371,23 +370,29 @@ public enum MapperFeature implements ConfigFeature
371370
* explicit overrides in classes (for example with
372371
* {@link com.fasterxml.jackson.annotation.JsonPropertyOrder} annotation)
373372
*<p>
373+
* Note: does <b>not</b> apply to {@link java.util.Map} serialization (since
374+
* entries are not considered Bean/POJO properties.
375+
*<p>
374376
* Feature is disabled by default.
375377
*/
376378
SORT_PROPERTIES_ALPHABETICALLY(false),
377379

378380
/**
379-
* Feature that enforces strict ordering as requested by other configuration methods
380-
* for POJO fields (note: does <b>not</b> apply to {@link java.util.Map}
381-
* serialization!):
382-
* if enabled, ordering is preserved even if {@link com.fasterxml.jackson.annotation.JsonCreator}
383-
* is present. Without this feature properties referenced by {@link com.fasterxml.jackson.annotation.JsonCreator}
384-
* taking precedence over other properties even if sorting is requested.
381+
* Feature that defines whether Creator properties (ones passed through
382+
* constructor or static factory method) should be sorted before other properties
383+
* for which no explicit order is specified, in case where alphabetic
384+
* ordering is to be used for such properties.
385+
* Note that in either case explicit order (whether by name or by index)
386+
* will have precedence over this setting.
385387
*<p>
386-
* Note that if ordering is not enabled using other ways, this feature has no effect.
388+
* Note: does <b>not</b> apply to {@link java.util.Map} serialization (since
389+
* entries are not considered Bean/POJO properties.
387390
*<p>
388-
* Feature is disabled by default.
391+
* Feature is enabled by default.
392+
*
393+
* @since 2.12
389394
*/
390-
STRICT_PROPERTIES_ORDERING(false),
395+
SORT_CREATOR_PROPERTIES_FIRST(true),
391396

392397
/*
393398
/******************************************************

src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java

-8
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,6 @@ public final boolean shouldSortPropertiesAlphabetically() {
188188
return isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
189189
}
190190

191-
/**
192-
* Accessor for checking whether default settings for forcing property
193-
* ordering is enabled.
194-
*/
195-
public final boolean shouldPreservePropertiesOrdering() {
196-
return isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING);
197-
}
198-
199191
/**
200192
* Accessor for checking whether configuration indicates that
201193
* "root wrapping" (use of an extra property/name pair at root level)

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

+6-5
Original file line numberDiff line numberDiff line change
@@ -1052,21 +1052,21 @@ protected void _sortProperties(Map<String, POJOPropertyBuilder> props)
10521052
// Then how about explicit ordering?
10531053
final AnnotationIntrospector intr = _annotationIntrospector;
10541054
Boolean alpha = intr.findSerializationSortAlphabetically(_classDef);
1055-
final boolean sort = (alpha == null)
1055+
final boolean sortAlpha = (alpha == null)
10561056
? _config.shouldSortPropertiesAlphabetically()
10571057
: alpha.booleanValue();
10581058
final boolean indexed = _anyIndexed(props.values());
10591059

10601060
String[] propertyOrder = intr.findSerializationPropertyOrder(_classDef);
10611061

10621062
// no sorting? no need to shuffle, then
1063-
if (!sort && !indexed && (_creatorProperties == null) && (propertyOrder == null)) {
1063+
if (!sortAlpha && !indexed && (_creatorProperties == null) && (propertyOrder == null)) {
10641064
return;
10651065
}
10661066
int size = props.size();
10671067
Map<String, POJOPropertyBuilder> all;
10681068
// Need to (re)sort alphabetically?
1069-
if (sort) {
1069+
if (sortAlpha) {
10701070
all = new TreeMap<String,POJOPropertyBuilder>();
10711071
} else {
10721072
all = new LinkedHashMap<String,POJOPropertyBuilder>(size+size);
@@ -1116,14 +1116,15 @@ protected void _sortProperties(Map<String, POJOPropertyBuilder> props)
11161116

11171117
// Third by sorting Creator properties before other unordered properties
11181118
// (unless strict ordering is requested)
1119-
if (_creatorProperties != null && !_config.shouldPreservePropertiesOrdering()) {
1119+
if ((_creatorProperties != null)
1120+
&& (!sortAlpha || _config.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST))) {
11201121
/* As per [databind#311], this is bit delicate; but if alphabetic ordering
11211122
* is mandated, at least ensure creator properties are in alphabetic
11221123
* order. Related question of creator vs non-creator is punted for now,
11231124
* so creator properties still fully predate non-creator ones.
11241125
*/
11251126
Collection<POJOPropertyBuilder> cr;
1126-
if (sort) {
1127+
if (sortAlpha) {
11271128
TreeMap<String, POJOPropertyBuilder> sorted =
11281129
new TreeMap<String,POJOPropertyBuilder>();
11291130
for (POJOPropertyBuilder prop : _creatorProperties) {

src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java

+6-9
Original file line numberDiff line numberDiff line change
@@ -278,24 +278,21 @@ public void testConfigForForcedPropertySorting() throws Exception
278278
ObjectMapper m = new ObjectMapper();
279279

280280
// sort-alphabetically is disabled by default:
281-
assertFalse(m.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
281+
assertTrue(m.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST));
282282
SerializationConfig sc = m.getSerializationConfig();
283-
assertFalse(sc.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
284-
assertFalse(sc.shouldPreservePropertiesOrdering());
283+
assertTrue(sc.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST));
285284
DeserializationConfig dc = m.getDeserializationConfig();
286-
assertFalse(dc.shouldPreservePropertiesOrdering());
285+
assertTrue(dc.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST));
287286

288287
// but when enabled, should be visible:
289288
m = jsonMapperBuilder()
290-
.enable(MapperFeature.STRICT_PROPERTIES_ORDERING)
289+
.disable(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST)
291290
.build();
292291
sc = m.getSerializationConfig();
293-
assertTrue(sc.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
294-
assertTrue(sc.shouldPreservePropertiesOrdering());
292+
assertFalse(sc.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST));
295293
dc = m.getDeserializationConfig();
296294
// and not just via SerializationConfig, but also via DeserializationConfig
297-
assertTrue(dc.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
298-
assertTrue(dc.shouldPreservePropertiesOrdering());
295+
assertFalse(dc.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST));
299296
}
300297

301298
public void testJsonFactoryLinkage()

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

+41-30
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,19 @@ public BeanForGH311(@JsonProperty("b") int b, @JsonProperty("a") int a) { //b an
8282
public int getB() { return b; }
8383
}
8484

85-
static class BeanForStrictOrdering {
86-
private final int a;
87-
private int b;
88-
private final int c;
85+
// We'll expect ordering of "FUBAR"
86+
@JsonPropertyOrder({ "f" })
87+
static class OrderingByIndexBean {
88+
public int r;
89+
public int a;
8990

90-
@JsonCreator
91-
public BeanForStrictOrdering(@JsonProperty("c") int c, @JsonProperty("a") int a) { //b and a are out of order, although alphabetic = true
92-
this.a = a;
93-
this.c = c;
94-
}
91+
@JsonProperty(index = 1)
92+
public int b;
9593

96-
public int getA() { return a; }
97-
public int getB() { return b; }
98-
public int getC() { return c; }
94+
@JsonProperty(index = 0)
95+
public int u;
96+
97+
public int f;
9998
}
10099

101100
// For [databind#2879]
@@ -115,19 +114,21 @@ public BeanFor2879(@JsonProperty("a") int a,
115114
}
116115
}
117116

118-
// We'll expect ordering of "FUBAR"
119-
@JsonPropertyOrder({ "f" })
120-
static class OrderingByIndexBean {
121-
public int r;
122-
public int a;
123-
124-
@JsonProperty(index = 1)
125-
public int b;
117+
// For [databind#2879]
118+
static class BeanForStrictOrdering {
119+
private final int a;
120+
private int b;
121+
private final int c;
126122

127-
@JsonProperty(index = 0)
128-
public int u;
123+
@JsonCreator
124+
public BeanForStrictOrdering(@JsonProperty("c") int c, @JsonProperty("a") int a) { //b and a are out of order, although alphabetic = true
125+
this.a = a;
126+
this.c = c;
127+
}
129128

130-
public int f;
129+
public int getA() { return a; }
130+
public int getB() { return b; }
131+
public int getC() { return c; }
131132
}
132133

133134
/*
@@ -142,11 +143,6 @@ static class OrderingByIndexBean {
142143
.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
143144
.build();
144145

145-
private final ObjectMapper STRICT_ALPHA_MAPPER = jsonMapperBuilder()
146-
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
147-
.enable(MapperFeature.STRICT_PROPERTIES_ORDERING)
148-
.build();
149-
150146
public void testImplicitOrderByCreator() throws Exception {
151147
assertEquals("{\"c\":1,\"a\":2,\"b\":0}",
152148
MAPPER.writeValueAsString(new BeanWithCreator(1, 2)));
@@ -209,9 +205,24 @@ public void testOrderByIndexEtc() throws Exception
209205
ALPHA_MAPPER.writeValueAsString(new OrderingByIndexBean()));
210206
}
211207

208+
// [databind#2879]: allow preventing Creator properties from overriding
209+
// alphabetic ordering
212210
public void testStrictAlphaAndCreatorOrdering() throws Exception
213211
{
214-
String json = STRICT_ALPHA_MAPPER.writeValueAsString(new BeanForStrictOrdering(1, 2));
215-
assertEquals("{\"a\":2,\"b\":0,\"c\":1}", json);
212+
// without changing defaults, creators are sorted before other properties
213+
// BUT are sorted within their own category
214+
assertTrue(ALPHA_MAPPER.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
215+
assertTrue(ALPHA_MAPPER.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST));
216+
assertEquals(a2q("{'a':3,'c':2,'b':0}"),
217+
ALPHA_MAPPER.writeValueAsString(new BeanForStrictOrdering(2, 3)));
218+
219+
// but can change that
220+
final ObjectMapper STRICT_ALPHA_MAPPER = jsonMapperBuilder()
221+
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
222+
.disable(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST)
223+
.build();
224+
225+
assertEquals(a2q("{'a':2,'b':0,'c':1}"),
226+
STRICT_ALPHA_MAPPER.writeValueAsString(new BeanForStrictOrdering(1, 2)));
216227
}
217228
}

0 commit comments

Comments
 (0)