diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
index 572f6392a0..4e5e88b971 100644
--- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
@@ -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 not 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.
+ *
+ * Note that if ordering is not enabled using other ways, this feature has no effect.
+ *
+ * Feature is disabled by default.
+ */
+ STRICT_PROPERTIES_ORDERING(false),
+
/*
/******************************************************
/* Name-related features
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
index e17f7d3bf7..5c5d0c6e0f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
@@ -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)
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 695ff38129..ee01876f80 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
@@ -1115,7 +1115,8 @@ protected void _sortProperties(Map 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,
diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
index 1434eea3b0..0044db1f8b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
@@ -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);
@@ -215,7 +215,7 @@ public void testFailedCopy() throws Exception
}
}
- public void testAnnotationIntrospectorCopyin()
+ public void testAnnotationIntrospectorCopying()
{
ObjectMapper m = new ObjectMapper();
m.setAnnotationIntrospector(new MyAnnotationIntrospector());
@@ -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
@@ -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 }";
@@ -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()
{
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/SerializationOrderTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationOrderTest.java
index 67807c56f7..045b70e7aa 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/SerializationOrderTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationOrderTest.java
@@ -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 {
@@ -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)));
@@ -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);
+ }
}