简体   繁体   English

Jackson - 将内部对象列表反序列化为更高级别的列表

[英]Jackson - deserialize inner list of objects to list of one higher level

With Spring Boot and Jackson, how can I deserialize a wrapped/inner list into a list directly in the outer level?使用 Spring Boot 和 Jackson,如何直接在外层将包装/内部列表反序列化为列表?

For example, I have:例如,我有:

{
    "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"
            }
        ]
    },
    ...
    }
}

In the JSON, item is a list under items ;在 JSON 中, itemitems下的列表; but I want to parse it as a list named items , directly under transaction , instead of defining a DTO Items which contains a list named item .但我想将它解析为一个名为items的列表,直接在transaction下,而不是定义一个包含名为item的列表的 DTO Items

Is this possible?这可能吗? How to define this DTO Item ?如何定义此 DTO Item

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

public class Item {

}

This question is similar but does not solve the problem.这个问题类似,但没有解决问题。 Deserialize wrapped list using Jackson 使用 Jackson 反序列化包装列表

We need to implement custom deserialiser.我们需要实现自定义反序列化器。 Because we want to skip one inner field our implementation should:因为我们想跳过一个内部字段,所以我们的实现应该:

  1. { - skip start object { - 跳过起始对象
  2. "any_field_name" - skip any field name. "any_field_name" - 跳过任何字段名称。 We assume that we have only one inner field.我们假设我们只有一个内部场。
  3. [{}, ..., {}] - use default deserialiser for List . [{}, ..., {}] - 对List使用默认反序列化器。
  4. } - skip end object } - 跳过结束对象

Using above concept implementation should be easy:使用上述概念实现应该很容易:

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());
    }
}

Let's assume we have JSON payload like this:假设我们有这样的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"
  }
}

Above JSON we can map to below POJO classes: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
}

To show it works for many different models let's introduce one more JSON payload:为了展示它适用于许多不同的模型,让我们再介绍一个JSON有效负载:

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

and two more POJO classes:以及另外两个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
}

Now we can test our solution:现在我们可以测试我们的解决方案:

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));
    }
}

Above example prints:上面的示例打印:

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

For more info read:欲了解更多信息,请阅读:

  1. Custom Jackson Deserializer Getting Access to Current Field Class 自定义 Jackson 反序列化器访问当前字段类
  2. Getting Started with Custom Deserialization in Jackson Jackson 中的自定义反序列化入门
  3. Jackson Exceptions – Problems and Solutions杰克逊例外 – 问题和解决方案
  4. Jackson UNWRAP_ROOT_VALUE 杰克逊 UNWRAP_ROOT_VALUE
  5. Configuring ObjectMapper in Spring 在 Spring 中配置 ObjectMapper

You can use a Map to represent the intermediate Items object.您可以使用Map来表示中间Items对象。

Given this example (all fields public just for demonstration purposes):鉴于此示例(所有字段public仅用于演示目的):

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

...you can achieve what you want in two ways: ...您可以通过两种方式实现您想要的:

1. By using a constructor: 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. By using a setter: 2. 通过使用 setter:

public class TransactionDTO {
    private List<Item> items;

    public void setItems(final Map<String, List<Item>> items) {
        this.items = items.get("item");
    }
}

It seems that @JsonUnwrapped is what I need.看来@JsonUnwrapped正是我所需要的。

https://www.baeldung.com/jackson-annotations https://www.baeldung.com/jackson-annotations

@JsonUnwrapped defines values that should be unwrapped/flattened when serialized/deserialized. @JsonUnwrapped定义了在序列化/反序列化时应该解包/展平的值。

Let's see exactly how that works;让我们看看它是如何工作的; we'll use the annotation to unwrap the property name:我们将使用注释来解开属性名称:

 public class UnwrappedUser { public int id; @JsonUnwrapped public Name name; public static class Name { public String firstName; public String lastName; } }

Let's now serialize an instance of this class:现在让我们序列化这个类的一个实例:

 @Test public void whenSerializingUsingJsonUnwrapped_thenCorrect() throws JsonProcessingException, ParseException { UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe"); UnwrappedUser user = new UnwrappedUser(1, name); String result = new ObjectMapper().writeValueAsString(user); assertThat(result, containsString("John")); assertThat(result, not(containsString("name"))); }

Here's how the output looks like – the fields of the static nested class unwrapped along with the other field:下面是输出的样子——静态嵌套类的字段与其他字段一起展开:

 { "id":1, "firstName":"John", "lastName":"Doe" }

So, it should be something like:所以,它应该是这样的:

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

public static class Item {
    @JsonUnwrapped
    private InnerItem innerItem;
    ...

}

public static class InnerItem {
    private String itemNumber;
    ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM