2017-06-14 30 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