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 .