使用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
的列表,而不是定义包含名为Items
的列表的DTO item
。
这可能吗?如何定义此DTO Item
?
public class TrasactionDTO {
private List<Item> items;
...
}
public class Item {
}
此问题类似,但不能解决问题。 Deserialize wrapped list using Jackson
答案 0 :(得分:2)
您可以使用Map
来表示中间Items
对象。
给出以下示例(所有字段public
仅用于演示目的):
public class Item {
public String itemNumber, itemDescription, itemPrice, itemQuantity, itemBrandName, itemCategory, itemTax;
}
...您可以通过两种方式实现所需的目标:
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");
}
}
答案 1 :(得分: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));
}
}
上面的示例打印:
Transaction{items=[Item{itemNumber=193487654}, Item{itemNumber=193487654}], name='Pickle Rick'}
Product{products=[ProductItem{id='1234'}]}
有关更多信息,请阅读:
答案 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;
...
}