2017-06-14 65 views
1

我是新来的杰克逊,并有通用字段反序列化JSON的问题。这是我想用Jackson解析的JSON。如何在运行时泛型中使用Jackson?

{ 
    "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] > contentobject,但topic > body > element[1] > contentstringbody元素可能只包含stringobject或两者。

这里是针对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 
} 

我尝试映射JSON使用JsonSubTypes批注POJO,所以如果type等于complex然后JSON应该被映射到Content类,对于simple类型的映射类应该是String对象。问题是,杰克逊转换complex内容到LinkedHashMap我不想要的。对于simple内容没有问题,它将被转换为String,但我认为杰克逊使用内部逻辑来映射这种正确的方式。

如果我尝试使用JsonDeserialize注释,则不会调用解串器方法。这就像杰克逊忽视注释并做出自己的工作。

我在哪里犯了一个错误?我应该怎么做才能解析complex内容到Content POJO?

回答

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,这有点简单。

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 

杰克逊将寻找typecontent领域内的JSON。但在你的情况下,type是JSON中body数组的元素,与content的级别相同。在这种情况下,你必须指定

include = JsonTypeInfo.As.EXTERNAL_PROPERTY 

所以杰克逊会寻找typecontent领域之外的JSON。

请记住,如果您有类似Body<T>的泛型类别,则必须向Jackson提供类型为T的反序列化(例如,使用TypeReference)。如果你想在同一个集合/数组中有Body<String>Body<Content>,我不会看到这是如何工作的。收集的类型必须是List<Body>,这不是通用的。

+0

感谢您的解释,但不幸的是它给出了相同的结果'LinkedHashMap'。 – user2319066