在使用 Jackson 序列化 class 时添加类型信息,以便可以在不使用注释的情况下使用接口类型反序列化?

Add type information when serializing class with Jackson so that it's possible to deserialize using interface type without using annotations?

这个问题与 include class name in all objects serialized by jackson 类似,但我想默认配置此问题 而不是 在每个 class 上使用注释。最好我想在序列化的 json 中添加一个 field,例如包含序列化的 class 名称的 _class_type [=53] =](最好不是完全合格的事件,但我同意)。我正在使用 Jackson 2.11.1.

例如,假设我有这个接口:

public interface MyInterface {}

和两个实现:

public class MyClass1 implements MyInterface {

    private String something;

    public String getSomething() {
        return something;
    }

    public void setSomething(String something) {
        this.something = something;
    }
}

public class MyClass2 implements MyInterface {
    
    private String somethingElse;

    public String getSomethingElse() {
        return somethingElse;
    }

    public void setSomethingElse(String somethingElse) {
        this.somethingElse = somethingElse;
    }
}

我希望能够序列化 MyClass1MyClass2 的实例,以便我可以将它们反序列化为 MyInterface(即在序列化时添加类型信息)。

我在 documentation 中读到,您应该能够像这样配置 ObjectMapper 以使其工作:

PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().build();
objectMapper.activateDefaultTyping(ptv); // default to using DefaultTyping.OBJECT_AND_NON_CONCRETE

但是当我尝试这个时:

ObjectMapper objectMapper = new ObjectMapper();
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().build();
objectMapper.activateDefaultTyping(ptv); // default to using DefaultTyping.OBJECT_AND_NON_CONCRETE

MyClass1 myClass1 = new MyClass1();
myClass1.setSomething("something");

String json = objectMapper.writeValueAsString(myClass1);

MyInterface myInterface = objectMapper.readValue(json, MyInterface.class);

我得到这个异常:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class org.something.MyInterface
 at [Source: (String)"{"something":"something"}"; line: 1, column: 1]

如果我将配置更改为使用 ObjectMapper.DefaultTyping.EVERYTHING,那么 Jackson 实际上似乎将类型包装在一个数组中,但在尝试反序列化时我仍然遇到异常:

ObjectMapper objectMapper = new ObjectMapper();
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().build();
objectMapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING);

MyClass1 myClass1 = new MyClass1();
myClass1.setSomething("something");

String json = objectMapper.writeValueAsString(myClass1);

MyInterface myInterface = objectMapper.readValue(json, MyInterface.class);

现在的例外是:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'org.something.MyClass1' as a subtype of `org.something.MyInterface`: Configured `PolymorphicTypeValidator` (of type `com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator`) denied resolution
 at [Source: (String)"["org.something.MyClass1",{"something":"something"}]"; line: 1, column: 53]

我该如何解决?

我实际上是通过像这样配置 ObjectMapper 来让它工作的:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.activateDefaultTyping(new LaissezFaireSubTypeValidator(), ObjectMapper.DefaultTyping.EVERYTHING);

不确定这是否是惯用的方法。

在构建 PolymorphicTypeValidator 时,如果您提供子 类 所在的包,它应该可以正常工作。你需要这样做:

  ObjectMapper mapper = new ObjectMapper();
  PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
                .allowIfSubType("com.example.core")
                .build();
  mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING);

com.example.core

是所有子类所在的包。