Skip to content

Commit

Permalink
Add support for strict property ordering (#2879)
Browse files Browse the repository at this point in the history
  • Loading branch information
siy authored Oct 24, 2020
1 parent 207b7f6 commit 75bd749
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 5 deletions.
14 changes: 14 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,20 @@ public enum MapperFeature implements ConfigFeature
*/
SORT_PROPERTIES_ALPHABETICALLY(false),

/**
* Feature that enforces strict ordering as requested by other configuration methods
* for POJO fields (note: does <b>not</b> apply to {@link java.util.Map}
* serialization!):
* if enabled, ordering is preserved even if {@link com.fasterxml.jackson.annotation.JsonCreator}
* is present. Without this feature properties referenced by {@link com.fasterxml.jackson.annotation.JsonCreator}
* taking precedence over other properties even if sorting is requested.
*<p>
* Note that if ordering is not enabled using other ways, this feature has no effect.
*<p>
* Feature is disabled by default.
*/
STRICT_PROPERTIES_ORDERING(false),

/*
/******************************************************
/* Name-related features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ public final boolean shouldSortPropertiesAlphabetically() {
return isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
}

/**
* Accessor for checking whether default settings for forcing property
* ordering is enabled.
*/
public final boolean shouldPreservePropertiesOrdering() {
return isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING);
}

/**
* Accessor for checking whether configuration indicates that
* "root wrapping" (use of an extra property/name pair at root level)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,8 @@ protected void _sortProperties(Map<String, POJOPropertyBuilder> props)
}

// Third by sorting Creator properties before other unordered properties
if (_creatorProperties != null) {
// (unless strict ordering is requested)
if (_creatorProperties != null && !_config.shouldPreservePropertiesOrdering()) {
/* As per [databind#311], this is bit delicate; but if alphabetic ordering
* is mandated, at least ensure creator properties are in alphabetic
* order. Related question of creator vs non-creator is punted for now,
Expand Down
34 changes: 30 additions & 4 deletions src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public void testCopy() throws Exception
assertTrue(m.isEnabled(JsonParser.Feature.IGNORE_UNDEFINED));

// // First: verify that handling of features is decoupled:

ObjectMapper m2 = m.copy();
assertFalse(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
m2.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Expand Down Expand Up @@ -215,7 +215,7 @@ public void testFailedCopy() throws Exception
}
}

public void testAnnotationIntrospectorCopyin()
public void testAnnotationIntrospectorCopying()
{
ObjectMapper m = new ObjectMapper();
m.setAnnotationIntrospector(new MyAnnotationIntrospector());
Expand Down Expand Up @@ -272,6 +272,32 @@ public void testConfigForPropertySorting() throws Exception
assertTrue(dc.shouldSortPropertiesAlphabetically());
}

// Test to ensure that we can check forced property ordering defaults...
public void testConfigForForcedPropertySorting() throws Exception
{
ObjectMapper m = new ObjectMapper();

// sort-alphabetically is disabled by default:
assertFalse(m.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
SerializationConfig sc = m.getSerializationConfig();
assertFalse(sc.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
assertFalse(sc.shouldPreservePropertiesOrdering());
DeserializationConfig dc = m.getDeserializationConfig();
assertFalse(dc.shouldPreservePropertiesOrdering());

// but when enabled, should be visible:
m = jsonMapperBuilder()
.enable(MapperFeature.STRICT_PROPERTIES_ORDERING)
.build();
sc = m.getSerializationConfig();
assertTrue(sc.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
assertTrue(sc.shouldPreservePropertiesOrdering());
dc = m.getDeserializationConfig();
// and not just via SerializationConfig, but also via DeserializationConfig
assertTrue(dc.isEnabled(MapperFeature.STRICT_PROPERTIES_ORDERING));
assertTrue(dc.shouldPreservePropertiesOrdering());
}

public void testJsonFactoryLinkage()
{
// first, implicit factory, giving implicit linkage
Expand All @@ -284,7 +310,7 @@ public void testJsonFactoryLinkage()
assertSame(m, f.getCodec());
}

public void testProviderConfig() throws Exception
public void testProviderConfig() throws Exception
{
ObjectMapper m = new ObjectMapper();
final String JSON = "{ \"x\" : 3 }";
Expand Down Expand Up @@ -332,7 +358,7 @@ public void testCustomDefaultPrettyPrinter() throws Exception
assertEquals("[1,2]", m.writer().without(SerializationFeature.INDENT_OUTPUT)
.writeValueAsString(input));
}

// For [databind#703], [databind#978]
public void testNonSerializabilityOfObject()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ public BeanForGH311(@JsonProperty("b") int b, @JsonProperty("a") int a) { //b an
public int getB() { return b; }
}

static class BeanForStrictOrdering {
private final int a;
private int b;
private final int c;

@JsonCreator
public BeanForStrictOrdering(@JsonProperty("c") int c, @JsonProperty("a") int a) { //b and a are out of order, although alphabetic = true
this.a = a;
this.c = c;
}

public int getA() { return a; }
public int getB() { return b; }
public int getC() { return c; }
}

// For [databind#2879]
@JsonPropertyOrder({ "a", "c" })
static class BeanFor2879 {
Expand Down Expand Up @@ -126,6 +142,11 @@ static class OrderingByIndexBean {
.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
.build();

private final ObjectMapper STRICT_ALPHA_MAPPER = jsonMapperBuilder()
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
.enable(MapperFeature.STRICT_PROPERTIES_ORDERING)
.build();

public void testImplicitOrderByCreator() throws Exception {
assertEquals("{\"c\":1,\"a\":2,\"b\":0}",
MAPPER.writeValueAsString(new BeanWithCreator(1, 2)));
Expand Down Expand Up @@ -187,4 +208,10 @@ public void testOrderByIndexEtc() throws Exception
assertEquals(aposToQuotes("{'f':0,'u':0,'b':0,'a':0,'r':0}"),
ALPHA_MAPPER.writeValueAsString(new OrderingByIndexBean()));
}

public void testStrictAlphaAndCreatorOrdering() throws Exception
{
String json = STRICT_ALPHA_MAPPER.writeValueAsString(new BeanForStrictOrdering(1, 2));
assertEquals("{\"a\":2,\"b\":0,\"c\":1}", json);
}
}

0 comments on commit 75bd749

Please sign in to comment.