1. Обзор
Типичным вариантом использования при работе с JSON является выполнение преобразования из одной модели в другую. Например, мы можем захотеть разобрать сложный, плотно вложенный граф объектов в более простую модель для использования в другой области.
В этой краткой статье мы рассмотрим как сопоставить вложенные значения с Jackson , чтобы сгладить сложную структуру данных. Мы десериализуем JSON тремя различными способами:
- Использование @JsonProperty
- Использование JsonNode
- Использование пользовательского JsonDeserializer
Дальнейшее чтение:
Использование опционально с Джексоном
Наследство с Джексоном
Использование @JsonComponent в Spring Boot
2. Зависимость Maven
Давайте сначала добавим следующую зависимость в pom.xml :
com.fasterxml.jackson.core jackson-databind 2.11.1
Мы можем найти последние версии jackson-databind on Maven Central .
3. Источник JSON
Рассмотрим следующий JSON в качестве исходного материала для наших примеров. Хотя структура надумана, обратите внимание, что мы включаем свойства, которые вложены на два уровня глубже:
{ "id": "957c43f2-fa2e-42f9-bf75-6e3d5bb6960a", "name": "The Best Product", "brand": { "id": "9bcd817d-0141-42e6-8f04-e5aaab0980b6", "name": "ACME Products", "owner": { "id": "b21a80b1-0c09-4be3-9ebd-ea3653511c13", "name": "Ultimate Corp, Inc." } } }
4. Упрощенная Модель Предметной области
В уплощенной модели домена, описанной классом Product ниже , мы извлекаем brand Name , который вложен на один уровень глубоко в наш исходный JSON. Кроме того, мы извлекем имя владельца , которое вложено на два уровня глубже и внутри вложенного бренда объекта:
public class Product { private String id; private String name; private String brandName; private String ownerName; // standard getters and setters }
5. Сопоставление С Аннотациями
Чтобы сопоставить вложенное свойство brandName , нам сначала нужно распаковать вложенный объект brand в Map и извлечь свойство name . Затем , чтобы сопоставить OwnerName , мы распаковываем вложенный owner объект в Map и извлекаем его name свойство.
Мы можем поручить Джексону распаковать вложенное свойство, используя комбинацию @JsonProperty и некоторую пользовательскую логику , которую мы добавляем в наш Product класс:
public class Product { // ... @SuppressWarnings("unchecked") @JsonProperty("brand") private void unpackNested(Mapbrand) { this.brandName = (String)brand.get("name"); Map owner = (Map )brand.get("owner"); this.ownerName = owner.get("name"); } }
Наш клиентский код теперь может использовать ObjectMapper для преобразования нашего исходного JSON, который существует как String constant SOURCE_JSON в тестовом классе:
@Test public void whenUsingAnnotations_thenOk() throws IOException { Product product = new ObjectMapper() .readerFor(Product.class) .readValue(SOURCE_JSON); assertEquals(product.getName(), "The Best Product"); assertEquals(product.getBrandName(), "ACME Products"); assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); }
6. Сопоставление С JsonNode
Сопоставление вложенной структуры данных с JsonNode требует немного больше работы. Здесь мы используем ObjectMapper ‘s read Tree для разбора нужных полей:
@Test public void whenUsingJsonNode_thenOk() throws IOException { JsonNode productNode = new ObjectMapper().readTree(SOURCE_JSON); Product product = new Product(); product.setId(productNode.get("id").textValue()); product.setName(productNode.get("name").textValue()); product.setBrandName(productNode.get("brand") .get("name").textValue()); product.setOwnerName(productNode.get("brand") .get("owner").get("name").textValue()); assertEquals(product.getName(), "The Best Product"); assertEquals(product.getBrandName(), "ACME Products"); assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); }
7. Сопоставление С Пользовательским JsonDeserializer
Сопоставление вложенной структуры данных с пользовательским JsonDeserializer идентично подходу JsonNode с точки зрения реализации. Сначала мы создаем JsonDeserializer:
public class ProductDeserializer extends StdDeserializer{ public ProductDeserializer() { this(null); } public ProductDeserializer(Class> vc) { super(vc); } @Override public Product deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonNode productNode = jp.getCodec().readTree(jp); Product product = new Product(); product.setId(productNode.get("id").textValue()); product.setName(productNode.get("name").textValue()); product.setBrandName(productNode.get("brand") .get("name").textValue()); product.setOwnerName(productNode.get("brand").get("owner") .get("name").textValue()); return product; } }
7.1. Ручная регистрация десериализатора
Чтобы вручную зарегистрировать наш пользовательский десериализатор, наш клиентский код должен добавить JsonDeserializer в Модуль , зарегистрировать Модуль с помощью ObjectMapper и вызвать readValue:
@Test public void whenUsingDeserializerManuallyRegistered_thenOk() throws IOException { ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(Product.class, new ProductDeserializer()); mapper.registerModule(module); Product product = mapper.readValue(SOURCE_JSON, Product.class); assertEquals(product.getName(), "The Best Product"); assertEquals(product.getBrandName(), "ACME Products"); assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); }
7.2. Автоматическая регистрация десериализатора
В качестве альтернативы ручной регистрации JsonDeserializer, мы можем зарегистрировать десериализатор непосредственно в классе :
@JsonDeserialize(using = ProductDeserializer.class) public class Product { // ... }
При таком подходе нет необходимости регистрироваться вручную. Давайте взглянем на наш клиентский код с помощью автоматической регистрации:
@Test public void whenUsingDeserializerAutoRegistered_thenOk() throws IOException { ObjectMapper mapper = new ObjectMapper(); Product product = mapper.readValue(SOURCE_JSON, Product.class); assertEquals(product.getName(), "The Best Product"); assertEquals(product.getBrandName(), "ACME Products"); assertEquals(product.getOwnerName(), "Ultimate Corp, Inc."); }
8. Заключение
В этом уроке мы продемонстрировали несколько способов использования Jackson для анализа JSON, содержащего вложенные значения . Посмотрите на нашу главную страницу Jackson Tutorial для получения дополнительных примеров.
И, как всегда, фрагменты кода можно найти на GitHub .