[英]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 中, item
是items
下的列表; 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:因为我们想跳过一个内部字段,所以我们的实现应该:
{
- skip start object {
- 跳过起始对象"any_field_name"
- skip any field name. "any_field_name"
- 跳过任何字段名称。 We assume that we have only one inner field.我们假设我们只有一个内部场。[{}, ..., {}]
- use default deserialiser for List
. [{}, ..., {}]
- 对List
使用默认反序列化器。}
- 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:欲了解更多信息,请阅读:
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: ...您可以通过两种方式实现您想要的:
public class TransactionDTO {
private List<Item> items;
@JsonCreator
public TransactionDTO(@JsonProperty("items") final Map<String, List<Item>> items) {
this.items = items.get("item");
}
}
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.