Jackson ObjectMapper - specify serialization order of object properties

JavaJsonSerializationJackson

Java Problem Overview


I'm implementing a RESTful web service where user has to send a signed verification token along with the request so that I could ensure that the request has not been tampered by a middle man. My current implementation is as follows.

Verification token is a VerifData object serialized into a String and then hashed and encrypted.

class VerifData {
    int prop1;
    int prop2;
}

In my service, I put data to be serialized into an instance of VerifData and then serialize it using Jackson ObjectMapper and passed along to the verification engine along with the verification token.

VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);

But it seems that each time the application container is started, the order of properties being mapped into a string by ObjectMapper changes.

Ex: one time it would be

{"prop1":12345,"prop2":67890}

and another time it would be

{"prop2":67890,"prop1":12345}

So if client has serialized the VerifData instance as into the first String, there is 50% chance of it being failed even though it is correct.

Is there a way to get around this? Can I specify the order of properties to map by ObjectMapper (like in ascending order)? Or is there any other way to best implement this verification step. Both client and server implementations are developed by me. I use Java Security API for signing and verifying.

Java Solutions


Solution 1 - Java

The annotations are useful, but can be a pain to apply everywhere. You can configure your whole ObjectMapper to work this way with

Current Jackson versions:

objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)

Older Jackson versions:

objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

Solution 2 - Java

From the Jackson Annotations documentation:

// ensure that "id" and "name" are output before other properties
@JsonPropertyOrder({ "id", "name" })

// order any properties that don't have explicit setting using alphabetic order
@JsonPropertyOrder(alphabetic=true)

Solution 3 - Java

The following 2 ObjectMapper configuration are required:

ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
or
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
> defines the property serialization order used for POJO fields
Note: does not apply to java.util.Map serialization!

and

ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
or
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
> Feature that determines whether java.util.Map entries are first sorted by key before serialization


Spring Boot config example (yaml):

spring:
  jackson:
    mapper:
      SORT_PROPERTIES_ALPHABETICALLY: true
    serialization:
      ORDER_MAP_ENTRIES_BY_KEYS: true

Solution 4 - Java

In Spring Boot you can add this behaviour globally by adding the following to your Application entry point class:

  @Bean
  public Jackson2ObjectMapperBuilder objectMapperBuilder() {

    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

    return builder;
  }

Solution 5 - Java

There is an easier way in Spring Boot by specifying a property (in application.properties for example:

spring.jackson.mapper.sort_properties_alphabetically=true

Solution 6 - Java

In Jackson 2.x, which you are probably using today, use:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

If you care about looks, you may also consider SerializationFeature.INDENT_OUTPUT as well.

Note that you must serialize Maps or Objects for this to sort correctly. If you serialize a JsonNode for example (from readTree), that won't be properly indented.

Example
import com.fasterxml.jackson.databind.*;

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

String input = "{\"hello\": {\"cruel\" : \"world\"} }";
Object pojo = mapper.readValue(input, Object.class);
System.out.println(mapper.writeValueAsString(pojo));

results in:

{
  "hello" : {
    "cruel" : "world"
  }
}

Solution 7 - Java

From Duncan McGregor's answer: Its better to use it like this:

objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

as MapperFeature is for XMLs and comes with jackson-databind which is not required...

Solution 8 - Java

I realize this is an old thread, but since I was looking or an answer and landed here, some additional info could be handy for other people.
The @JsonProperty annotation I am using currently (jackson-annotations-2.11.2) accepts, besides the "value" argument, an "index" (numeric) argument that specifies the order of the fields during serialization.

Solution 9 - Java

I discovered yet another 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 if the rest of the fields are not annotated. 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"}

Solution 10 - Java

Instead of using flag argument:

objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

Solution 11 - Java

You can use mix-in and specify the order of properties as you like:

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public final class ObjectMapperUtils {

	private static final ObjectMapper MAPPER = new ObjectMapper();

	static {
		MAPPER.addMixIn(Object.class, IdFirst.class);
	}

	@Bean
	public ObjectMapper objectMapper() {
		return MAPPER;
	}

	@JsonPropertyOrder({"id", "...", "..."})
	private abstract static class IdFirst {}

}

Solution 12 - Java

As @Gary Rowe mentioned, we can use Jackson2ObjectMapperBuilder to sort the properties globally. However for this to work, you must have Jackson2ObjectMapperBuilder in your classpath. It is not part of the Jackson library.

As per this documentation, spring-web dependency has Jackson2ObjectMapperBuilder file and should be in your classpath.

@Bean
  public Jackson2ObjectMapperBuilder objectMapperBuilder() {

    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

    return builder;
  }

You can refer to this for other possible solutions

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLizzyView Question on Stackoverflow
Solution 1 - JavaDuncan McGregorView Answer on Stackoverflow
Solution 2 - JavawgitschtView Answer on Stackoverflow
Solution 3 - JavamagiccrafterView Answer on Stackoverflow
Solution 4 - JavaGary RoweView Answer on Stackoverflow
Solution 5 - JavaWim DeblauweView Answer on Stackoverflow
Solution 6 - Javauser2089674View Answer on Stackoverflow
Solution 7 - JavaNemes NisanoView Answer on Stackoverflow
Solution 8 - JavaPaulNView Answer on Stackoverflow
Solution 9 - JavaRythmicView Answer on Stackoverflow
Solution 10 - JavaLeonView Answer on Stackoverflow
Solution 11 - JavaMarcus Vinícius VoltolimView Answer on Stackoverflow
Solution 12 - JavaPrajView Answer on Stackoverflow