[英]How do I unwrap a list of list wrapped items in Jackson?
我有一個類似於這個的bean:
public class Product {
public String id;
public String vendor;
public Set<Image> images;
}
public class Image {
public String originalSrc;
}
我正在嘗試反序列化類似於此的JSON:
{
"id": "gid:\/\/mysite\/Product\/1853361520730",
"vendor": "gadgetdown",
"images": {
"edges": [
{
"node": {
"originalSrc": "https:\/\/cdn.something.com"
}
},
{
"node": {
"originalSrc": "https:\/\/cdn.something.com"
}
}
]
}
我無法反序列化對象,因為每個image
對象都包含在node
對象中並且共同包含在edges
對象中。
編輯:為了清楚起見,我不想通過使用bean來實現這一點,這個例子是一個簡化,JSON有效負載中的所有數組項都包含在這個edges
和node
表示中。
因此, images
不是一個set
它是一個帶有edges
列表的JSONObject
public class Images {
private List<Edge> edges;
}
每個Edge
包含Node
對象,
public class Edge {
private Node node;
}
每個Node
都有單個String屬性originalSrc
public class Node {
private String originalSrc;
}
如果每個列表都具有如下結構:
{
"images": {
"edges": [
{
"node": {
"entry": "entry-value"
}
}
]
}
}
每個列表都是帶有edges
屬性的JSON Object
,數組中的每個元素都由具有node
屬性的JSON Object
包裝。 對於這個結構,我們可以編寫類似於Jackson的通用反序列化器- 將對象的內部列表反序列化為一個更高級別的問題列表 。
示例Set
反序列化器:
class InnerSetDeserializer extends JsonDeserializer<Set> implements ContextualDeserializer {
private final JavaType propertyType;
public InnerSetDeserializer() {
this(null);
}
public InnerSetDeserializer(JavaType propertyType) {
this.propertyType = propertyType;
}
@Override
public Set deserialize(JsonParser p, DeserializationContext context) throws IOException {
p.nextToken(); // SKIP START_OBJECT
p.nextToken(); // SKIP any FIELD_NAME
CollectionType collectionType = getCollectionType(context);
List<Map<String, Object>> list = context.readValue(p, collectionType);
p.nextToken(); // SKIP END_OBJECT
return list.stream().map(Map::values).flatMap(Collection::stream).collect(Collectors.toSet());
}
private CollectionType getCollectionType(DeserializationContext context) {
TypeFactory typeFactory = context.getTypeFactory();
MapType mapType =
typeFactory.constructMapType(
Map.class, String.class, propertyType.getContentType().getRawClass());
return typeFactory.constructCollectionType(List.class, mapType);
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
return new InnerSetDeserializer(property.getType());
}
}
我們可以使用如下:
class Product {
private String id;
private String vendor;
@JsonDeserialize(using = InnerSetDeserializer.class)
private Set<Image> images;
// getters, setters
}
應用示例:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class JsonApp {
public static void main(String[] args) throws IOException {
File jsonFile = new File("./resources/test.json");
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(jsonFile, Product.class);
System.out.println(product);
}
}
上面的代碼打印:
Product{id='gid://mysite/Product/1853361520730', vendor='gadgetdown', images=[Image{originalSrc='https://cdn.something.com'}, Image{originalSrc='https://cdn.something.com'}]}
要從"node": { "originalSrc": "https:\\/\\/cdn.something.com" }
解包Image
"node": { "originalSrc": "https:\\/\\/cdn.something.com" }
您只需使用@JsonRootName
注釋即可
@JsonRootName(value = "node")
class Image {
public String originalSrc;
}
但是從"images": { "edges": [{...}, {...}] }
展開圖像集合"images": { "edges": [{...}, {...}] }
有點復雜,需要使用自定義JsonDeserializer
class Product {
public String id;
public String vendor;
@JsonDeserialize(using = ImageSetDeserializer.class)
public Set<Image> images;
}
class ImageSetDeserializer extends JsonDeserializer<Set<Image>> {
public Set<Image> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
JsonNode node = mapper.readTree(jsonParser);
return mapper.convertValue(node.get("edges").findValues("node"), new TypeReference<Set<Image>>() {});
}
}
最后一個測試:
public class ProductTest {
private final String source = "{\n" +
" \"id\": \"gid:\\/\\/mysite\\/Product\\/1853361520730\",\n" +
" \"vendor\": \"gadgetdown\",\n" +
" \"images\": {\n" +
" \"edges\": [\n" +
" {\n" +
" \"node\": {\n" +
" \"originalSrc\": \"https:\\/\\/cdn.something.com\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"node\": {\n" +
" \"originalSrc\": \"https:\\/\\/cdn.something.com\"\n" +
" }\n" +
" }\n" +
" ]\n" +
" }" +
"}";
@Test
public void test() throws IOException {
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(source, Product.class);
assertEquals(product.id, "gid://mysite/Product/1853361520730");
assertEquals(product.vendor, "gadgetdown");
assertNotNull(product.images);
List<Image> images = new ArrayList<>(product.images);
assertEquals(images.size(), 2);
assertEquals(images.get(0).originalSrc, "https://cdn.something.com");
assertEquals(images.get(1).originalSrc, "https://cdn.something.com");
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.