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

时间:2019-01-29 15:37:43

标签: java json spring-boot jackson

使用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中,itemitems下的列表;但我想将其解析为直接位于items下的名为transaction的列表,而不是定义包含名为Items的列表的DTO item

这可能吗?如何定义此DTO Item

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

public class Item {

}

此问题类似,但不能解决问题。 Deserialize wrapped list using Jackson

3 个答案:

答案 0 :(得分:2)

您可以使用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。通过使用setter:

public class TransactionDTO {
    private List<Item> items;

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

答案 1 :(得分:2)

我们需要实现自定义反序列化器。因为我们要跳过一个内部字段,所以我们的实现应该:

  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 Jackson Deserializer Getting Access to Current Field Class
  2. Getting Started with Custom Deserialization in Jackson
  3. Jackson Exceptions – Problems and Solutions
  4. Jackson UNWRAP_ROOT_VALUE
  5. Configuring ObjectMapper in Spring

答案 2 :(得分:0)

似乎@JsonUnwrapped是我所需要的。

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

@JsonUnwrapped 定义在序列化/反序列化时应该解包/展平的值。

让我们确切地了解它是如何工作的;我们将使用注释解开属性名称:

public class UnwrappedUser {
    public int id;

    @JsonUnwrapped
    public Name name;

    public static class Name {
        public String firstName;
        public String lastName;
    }
 }

现在让我们序列化此类的实例:

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

这是输出的样子–静态嵌套类的字段与其他字段一起展开:

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

因此,它应该类似于:

public class TrasactionDTO {

    private List<Item> items;
    ...
}

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

}

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