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

Сопоставление вложенных значений JSON с Jackson

Изучите три способа десериализации вложенных значений JSON в Java с помощью библиотеки Джексона.

Автор оригинала: baeldung.

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(Map brand) {
        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 .