简体   繁体   English

Jackson 无法反序列化它使用 enableDefaultTyping() 序列化的 JSON

[英]Jackson can't deserialize JSON it serializes with enableDefaultTyping()

Using Jackson 2.9.5, I am serializing an object to JSON and deserializing it back to a Java object.使用 Jackson 2.9.5,我将对象序列化为 JSON 并将其反序列化为 Java 对象。 Upon deserializing the JSON, Jackson throws this exception:反序列化 JSON 后,Jackson 抛出此异常:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of 'com.xxx.models.Header' out of START_ARRAY token
     at [Source: (String)"{
      "header" : [ "com.xxx.models.Header", {
        "sourceAddress" : 0,
        "destinationAddress" : 1, ...

That's semi-understandable because the deserialized JSON looks like this:这是半可以理解的,因为反序列化的 JSON 如下所示:

{
  "header" : [ "com.xxx.models.Header", {
    "sourceAddress" : 0,
    "destinationAddress" : 1
]}

Jackson adds the array syntax when mapper.enableDefaultTyping() , mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE) , or mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS) is used and writeValueAsString() is called.当使用mapper.enableDefaultTyping()mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE)mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS)并调用writeValueAsString()时,Jackson 添加数组语法

But calling readValue() on the same object mapper to deserialize the same JSON it just generated throws the above exception.但是在同一个对象映射器上调用readValue()来反序列化它刚刚生成的同一个 JSON 会引发上述异常。 Why?为什么? What am I doing wrong?我究竟做错了什么?

I should note that if I strip out the added [ "com.xxx.models.Header", bit (and its corresponding array terminator) the JSON is parsed as expected and the deserialized object is fully populated.我应该注意,如果我[ "com.xxx.models.Header",添加的[ "com.xxx.models.Header", bit(及其相应的数组终止符),JSON 将按预期解析,反序列化对象已完全填充。

It seems like this is specifically related to polymorphism, so here are the object definitions.似乎这与多态性有关,所以这里是对象定义。 A SerialMessage contains an IHeader and IPayload . SerialMessage包含一个IHeaderIPayload A Header extends an AbstractHeader which implements IHeader and is what I'm serializing and can't deserialize. Header扩展了一个AbstractHeader ,它实现了IHeader并且是我正在序列化并且无法反序列化的内容。

public class SerialMessage {

    private IHeader header;
    private IPayload payload;

    public SerialMessage() {};
    public SerialMessage(IHeader header) {
        this.header = header;
    }

    public SerialMessage(IHeader header, IPayload payload) {
        this(header);
        this.payload = payload;
    };

    public IHeader getHeader() {
        return header;
    }
    public void setHeader(Header header) {
        this.header = header;
    }
    public IPayload getPayload() {
        return payload;
    }
    public void setPayload(IPayload payload) {
        this.payload = payload;
    }
}

. .

public class AbstractHeader implements IHeader {

    protected short sourceAddress;
    protected short destinationAddress;

    public short getSourceAddress() {
        return sourceAddress;
    }

    public void setSourceAddress(short sourceAddress) {
        this.sourceAddress = sourceAddress;
    }

    public short getDestinationAddress() {
        return destinationAddress;
    }

    public void setDestinationAddress(short destinationAddress) {
        this.destinationAddress = destinationAddress;
    }
}

. .

public class Header extends AbstractHeader {
}

This is a consistent problem with deserializing Jackson-serialized objects with enableDefaultTyping() , mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE) , or mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS) . 这是使用enableDefaultTyping()mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE)mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS)反序列化Jackson序列化对象时的一致问题。

The root cause seems to be that Jackson simply doesn't understand its own wrapper syntax generated from these options. 根本原因似乎是Jackson根本不理解从这些选项生成的自己的包装器语法。 After much experimentation, Jackson was only able to deserialize the object hierarchy (from the question) it had serialized if the objects were annotated with @JsonType. 经过大量实验后, 如果用@JsonType注释对象 ,Jackson只能反序列化已序列化的对象层次结构(根据问题)

In the case shown in the question, the interface IHeader can be annotated as: 在问题所示的情况下,可以将接口IHeader注释为:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
public interface IHeader {
...
}

which produces the JSON 产生JSON

{
  "header" : {
    "@class" : "com.xxx.models.Header",
    "sourceAddress" : 0,
    "destinationAddress" : 1
}

instead of wrapping each object with an array syntax to indicate its implementation type. 而不是使用数组语法包装每个对象以指示其实现类型。

I was not able to find a way to tell ObjectMapper to use the equivalent of JsonTypeInfo.As.PROPERTY. 我无法找到一种方法来告诉ObjectMapper使用等效的JsonTypeInfo.As.PROPERTY。 Annotating every interface with @JsonType is not ideal, but does provide a work-around the original problem. @JsonType注释每个接口不是理想的,但是确实提供了解决原始问题的方法。

See Jackson's documentation for @JsonType at https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization#12-per-class-annotations . 请参阅https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization#12-per-class-annotations的 @JsonType Jackson文档。

    public IHeader getHeader() {
        return header;
    }
    public void setHeader(Header header) {
        this.header = header;
    }

The cause of the problem is that the getter of header field return a Interface type of IHeader , but the setter takes a concrete type of Header .问题的原因是header字段的 getter 返回了IHeaderInterface类型,但 setter 接受了Header的具体类型。 When JSON serializing, the type of header field decides by its getter, that is IHeader , and IHeader is a polymorphic type. JSON 序列化时, header字段的类型由其 getter 决定,即IHeader ,而IHeader是多态类型。 When JSON deserializing, the type of header field decides by its setter's parameter type, that is Header , and Header is a concrete type. JSON 反序列化时, header字段的类型由其 setter 的参数类型决定,即HeaderHeader是具体类型。 so at this point, Jackson throws a exception with this message.所以在这一点上,杰克逊用这条消息抛出一个异常。

"Can not deserialize instance of xxx out of START_ARRAY token" “无法从 START_ARRAY 令牌中反序列化 xxx 的实例”

More details you can refer to com.fasterxml.jackson.databind.ser.BeanSerializerFactory#_constructWriter and com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#addBeanProps更多细节可以参考com.fasterxml.jackson.databind.ser.BeanSerializerFactory#_constructWritercom.fasterxml.jackson.databind.deser.BeanDeserializerFactory#addBeanProps

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

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