Jackson serialization/deserialization 使用@JsonTypeInfo 时出现问题。有人可以向我解释这是如何工作的吗?

Jackson serialization/deserialization problems when using @JsonTypeInfo. Can someone explain to me how this is working?

说我有这些 classes

public abstract class Shape {}

public class Circle extends Shape{
    private int r;
    ...
}

public class Main {
    public static void main(String [] args) {
         String json = "{\"r\": 25 }";
         ObjectMapper om = new ObjectMapper();

         //WORKS FINE
         Circle circle = om.readValue(json, Circle.class); 
    }
}

现在,假设我想添加另一个 class,它将包含一个 Shape 对象的列表。我知道要反序列化这个 class,我必须添加 @JsonTypeInfo。

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Circle.class, name = "circle")
})
public abstract class Shape {}

public class Circle extends Shape{...}

public class ListOfShapes {
    List<Shape> shapes;
}

public class Main {
    public static void main(String [] args) {
         String json = "{\"r\": 25 }";
         ObjectMapper om = new ObjectMapper();

         //DOES NOT WORK
         Circle circle = om.readValue(json, Circle.class); 
    }
}

我现在可以使用这些注释成功序列化 ListOfShapes 对象,但我无法再序列化圆 class。我还注意到,当我序列化 class 时,json 模式发生了一些变化,所以我现在有一个额外的键(“type”):

{
    "type": "circle",
    "r": 5
}

我目前的解决方法是通过添加新的键值对来稍微更改 json 字符串:

String json = "{\"type\": \"circle\", \"r\": 25 }";

谁能给我解释一下这是怎么回事? 为什么我无法再序列化圆形对象? 为什么 json 架构会因添加新的键值对而发生变化?

感谢您的宝贵时间。

一切正常。当您将 JsonTypeInfoJsonSubTypes 注释添加到 Shape class 时,如下所示:

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Circle.class, name = "circle")
})
public abstract class Shape {}

您正在通知杰克逊您希望在每个 json 中找到一个名为 type 的 属性 并反序列化为 Shape class 并且如果不存在,杰克逊将引发错误,而且 type 属性 将使用其值进行序列化。您还通知杰克逊,如果与 type 属性 关联的值是 circle,则 json 必须反序列化为 Circle对象。

当您不知道必须反序列化或序列化哪种类型的对象时,此行为特别有用,如下例所示:

String json = "{\"type\": \"circle\", \"r\": 25 }";
ObjectMapper om = new ObjectMapper();
//I don't know json is a circle object but it can be deserialized as a Shape
Shape circle = om.readValue(json, Shape.class);
System.out.println(circle instanceof Circle); //<-- it prints true
//ok type is present with value circle {"type":"circle","r":25}
System.out.println(mapper.writeValueAsString(circle)); 

同样的方法用于 ListOfShapes 包装器 class:

String json = "{\"type\": \"circle\", \"r\": 25 }";
ObjectMapper om = new ObjectMapper();
Shape circle = om.readValue(json, Shape.class);
//instantiate the wrapper class
ListOfShapes listOfShapes = new ListOfShapes();
listOfShapes.shapes = List.of(circle);
json = mapper.writeValueAsString(listOfShapes);
//it prints {"shapes":[{"type":"circle","r":25}]}
System.out.println(json);
listOfShapes = om.readValue(json, ListOfShapes.class);
//ok list inside contains the Circle object
System.out.println(listOfShapes);