Jackson - 将内部对象列表反序列化为更高级别的列表
Posted
技术标签:
【中文标题】Jackson - 将内部对象列表反序列化为更高级别的列表【英文标题】:Jackson - deserialize inner list of objects to list of one higher level 【发布时间】:2019-06-22 18:55:45 【问题描述】:使用 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
下,而不是定义一个包含名为item
的列表的DTO Items
。
这可能吗?如何定义这个 DTO Item
?
public class TrasactionDTO
private List<Item> items;
...
public class Item
这个问题类似,但不能解决问题。 Deserialize wrapped list using Jackson
【问题讨论】:
无法修改 JSON 的数据结构来获得你想要的结果?就像在发布数据之前一样,data.items = data.items.item;
?
JSON 作为请求体传入,我不知道预先转换的方法。
一种方法是自定义反序列化器
是的,虽然不是很熟悉。我有 4 个这样的字段;如果我们可以用一些注释来做到这一点会更好吗?
可以在public class TrasactionDTO
baeldung.com/jackson-annotations之上尝试这个@JsonRootName("items")
【参考方案1】:
您可以使用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");
【讨论】:
【参考方案2】:我们需要实现自定义反序列化器。因为我们想跳过一个内部字段,所以我们的实现应该:
- 跳过开始对象
"any_field_name"
- 跳过任何字段名称。我们假设我们只有一个内部字段。
[, ..., ]
- 为 List
使用默认解串器。
- 跳过结束对象
使用上述概念实现应该很容易:
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));
以上示例打印:
Transactionitems=[ItemitemNumber=193487654, ItemitemNumber=193487654], name='Pickle Rick'
Productproducts=[ProductItemid='1234']
更多信息请阅读:
-
Custom Jackson Deserializer Getting Access to Current Field Class
Getting Started with Custom Deserialization in Jackson
Jackson Exceptions – Problems and Solutions
Jackson UNWRAP_ROOT_VALUE
Configuring ObjectMapper in Spring
【讨论】:
【参考方案3】:看来@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;
...
【讨论】:
以上是关于Jackson - 将内部对象列表反序列化为更高级别的列表的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Jackson 将原始 JSON 反序列化为 Java 对象