【问题标题】:How to use Jackson with runtime generics?如何将 Jackson 与运行时泛型一起使用?
【发布时间】:2017-06-14 20:04:33
【问题描述】:

我是 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": [
      {
        ...
      },
      ...
    ]
  }
}

注意

topic &gt; body &gt; element[0] &gt; contentobject,但 topic &gt; body &gt; element[1] &gt; contentstringbody 元素可能只包含 strings 或 objects 或两者。

这里是 bodycontent 的 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 类,对于 simple 类型,映射类应该是 @987654337 @ 目的。问题是杰克逊将complex 内容转换为LinkedHashMap 我不想要的内容。对于simple 的内容没有问题,它会被转换为String,但我认为Jackson 使用内部逻辑来映射这种正确的方式。

如果我尝试使用JsonDeserialize 注释,则不会调用任何反序列化器方法。就像杰克逊忽略了注释,自己做事一样。

我在哪里做错了?我应该怎么做才能将complex内容解析为Content POJO?

【问题讨论】:

    标签: java json generics jackson deserialization


    【解决方案1】:

    @JsonTypeInfo@JsonSubTypes 旨在帮助继承而不是泛型。由于StringContent 都隐式扩展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
    

    Jackson 将在 JSON 的 content 字段中查找 type。但在您的情况下,type 位于 JSON 中body 数组的元素中,与content 处于同一级别。在这种情况下,您必须指定

    include = JsonTypeInfo.As.EXTERNAL_PROPERTY
    

    所以 Jackson 将在 JSON 中的 content 字段之外查找 type

    请记住,如果您有像 Body&lt;T&gt; 这样的泛型类,则必须向 Jackson 提供类型 T 以进行反序列化(例如,使用 TypeReference)。如果您想在同一个集合/数组中拥有Body&lt;String&gt;Body&lt;Content&gt;,我看不出这将如何工作。集合的类型必须是 List&lt;Body&gt;,这不再是通用的。

    【讨论】:

    • 感谢您的解释,但不幸的是它给出了相同的结果LinkedHashMap
    • @ManosNikolaidis 你是救世主,非常感谢,我们可以更进一步,用泛型替换Object 吗?
    【解决方案2】:

    我已经通过使用自定义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,简单一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-12
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-06
      • 2022-11-02
      相关资源
      最近更新 更多