我是杰克逊的新手,并且在使用通用字段反序列化JSON方面存在问题。这是我想用杰克逊解析的JSON。
{
"topic": {
"headline": {
...
},
"body": [
{
"type": "complex",
"content": {
"player_template": "12345",
"width": 600,
"height": 338,
"url": "http://...",
"caption": "foobar",
"vid": "12345",
"watch_url": "http://...",
"embed_html": "<script..."
},
"preview_image_url": "https://...",
"position": 0
},
{
"content": "foobar",
"type": "simple",
"position": 1
}
],
"type": "some type",
"part": "image",
"box_link": [
{
...
},
...
]
}
}
注意
topic > body > element[0] > content
是object
,但topic > body > element[1] > content
是string
。 body
元素可能只包含string
或object
或两者。
以下是body
和content
的Java类。
public class Body<T> {
// @JsonDeserialize(using = ContentDeserializer.class)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = String.class, name = "simple"),
@JsonSubTypes.Type(value = Content.class, name = "complex")
})
@JsonProperty("content")
private T mContent;
@JsonProperty("type")
private String mType;
@JsonProperty("preview_image_url")
private String mPreviewImageUrl;
@JsonProperty("position")
private int mPosition;
// getter and setter
}
public class Content {
@JsonProperty("player_template")
private String mPlayerTemplate;
@JsonProperty("width")
private int mWidth;
@JsonProperty("height")
private int mHeight;
@JsonProperty("url")
private String mUrl;
@JsonProperty("caption")
private String mCaption;
@JsonProperty("vid")
private String mVid;
@JsonProperty("watch_url")
private String mWatchUrl;
@JsonProperty("embed_html")
private String mEmbedHtml;
// getter and setter
}
我尝试使用JsonSubTypes
注释将JSON映射到POJO,因此如果type
等于complex
,那么JSON应该映射到Content
类,{{1键入映射类应该是simple
对象。问题是杰克逊将String
内容转换为complex
我不想要的内容。对于LinkedHashMap
内容没有问题,它会转换为simple
,但我认为杰克逊使用内部逻辑来映射这种正确的方式。
如果我尝试使用String
注释,则不会调用反序列化方法。就像杰克逊忽视注释并自己创作一样。
我确实犯了错误?如何将JsonDeserialize
内容解析为complex
POJO?
答案 0 :(得分:1)
@JsonTypeInfo
和@JsonSubTypes
旨在帮助继承而不是泛型。由于String
和Content
隐式扩展Object
,因此您可以将mContent
定义为Object
。以下是您的Body
课程的样子:
class Body {
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = String.class, name = "simple"),
@JsonSubTypes.Type(value = Content.class, name = "complex")
})
@JsonProperty("content")
private Object mContent;
指定
时include = JsonTypeInfo.As.PROPERTY
杰克逊将在JSON的type
字段中查找content
。但在您的情况下,type
位于JSON中body
数组的元素中,与content
处于同一级别。在这种情况下,您必须指定
include = JsonTypeInfo.As.EXTERNAL_PROPERTY
所以杰克逊会在JSON的type
字段之外寻找content
。
请注意,如果您拥有像Body<T>
这样的通用类,则必须向Jackson提供类型T
以进行反序列化(例如使用TypeReference
)。如果您想在同一个集合/数组中使用Body<String>
和Body<Content>
,我不会看到它会如何工作。集合的类型必须是List<Body>
,这不再是通用的。
答案 1 :(得分:0)
我已使用自定义JsonDeserializer
解决了此问题。
@JsonDeserialize(using = BodyDeserializer.class)
public class Body<T> {
@JsonProperty("content")
private T mContent;
@JsonProperty("type")
private String mType;
@JsonProperty("preview_image_url")
private String mPreviewImageUrl;
@JsonProperty("position")
private int mPosition;
// getter and setter
}
public class BodyDeserializer extends StdDeserializer<Body> {
private static final String CAPTION = "caption";
private static final String CONTENT = "content";
private static final String COMPLEX = "complex";
private static final String EMBED_HTML = "embed_html";
private static final String HEIGHT = "height";
private static final String PLAYER_TEMPLATE = "player_template";
private static final String POSITION = "position";
private static final String PREVIEW_IMAGE_URL = "preview_image_url";
private static final String PROVIDER = "provider";
private static final String TYPE = "type";
private static final String URL = "url";
private static final String VID = "vid";
private static final String WATCH_URL = "watch_url";
private static final String WIDTH = "width";
public BodyDeserializer() {
this(Body.class);
}
protected BodyDeserializer(Class<Body> vc) {
super(vc);
}
@Override
public Body deserialize(JsonParser parser, DeserializationContext context) throws IOException {
final ObjectCodec oc = parser.getCodec();
final JsonNode node = oc.readTree(parser);
return deserialize(node);
}
private Body deserialize(JsonNode node) {
final String type = node.get(TYPE).asText();
if (COMPLEX.equals(type)) {
return deserializeToBodyWithContent(node, type);
} else {
return deserializeToBodyWithString(node, type);
}
}
private Body deserializeToBodyWithString(JsonNode node, String type) {
final int position = node.get(POSITION).asInt();
return new Body<String>().setContent(node.get(CONTENT).asText()).setType(type).setPosition(position);
}
private Body deserializeToBodyWithContent(JsonNode node, String type) {
final int position = node.get(POSITION).asInt();
final String provider = node.get(PROVIDER).asText();
final String previewImageUrl = node.get(PREVIEW_IMAGE_URL).asText();
return new Body<Content>().setContent(createContent(node.get(CONTENT)))
.setType(type)
.setProvider(provider)
.setPreviewImageUrl(previewImageUrl)
.setPosition(position);
}
private Content createContent(JsonNode node) {
final int width = node.get(WIDTH).asInt();
final int height = node.get(HEIGHT).asInt();
final String vid = node.get(VID).asText();
final String url = node.get(URL).asText();
final String caption = node.get(CAPTION).asText();
final String watchUrl = node.get(WATCH_URL).asText();
final String embedHtml = node.get(EMBED_HTML).asText();
final String playerTemplate = node.get(PLAYER_TEMPLATE).asText();
return new Content().setPlayerTemplate(playerTemplate)
.setWidth(width)
.setHeight(height)
.setUrl(url)
.setCaption(caption)
.setVid(vid)
.setWatchUrl(watchUrl)
.setEmbedHtml(embedHtml);
}
}
这不是最好的解决方案,但它确实有效。我现在使用GSON,它有点简单。