Рубрики
Без рубрики

Сопоставление динамического объекта JSON с Джексоном

Изучите несколько способов обработки динамических объектов JSON с помощью Jackson.

Автор оригинала: Attila Fejér.

1. введение

Работа с предопределенными структурами данных JSON с помощью Jackson проста. Однако иногда нам нужно обрабатывать динамические объекты JSON, которые имеют неизвестные свойства .

В этом коротком уроке мы рассмотрим несколько способов отображения динамических объектов JSON в классы Java.

Обратите внимание, что во всех тестах мы предполагаем, что у нас есть поле ObjectMapper типа com.fasterxml.jackson.databind.ObjectMapper .

Дальнейшее чтение:

Сопоставление вложенных значений с Джексоном

Использование опционально с Джексоном

2. Использование JsonNode

Допустим, мы хотим обработать спецификации продукта в интернет-магазине. Все продукты имеют некоторые общие свойства, но есть и другие, которые зависят от типа продукта.

Например, мы хотим знать соотношение сторон дисплея мобильного телефона, но это свойство не имеет большого смысла для обуви.

Структура данных выглядит следующим образом:

{
    "name": "Pear yPhone 72",
    "category": "cellphone",
    "details": {
        "displayAspectRatio": "97:3",
        "audioConnector": "none"
    }
}

Мы храним динамические свойства в объекте details .

Мы можем сопоставить общие свойства со следующим классом Java:

class Product {

    String name;
    String category;

    // standard getters and setters
}

Кроме того, нам нужно соответствующее представление для объекта details . Например, com.fasterxml.jackson.databind.JsonNode может обрабатывать динамические ключи .

Чтобы использовать его, мы должны добавить его в качестве поля в наш класс Product :

class Product {

    // common fields

    JsonNode details;

    // standard getters and setters
}

Наконец, мы проверяем, что это работает:

String json = "";

Product product = objectMapper.readValue(json, Product.class);

assertThat(product.getName()).isEqualTo("Pear yPhone 72");
assertThat(product.getDetails().get("audioConnector").asText()).isEqualTo("none");

Однако у нас есть проблема с этим решением. Наш класс зависит от библиотеки Джексона, так как у нас есть поле JsonNode .

3. Использование карты

Мы можем решить эту проблему с помощью java.util.Карта для поля подробности . Точнее, мы должны использовать Map Object> . Object>

Все остальное может остаться прежним:

class Product {

    // common fields

    Map details;

    // standard getters and setters
}

И тогда мы сможем проверить это с помощью теста:

String json = "";

Product product = objectMapper.readValue(json, Product.class);

assertThat(product.getName()).isEqualTo("Pear yPhone 72");
assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");

4. Использование @JsonAnySetter

Предыдущие решения хороши, когда объект содержит только динамические свойства. Однако иногда у нас есть фиксированные и динамические свойства, смешанные в одном объекте JSON .

Например, нам может потребоваться выровнять представление нашего продукта:

{
    "name": "Pear yPhone 72",
    "category": "cellphone",
    "displayAspectRatio": "97:3",
    "audioConnector": "none"
}

Мы можем рассматривать подобную структуру как динамический объект. К сожалению, это означает, что мы не можем определить общие свойства — мы также должны обрабатывать их динамически.

В качестве альтернативы мы могли бы использовать @JsonAnySetter для обозначения метода обработки дополнительных неизвестных свойств . Такой метод должен принимать два аргумента: имя и значение свойства:

class Product {

    // common fields

    Map details = new LinkedHashMap<>();

    @JsonAnySetter
    void setDetail(String key, Object value) {
        details.put(key, value);
    }

    // standard getters and setters
}

Обратите внимание, что мы должны создать экземпляр объекта details , чтобы избежать NullPointerExceptions .

Поскольку мы храним динамические свойства в Map , мы можем использовать его так же, как и раньше:

String json = "";

Product product = objectMapper.readValue(json, Product.class);

assertThat(product.getName()).isEqualTo("Pear yPhone 72");
assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");

5. Создание пользовательского десериализатора

В большинстве случаев эти решения работают просто отлично. Однако иногда нам нужно гораздо больше контроля. Например, мы могли бы хранить информацию о десериализации наших объектов JSON в базе данных.

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

6. Заключение

В этой статье мы рассмотрели несколько способов обработки динамических объектов JSON с помощью Jackson.

Как обычно, примеры доступны на GitHub .