Джексон - десериализовать внутренний список объектов до списка более высокого уровня - PullRequest
0 голосов
/ 29 января 2019

С помощью Spring Boot и Jackson как я могу десериализовать завернутый / внутренний список в список непосредственно на внешнем уровне?

Например, у меня есть:

{
    "transaction": {
    "items": {
        "item": [
            {
                "itemNumber": "193487654",
                "itemDescription": "Widget",
                "itemPrice": "599.00",
                "itemQuantity": "1",
                "itemBrandName": "ACME",
                "itemCategory": "Electronics",
                "itemTax": "12.95"
            },
            {
                "itemNumber": "193487654",
                "itemDescription": "Widget",
                "itemPrice": "599.00",
                "itemQuantity": "1",
                "itemBrandName": "ACME",
                "itemCategory": "Electronics",
                "itemTax": "12.95"
            }
        ]
    },
    ...
    }
}

В JSON item - это список под items;но я хочу проанализировать его как список с именем items, непосредственно под transaction, вместо определения DTO Items, который содержит список с именем item.

Возможно ли это?Как определить это DTO Item?

public class TrasactionDTO {
    private List<Item> items;
    ...
}

public class Item {

}

Этот вопрос похож, но не решает проблему. Десериализовать упакованный список, используя Джексона

Ответы [ 2 ]

0 голосов
/ 30 января 2019

Нам нужно реализовать собственный десериализатор.Поскольку мы хотим пропустить одно внутреннее поле, наша реализация должна:

  1. { - пропустить начальный объект
  2. "any_field_name" - пропустить любое имя поля.Мы предполагаем, что у нас есть только одно внутреннее поле.
  3. [{}, ..., {}] - использовать десериализатор по умолчанию для List.
  4. } - пропустить конечный объект

Использование вышеуказанной концепции должно быть простым:

public class InnerListDeserializer extends JsonDeserializer<List> implements ContextualDeserializer {

    private final JavaType propertyType;

    public InnerListDeserializer() {
        this(null);
    }

    public InnerListDeserializer(JavaType propertyType) {
        this.propertyType = propertyType;
    }

    @Override
    public List deserialize(JsonParser p, DeserializationContext context) throws IOException {
        p.nextToken(); // SKIP START_OBJECT
        p.nextToken(); // SKIP any FIELD_NAME

        List list = context.readValue(p, propertyType);

        p.nextToken(); // SKIP END_OBJECT

        return list;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
        return new InnerListDeserializer(property.getType());
    }
}

Давайте предположим, что у нас есть JSON полезная нагрузка, подобная этой:

{
  "transaction": {
    "items": {
      "item": [
        {
          "itemNumber": "193487654",
          "itemDescription": "Widget",
          "itemPrice": "599.00",
          "itemQuantity": "1",
          "itemBrandName": "ACME",
          "itemCategory": "Electronics",
          "itemTax": "12.95"
        },
        {
          "itemNumber": "193487654",
          "itemDescription": "Widget",
          "itemPrice": "599.00",
          "itemQuantity": "1",
          "itemBrandName": "ACME",
          "itemCategory": "Electronics",
          "itemTax": "12.95"
        }
      ]
    },
    "name": "Pickle Rick"
  }
}

Выше JSON мы можем отобразить ниже POJO классов:

@JsonRootName("transaction")
public class Transaction {

    private String name;
    private List<Item> items;

    @JsonDeserialize(using = InnerListDeserializer.class)
    public List<Item> getItems() {
        return items;
    }

    // getters, setters, toString
}

public class Item {

    private String itemNumber;

    // getters, setters, toString
}

Чтобы показать, что он работает для многих различных моделей, давайте представим еще одну JSON полезную нагрузку:

{
  "product": {
    "products": {
      "innerArray": [
        {
          "id": "1234"
        }
      ]
    }
  }
}

и еще два POJO класса:

@JsonRootName("product")
class Product {

    private List<ProductItem> products;

    @JsonDeserialize(using = InnerListDeserializer.class)
    public List<ProductItem> getProducts() {
        return products;
    }

    // getters, setters, toString
}

class ProductItem {

    private String id;

    // getters, setters, toString
}

Теперь мы можем протестировать наше решение:

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class JSoupTest {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        File jsonFile = new File("Path to 1-st JSON").getAbsoluteFile();
        File jsonFile1 = new File("Path to 2-nd JSON").getAbsoluteFile();

        System.out.println(mapper.readValue(jsonFile, Transaction.class));
        System.out.println(mapper.readValue(jsonFile1, Product.class));
    }
}

Приведенный выше пример печати:

Transaction{items=[Item{itemNumber=193487654}, Item{itemNumber=193487654}], name='Pickle Rick'}
Product{products=[ProductItem{id='1234'}]}

Для получения дополнительной информации читайте:

  1. CustomДесериализатор Джексона Получение доступа к текущему классу поля
  2. Начало работы с пользовательской десериализацией в Джексоне
  3. Исключения Джексона - проблемы и решения
  4. Джексон UNWRAP_ROOT_VALUE
  5. Конфигурирование ObjectMapper весной
0 голосов
/ 29 января 2019

Вы можете использовать Map для представления промежуточного Items объекта.

Для данного примера (все поля public только для демонстрационных целей):

public class Item {
    public String itemNumber, itemDescription, itemPrice, itemQuantity, itemBrandName, itemCategory, itemTax;
}

...вы можете достичь желаемого двумя способами:

1.Используя конструктор:

public class TransactionDTO {
    private List<Item> items;

    @JsonCreator
    public TransactionDTO(@JsonProperty("items") final Map<String, List<Item>> items) {
        this.items = items.get("item");
    }
}

2.Используя сеттер:

public class TransactionDTO {
    private List<Item> items;

    public void setItems(final Map<String, List<Item>> items) {
        this.items = items.get("item");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...