简体   繁体   English

编写多个自定义Jackson解串器以处理继承的正确方法

[英]Correct way for writing multiple custom Jackson deserializers to handle inheritance

I want to deserialize a quite complicated set of JSON documents with Jackson. 我想用Jackson来反序列化一组相当复杂的JSON文档。 To handle inheritance I implemented some custom deserializers. 为了处理继承,我实现了一些自定义反序列化器。

To choose the correkt class I have to check the properties of the next node. 要选择correkt类,我必须检查下一个节点的属性。 Therefore I read the tree, check the properties and choose the correct class. 因此,我阅读了树,检查了属性并选择了正确的类。

After that I read the JSON via mapper.readerFor(targetClass).readValue(rootNode) . 之后,我通过mapper.readerFor(targetClass).readValue(rootNode)读取JSON。 Everything is fine until here. 一切都很好,直到这里。

But as I use mapper.readerFor(...) the next called serializer gets an ObjectReader instance instead of an ObjectMapper . 但是当我使用mapper.readerFor(...) ,下一个调用的序列化程序将获得一个ObjectReader实例,而不是ObjectMapper But I need an ObjectMapper instance. 但是我需要一个ObjectMapper实例。

How could I do it better? 我怎样才能做得更好?

Here is one of my deserializers, which cause my problem: 这是我的反序列化器之一,它导致我的问题:

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;
}

This may be easier than you think, although I can't tell for sure without seeing some of your JSON examples. 这可能比您想像的要容易,尽管我在看不到您的一些JSON示例的情况下无法确定。 Jackson can deal with polymorphism directly without custom serializers using the @JsonTypeInfo and @JsonSubTypes annotations, so long as there is a field with a value indicating which subclass to create. 杰克逊可以使用@JsonTypeInfo和@JsonSubTypes批注直接处理多态性,而无需使用自定义序列化程序,只要存在一个带有值的字段来指示要创建哪个子类即可。 For example, suppose I have two different documents that share some common fields: 例如,假设我有两个共享相同字段的不同文档:

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

This could be annotated with: 可以用以下注释:

@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;
}

So the solution was quite simple. 因此,解决方案非常简单。 Instead calling mapper.readerFor(targetClass).readValue(root) to deserialize the node tree into an object I had to call mapper.treeToValue(root, targetClass) . 而是调用mapper.readerFor(targetClass).readValue(root)将节点树反序列化为对象,我必须调用mapper.treeToValue(root, targetClass)

Here is the working version of the method I posted in my question: 这是我在问题中发布的方法的工作版本:

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM