Jackson ObjectMapper: especifique el orden de serialización de las propiedades del objeto

Estoy implementando un servicio web RESTful donde el usuario tiene que enviar un token de verificación firmado junto con la solicitud para que yo pueda asegurarme de que la solicitud no haya sido manipulada por un intermediario. Mi implementación actual es la siguiente.

El token de verificación es un objeto VerifData serializado en un String y luego hash y encriptado.

class VerifData { int prop1; int prop2; } 

En mi servicio, pongo los datos a ser serializados en una instancia de VerifData y luego los serializo usando Jackson ObjectMapper y los transfiero al motor de verificación junto con el token de verificación.

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

Pero parece que cada vez que se inicia el contenedor de la aplicación, cambia el orden de las propiedades que están siendo mapeadas en una cadena por ObjectMapper.

Ej .: una vez sería

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

y en otro momento sería

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

Por lo tanto, si el cliente ha serializado la instancia de VerifData como en la primera Cadena, existe un 50% de probabilidad de que se produzca un error aunque sea correcta.

¿Hay alguna forma de evitar esto? ¿Puedo especificar el orden de las propiedades para mapear por ObjectMapper (como en orden ascendente)? ¿O hay otra forma de implementar mejor este paso de verificación? Ambas implementaciones de cliente y servidor son desarrolladas por mí. Uso Java Security API para firmar y verificar.

De la documentación de Jackson Annotations :

 // 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) 

Las anotaciones son útiles, pero puede ser difícil aplicarlas en todas partes. Puede configurar todo su ObjectMapper para que funcione de esta manera con

Versiones actuales de Jackson: objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)

Versiones anteriores de Jackson: objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

En Spring Boot puede agregar este comportamiento de forma global agregando lo siguiente a su clase de punto de entrada de la Application :

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

Hay una manera más fácil en Spring Boot especificando una propiedad (en application.properties por ejemplo:

 spring.jackson.mapper.sort_properties_alphabetically=true 

De la respuesta de Duncan McGregor: Mejor usarlo así:

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

como MapperFeature es para XML y viene con jackson-databind que no es necesario …

En Jackson 2.x, que probablemente esté usando hoy, use:

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

Si le interesan las miradas, también puede considerar SerializationFeature.INDENT_OUTPUT también.

Tenga en cuenta que debe serializar Mapas u Objetos para que esto se ordene correctamente. Si serializas un JsonNode por ejemplo (desde readTree ), eso no se readTree adecuadamente.

Ejemplo

 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)); 

resultados en:

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