Jackson ObjectMapper - укажите порядок сериализации свойств объекта

84

Я реализую веб-службу RESTful, в которой пользователь должен отправить подписанный токен проверки вместе с запросом, чтобы я мог убедиться, что запрос не был изменен посредником. Моя текущая реализация выглядит следующим образом.

Токен проверки - это объект VerifData, сериализованный в строку, а затем хэшированный и зашифрованный.

class VerifData {
    int prop1;
    int prop2;
}

В своем сервисе я помещаю данные для сериализации в экземпляр VerifData, а затем сериализую их с помощью Jackson ObjectMapper и передаю механизму проверки вместе с токеном проверки.

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

Но кажется, что каждый раз, когда контейнер приложения запускается, порядок свойств, отображаемых ObjectMapper в строку, изменяется.

Пример: однажды это было бы

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

а в другой раз это будет

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

Таким образом, если клиент сериализовал экземпляр VerifData, как в первую строку, есть 50% вероятность того, что он потерпит неудачу, даже если он правильный.

Есть ли способ обойти это? Могу ли я указать порядок свойств для сопоставления с помощью ObjectMapper (например, в порядке возрастания)? Или есть другой способ лучше всего реализовать этот этап проверки. Я разрабатываю как клиентские, так и серверные реализации. Я использую Java Security API для подписи и проверки.

Лиззи
источник

Ответы:

82

Из документации 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)
Wgitscht
источник
из ссылки «все услуги codehaus были прекращены»
Эндрю Норман
2
Как упомянуто ниже, если ваша цель - применять порядок повсюду, а не при сериализации определенного объекта, см. Stackoverflow.com/a/46267506/2089674 .
user2089674
91

Аннотации полезны, но их сложно применять везде. Вы можете настроить весь свой ObjectMapper для работы таким образом с

Текущие версии Джексона: objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)

Старые версии Джексона: objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

Дункан МакГрегор
источник
Это не совсем правильно (или, по крайней мере, не работает во всех сценариях) - см. Stackoverflow.com/a/46267506/2089674 для полного примера того, что работает.
user2089674
2
@ user2089674 - ваш пример работает только для ключей карты. OP запросил решение для полей объекта.
Дэйв
10

В Jackson 2.x, который вы, вероятно, используете сегодня, используйте:

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

Если вы заботитесь о внешности, вы также можете подумать SerializationFeature.INDENT_OUTPUT.

Обратите внимание, что для правильной сортировки вы должны сериализовать карты или объекты . Если вы сериализуете, JsonNodeнапример (из readTree), он не будет иметь правильный отступ.

пример

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

приводит к:

{
  "hello" : {
    "cruel" : "world"
  }
}
user2089674
источник
6
Есть только одна запись на карте. Поэтому я не вижу его порядка.
Бен
9

В Spring Boot вы можете добавить это поведение глобально, добавив следующее в свой Applicationкласс точки входа:

  @Bean
  public Jackson2ObjectMapperBuilder objectMapperBuilder() {

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

    return builder;
  }
Гэри Роу
источник
9

В Spring Boot есть более простой способ, указав свойство ( application.propertiesнапример, в:

spring.jackson.mapper.sort_properties_alphabetically=true
Вим Деблаув
источник
7

Требуются следующие 2 конфигурации ObjectMapper:

ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
или же
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)

определяет порядок сериализации свойств, используемый для полей POJO.
Примечание : не применяется к java.util.Mapсериализации!

и

ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
или же
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)

Функция, определяющая, java.util.Mapсортируются ли записи по ключу перед сериализацией


Пример конфигурации Spring Boot (yaml):

spring:
  jackson:
    mapper:
      SORT_PROPERTIES_ALPHABETICALLY: true
    serialization:
      ORDER_MAP_ENTRIES_BY_KEYS: true
волшебник
источник
3

Из ответа Дункана МакГрегора: лучше использовать его так:

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

поскольку MapperFeature предназначен для XML и поставляется с привязкой данных Джексона, которая не требуется ...

Немес Нисано
источник
0

Вместо использования аргумента флага:

objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
Леон
источник
0

Вы можете использовать смешивание и указать порядок свойств по своему усмотрению:

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 {}

}
Марк Виниций Вольтолим
источник
0

Я понимаю, что это старая ветка, но поскольку я искал или отвечал и попал сюда, некоторая дополнительная информация может быть полезна другим людям.
Аннотация @JsonProperty, которую я использую в настоящее время (jackson-annotations-2.11.2), принимает, помимо аргумента «значение», аргумент «индекс» (числовой), который указывает порядок полей во время сериализации.

PaulN
источник