Перейти к основному содержимому

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

· 3 мин. чтения

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

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

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

class Product {

String name;
String category;

// standard getters and setters
}

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

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

class Product {

// common fields

JsonNode details;

// standard getters and setters
}

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

String json = "<json object>";

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 для поля сведений . Точнее, мы должны использовать Map<String, Object> .

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

class Product {

// common fields

Map<String, Object> details;

// standard getters and setters
}

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

String json = "<json object>";

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<String, Object> details = new LinkedHashMap<>();

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

// standard getters and setters
}

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

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

String json = "<json object>";

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 .