Java 如何将Jackson与运行时泛型一起使用?

Java 如何将Jackson与运行时泛型一起使用?,java,json,generics,jackson,deserialization,Java,Json,Generics,Jackson,Deserialization,我是Jackson的新手,无法用泛型字段反序列化JSON。这是我想用Jackson解析的JSON { "topic": { "headline": { ... }, "body": [ { "type": "complex", "content": { "player_template": "12345", "width": 600, "height":

我是Jackson的新手,无法用泛型字段反序列化JSON。这是我想用Jackson解析的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": [
      {
        ...
      },
      ...
    ]
  }
}
我尝试使用
JsonSubTypes
注释将JSON映射到POJO,因此如果
type
等于
complex
,那么JSON应该映射到
Content
类,对于
simple
类型,映射类应该是
String
对象。问题是,Jackson将
复杂的内容转换为
LinkedHashMap
我不想要的内容。对于
simple
内容没有问题,它将被转换为
String
,但我认为Jackson使用内部逻辑来映射这种正确的方式

如果我尝试使用
JsonDeserialize
注释,则不会调用反序列化程序方法。这就好像杰克逊忽略了注释而自己创作


我在哪里犯了错误?如何将
complex
内容解析为
content
POJO?

@JsonTypeInfo
@JsonSubTypes
旨在帮助继承而不是泛型。由于
字符串
内容
都隐式扩展了
对象
,因此可以将
mContent
定义为
对象
。下面是您的
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
Jackson将在JSON中的
content
字段中查找
type
。但是在您的例子中,
类型
位于JSON中
主体
数组的元素中,与
内容
处于同一级别。在这种情况下,您必须指定

include = JsonTypeInfo.As.EXTERNAL_PROPERTY
因此,Jackson将在JSON中的
content
字段之外查找
type


请记住,如果您有一个像
Body
这样的泛型类,则必须向Jackson提供类型
T
,以便进行反序列化(例如,使用
类型引用
)。如果你想将
Body
Body
放在同一个集合/数组中,我看不出这是怎么回事。集合的类型必须是不再通用的
List

我已通过使用自定义
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);
    }
}
@JsonDeserialize(使用=BodyDeserializer.class)
公共阶级团体{
@JsonProperty(“内容”)
私有内容;
@JsonProperty(“类型”)
私有字符串mType;
@JsonProperty(“预览图像url”)
私有字符串mPreviewImageUrl;
@JsonProperty(“职位”)
私人投资;
//接二连三
}
公共类BodyDeserializer扩展StdDeserializer{
私有静态最终字符串CAPTION=“CAPTION”;
私有静态最终字符串CONTENT=“CONTENT”;
私有静态最终字符串COMPLEX=“COMPLEX”;
私有静态最终字符串EMBED\u HTML=“EMBED\u HTML”;
私有静态最终字符串高度=“高度”;
私有静态最终字符串播放器\ u TEMPLATE=“播放器\ u TEMPLATE”;
私有静态最终字符串POSITION=“POSITION”;
私有静态最终字符串PREVIEW\u IMAGE\u URL=“PREVIEW\u IMAGE\u URL”;
私有静态最终字符串PROVIDER=“PROVIDER”;
私有静态最终字符串TYPE=“TYPE”;
私有静态最终字符串URL=“URL”;
私有静态最终字符串VID=“VID”;
私有静态最终字符串WATCH\u URL=“WATCH\u URL”;
私有静态最终字符串宽度=“宽度”;
公共BodyDeserializer(){
这个(Body.class);
}
受保护的BodyDeserializer(vc类){
超级(vc);
}
@凌驾
公共体反序列化(JsonParser解析器,反序列化上下文)引发IOException{
final ObjectCodec oc=parser.getCodec();
final-JsonNode=oc.readTree(解析器);
返回反序列化(节点);
}
私有主体反序列化(JsonNode节点){
最终字符串类型=node.get(type).asText();
if(复数等于(类型)){
返回反序列化的ZetoBodyWithContent(节点,类型);
}否则{
返回反序列化ZetoBodyWithString(节点,类型);
}
}
私有主体反序列化ToBodyWithString(JsonNode节点,字符串类型){
final int position=node.get(position.asInt();
返回新的Body().setContent(node.get(CONTENT).asText()).setType(type).setPosition(position);
}
私有主体反序列化ToBodyWithContent(JsonNode节点,字符串类型){
final int position=node.get(position.asInt();
final String provider=node.get(provider.astex();
最终字符串previewImageUrl=node.get(PREVIEW\u IMAGE\u URL.asText();
返回新的Body().setContent(createContent(node.get(CONTENT)))
.setType(类型)
.setProvider(提供程序)
.setPreviewImageUrl(previewImageUrl)
.设置位置(位置);
}
私有内容createContent(JsonNode节点){
final int width=node.get(width.asInt();
final int height=node.get(height).asInt();
最后一个字符串vid=node.get(vid.asText();
最后一个字符串url=node.get(url.asText();
最后一个字符串caption=node.get(caption.asText();
最后一个字符串watchUrl=node.get(WATCH_URL.asText();
最后一个字符串embedHtml=node.get(EMBED_HTML).asText();
最后一个字符串playerTemplate=node.get(PLAYER_-TEMPLATE).asText();
返回新内容().setPlayerTemplate(playerTemplate)
.setWidth(宽度)
.setHeight(高度)
.setUrl(url)
.setCaption(标题)
.setVid(vid)
.setWatchUrl(watchUrl)
.setEmbedHtml(embedHtml);
}
}

这不是最好的解决方案,但它是有效的。我现在使用,它有点简单。

感谢您的解释,但不幸的是它给出了相同的结果
LinkedHashMap
@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);
    }
}