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

З документації щодо анотацій Джексона :

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

за посиланням "всі послуги кодового будинку припинені"
Ендрю Норман,

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 запросив рішення для полів Object.
Дейв

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

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 і постачається з jackson-databind, що не потрібно ...


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), окрім аргументу "value", приймає аргумент "index" (числовий), який визначає порядок полів під час серіалізації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.