2017-03-04 30 views
0

我想用Jackson反序列化一組相當複雜的JSON文檔。爲了處理繼承,我實現了一些自定義的反序列化器。編寫多個自定義Jackson反串行器來處理繼承的正確方法

要選擇correkt類,我必須檢查下一個節點的屬性。因此,我閱讀樹,檢查屬性並選擇正確的類。

之後,我通過mapper.readerFor(targetClass).readValue(rootNode)閱讀了JSON。一切都很好,直到這裏。

但是,當我使用mapper.readerFor(...)時,下一個被調用的串行器獲取ObjectReader實例而不是ObjectMapper。但我需要一個ObjectMapper實例。

我怎麼能做得更好?

這裏是我的解串器,這導致我的問題之一:

public AbstractParametersObject deserialize(JsonParser p, DeserializationContext ctxt) 
    throws IOException, JsonProcessingException { 

    Class<? extends AbstractParametersObject> targetClass = null; 
    ObjectMapper mapper = (ObjectMapper) p.getCodec(); 
    ObjectNode root =mapper.readTree(p); 
    boolean isReference = root.has("$ref"); 
    boolean isParameter = root.has("in"); 

    if (isReference) targetClass = ParameterAsReference.class; 
    else if (isParameter) { 
     targetClass = Optional.of(root.get("in")).map(JsonNode::asText).map(value -> { 
      Class<? extends AbstractParametersObject> effectiveClass = null; 

      switch (value) { 
       case "body": effectiveClass = BodyParameterObject.class; 
        break; 
       case "query": effectiveClass = QueryParameterObject.class; 
        break; 
       case "path": effectiveClass = PathParameterObject.class; 
        break; 
       case "formData": effectiveClass = FormDataParameterObject.class; 
        break; 
       case "header": effectiveClass = HeaderParameterObject.class; 
        break; 
      } 

      return effectiveClass; 
     }).orElseThrow(() -> new IllegalArgumentException("todo")); 
    } 

    AbstractParametersObject parametersObject = mapper.readerFor(targetClass) 
                 .readValue(root); 
    return parametersObject; 
} 

回答

0

因此,解決方案是非常簡單的。相反,調用mapper.readerFor(targetClass).readValue(root)將節點樹反序列化爲一個對象,我不得不打電話給mapper.treeToValue(root, targetClass)

這裏是我貼在我的問題的方法的工作版本:

public AbstractParametersObject deserialize(JsonParser p, DeserializationContext ctxt) 
    throws IOException { 

    Class<? extends AbstractParametersObject> targetClass = null; 
    ObjectMapper mapper = (ObjectMapper) p.getCodec(); 
    ObjectNode root =mapper.readTree(p); 
    boolean isReference = root.has("$ref"); 
    boolean isParameter = root.has("in"); 

    if (isReference) targetClass = ParameterAsReference.class; 
    } else if (isParameter) { 
     targetClass = Optional.of(root.get("in")).map(JsonNode::asText).map(value -> { 
      Class<? extends AbstractParametersObject> effectiveClass = null; 

      switch (value) { 
       case "body": effectiveClass = BodyParameterObject.class; 
        break; 
       case "query": effectiveClass = QueryParameterObject.class; 
        break; 
       case "path": effectiveClass = PathParameterObject.class; 
        break; 
       case "formData": effectiveClass = FormDataParameterObject.class; 
        break; 
       case "header": effectiveClass = HeaderParameterObject.class; 
        break; 
      } 

      return effectiveClass; 
     }).orElseThrow(() -> new IllegalArgumentException("todo")); 
    } 

    return mapper.treeToValue(root, targetClass); 
} 
1

這可能比你想象的更容易,雖然我不能肯定地說沒有看到你們的JSON例子。傑克遜可以直接處理多態,無需使用@JsonTypeInfo和@JsonSubTypes註釋的自定義序列化器,只要有一個值指示要創建哪個子類的字段即可。例如,假設我有一些共同的領域兩個不同的文件:

{ "type":"square", "name":"my square", "x":12, "y":34, "size":22 } 
{ "type":"rectangle", "name":"my rect", "x":9, "y":11, "width":4, "height":9 } 

這可能與被註釋:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, property="type") 
@JsonSubTypes({ 
    @Type(name = "square", value = Square.class), 
    @Type(name = "rectangle", value = Rectangle.class) 
}) 
public abstract class Shape { 
    public String name; 
    public int x; 
    public int y; 
} 
public class Square extends Shape { 
    public int size; 
} 
public class Rectangle extends Shape { 
    public int width; 
    public int height; 
} 
+0

你回答的作品,但不是去對我來說有兩個原因的方式。第一個原因是我無法修改JSON值。他們必須匹配他們的模式(JSON模式)。第二個原因是,如果我檢查根節點的特定字段及其值,我只能確定目標類。我給你一個upvote。 – Oliver

+0

嗯,我看你沒有一個字段值來區分,你必須結合「$ ref」和「in」字段。 – Wheezil