117

I'm using ObjectMapper to do my java-json mapping.

ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
ow.writeValue(new File( fileName +".json"), jsonObj);

this is my java class:

public class Relation {

private String id;
private String source;
private String target;
private String label;
private List<RelAttribute> attributes;

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getSource() {
    return source;
}

public void setSource(String source) {
    this.source = source;
}

public String getTarget() {
    return target;
}

public void setTarget(String target) {
    this.target = target;
}

public String getLabel() {
    return label;
}
public void setLabel(String label) {
    this.label = label;
}

public void setAttributes(List<RelAttribute> attributes) {
    this.attributes = attributes;
}

public List<RelAttribute> getAttributes() {
    return attributes;
}

}

this is what I get:

{
    "id" : "-75da69d3-79c8-4000-a3d8-b10350a57a7e",
    "attributes" : [ {
      "attrName" : "ID",
      "attrValue" : ""
    }, {
      "attrName" : "Description",
      "attrValue" : "Primary Actor"
    }, {
      "attrName" : "Status",
      "attrValue" : ""
    } ],
    "label" : "new Label",
    "target" : "-46b238ac-b8b3-4230-b32c-be9707f8b691",
    "source" : "-daa34638-061a-45e0-9f2e-35afd6c271e0"
  }

So my question now is, how can I get this json output:

{
    "id" : "-75da69d3-79c8-4000-a3d8-b10350a57a7e",
    "label" : "new Label",
    "target" : "-46b238ac-b8b3-4230-b32c-be9707f8b691",
    "source" : "-daa34638-061a-45e0-9f2e-35afd6c271e0",
    "attributes" : [ {
      "attrName" : "ID",
      "attrValue" : ""
    }, {
      "attrName" : "Description",
      "attrValue" : "Primary Actor"
    }, {
      "attrName" : "Status",
      "attrValue" : ""
    } ]

  }

I want it with same order as in my java declaration. Is there a way to specify it ? Maybe with annotations or stuff like that ?

malana
  • 5,045
  • 3
  • 28
  • 41
justSaid
  • 1,540
  • 2
  • 12
  • 23
  • 3
    The property order being significant points to a code/design smell. Whatever is consuming the JSON shouldn't care about the order (lists yes, properties no). – ach Oct 09 '13 at 13:08
  • 35
    I just need it for the sake of readability :-) – justSaid Oct 09 '13 at 13:26
  • 13
    @ach that's just not true. An ordered map is a very common, and very useful, data type. Desiring order is not a smell at all. – goat Feb 13 '17 at 21:34
  • 1
    No its not a code/design smell. Consider a scrnario where you sign/verify json data. – yerlilbilgin Dec 01 '20 at 11:15
  • 2
    @Honza Zidek My comment was 7 years ago but I can only imagine it was directed towards interoperability (which the second sentence supports as being the purpose of the comment). I still stand by that -- there's a reason that the JSON spec provides no requirements for property ordering, and neither does JSON schema AFAIR. Ordered properties for readability is reasonable. – ach Jan 25 '21 at 00:58
  • @ach: "**Whatever** is consuming the JSON shouldn't care about the order" - no, humans also belong to the "whatever" is consuming the JSON. And yes, humans *do* care about the order :) Your comment is factually invalid. – Honza Zidek Jan 25 '21 at 09:44
  • 1
    @ach if you're creating a checksum of the JSON and using that to know if something has changed then yes, order is critical. Running the same code twice in a row is giving different ordering, so the checksum erroneously indicates that the JSON is different. Same issues with signing. – johncurrier Apr 09 '21 at 15:52

6 Answers6

244
@JsonPropertyOrder({ "id", "label", "target", "source", "attributes" })
public class Relation { ... }
Andrey Atapin
  • 7,745
  • 3
  • 28
  • 34
28

Do you know there is a convenient way to specify alphabetic ordering?

@JsonPropertyOrder(alphabetic = true)
public class Relation { ... }

If you have specific requirements, here how you configure custom ordering:

@JsonPropertyOrder({ "id", "label", "target", "source", "attributes" })
public class Relation { ... }
naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
  • Just what I wanted! Not for any requirement but for ease of reading :) – trebor Feb 16 '22 at 17:29
  • What if I am using also the annotation @JsonInclude(value = Include.NON_NULL), and some of the attributes I specified in the JsonPropertyOrder annotation are null, they just get skipped? For example what if "target" is null, I would expect to get the json with "id", "label", "source", "attributes". – BugsOverflow Jun 26 '23 at 06:53
  • @BugsOverflow I don't know. I recommend you to [ask your question](https://stackoverflow.com/questions/ask) on StackOverflow. – naXa stands with Ukraine Jun 26 '23 at 11:20
20

The ordering of fields within a generated .class is indeterminate, so you can't count on that.

If you want specific ordering per class then you'll need to use the one of the approaches specified in other answers.

If you want everything to default to alphabetical ordering (e.g. for consistency in how the JSON is structured) then you can configure the ObjectMapper like this:

ObjectMapper mapper = new ObjectMapper();
mapper.setConfig(mapper.getSerializationConfig()
   .with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));

For more consistent JSON consider also adding:

   .with(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)

One advantage of this approach is that you don't have to modify each class being serialized.

johncurrier
  • 348
  • 3
  • 7
12

I discovered a third way today in case alphabetic is not your desired sorting order. It turns out adding a @JsonProperty annotation on a field places it last when writing. I discovered that when I wanted to specify a property name which did not conform to java naming conventions.

By Adding an index attribute you can define the order. Lowest index is placed first.

@JsonProperty(index=20)
String prop1;

@JsonProperty(index=10)
String prop2;

Would render:

{"prop2": "valueProp2", "prop1": "valueProp1"}
Rythmic
  • 759
  • 2
  • 10
  • 25
2

You can use @XmlAccessorType(XmlAccessType.FIELD)

@XmlType(name = "response", propOrder = { "prop1", "prop2",
        "prop3", "prop4", "prop5", "prop6" }).

@JsonPropertyOrder requires a new jar to be added.

CSchulz
  • 10,882
  • 11
  • 60
  • 114
Prasad
  • 1,089
  • 13
  • 21
  • 3
    @JsonPropertyOrder does not requires a new jar because if you use jackson its already included. – Patrick Jun 07 '16 at 07:30
  • 1
    It depends on the framework which you are using.If you dont use jackson then it better to go with above implementation which will apply for both json and xml and no need extra configuration if you like to return data in xml format – Prasad Jun 07 '16 at 11:14
  • Yes, but the question was asked by using jackson. If you want to provide a solution without using jackson, you have to be more sepcific in your answer. – Patrick Jun 07 '16 at 11:24
0

As per this documentation, you can configure Jackson2ObjectMapperBuilder globally. This class is available in spring-web dependency.

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
      Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
      builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
      return builder;
}

Also, you can use @JsonProperty(index) to determine the order in inherited classes as well.

class animal {
    @JsonProperty(index=2)
    int p1;
    @JsonProperty(index=3)
    int p2;
}

class cat extends animal{
    @JsonProperty(index=1)
    int p3;
}
Praj
  • 451
  • 2
  • 5