Отображение вложенных значений с Джексоном

Отображение вложенных значений с Джексоном

1. обзор

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

В этой быстрой статье мы рассмотримhow to map nested values with Jackson, чтобы выровнять сложную структуру данных. Мы десериализуем JSON тремя разными способами:

  • Используя@JsonProperty

  • ИспользуяJsonNode

  • Использование пользовательскогоJsonDeserializer

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

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

Краткий обзор того, как мы можем использовать Дополнительный с Джексоном.

Read more

Наследование с Джексоном

Из этого туториала вы узнаете, как обрабатывать включение метаданных подтипа и игнорировать свойства, унаследованные от суперклассов с Джексоном.

Read more

Использование @JsonComponent в Spring Boot

Узнайте, как использовать аннотацию @JsonComponent в Spring Boot.

Read more

2. Maven Dependency

Давайте сначала добавим следующую зависимость кpom.xml:


    com.fasterxml.jackson.core
    jackson-databind
    2.9.4

Мы можем найти последние версииjackson-databind на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 ниже, мы извлечемbrandName, который вложен на один уровень в глубину нашего исходного JSON. Кроме того, мы извлечемownerName, который вложен на два уровня в глубину вложенного объектаbrand:

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.

Мы можем указать Джексонуunpack the nested property by using a combination of @JsonProperty and some custom logic, что мы добавляем в наш класс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, который существует как константаStringSOURCE_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‘sreadTree для анализа нужных полей:

@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 кModule, зарегистрироватьModule с помощью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, мы можемregister the deserializer directly on the class:

@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 to parse JSON containing nested values. Взгляните на нашу главную страницуJackson Tutorial, чтобы увидеть больше примеров.

И, как всегда, фрагменты кода можно найтиover on GitHub.