杰克逊对带有泛型属性的类的序列化会丢失泛型属性的类型信息。

huangapple go评论89阅读模式
英文:

Jackson Serialization of Class with Generic Property losing type information of the generic property

问题

以下是您提供的内容的翻译部分:

我在使用Jackson的objectMapper将载荷序列化为字符串值时,遇到了保留泛型属性的类型信息的困难。

我想要实现的结果如下所示:

{"@type":"BlockChainWrapper","@id":1,"payload":{"@type":"TestClassA","@id":2,"helloWorld":"Hello World"},"nonce":255,"signature":"SGVsbG8gV29ybGQ="}

实际得到的结果如下所示(注意TestClassA缺少@type信息):

{"@type":"BlockChainWrapper","@id":1,"payload":{"@id":2,"helloWorld":"Hello World"},"nonce":255,"signature":"SGVsbG8gV29ybGQ="}

我正在使用以下测试:

class BlockChainWrapperTest extends Specification {
    
    def objectMapper = JacksonConfiguration.createObjectMapper()
    
    def "test Json marshalling"() {
        given: "一个测试载荷"
        def payload = new BlockChainWrapper<TestClassA>(
            payload: new TestClassA(),
            nonce: 255,
            signature: "Hello World".getBytes(Charset.defaultCharset())
        )
    
        when:
        def result = objectMapper.writeValueAsString(payload)
    
        then:
        assert result == "{\"@type\":\"BlockChainWrapper\",\"@id\":1,\"payload\":{\"@type\":\"TestClassA\",\"@id\":2,\"helloWorld\":\"Hello World\"},\"nonce\":255,\"signature\":\"SGVsbG8gV29ybGQ=\"}"
    }
    
    def "test TestClassA generates correct JSON"() {
        given:
        def payload = new TestClassA()
    
        when:
        def result = objectMapper.writeValueAsString(payload)
    
        then:
        assert result == "{\"@type\":\"TestClassA\",\"@id\":1,\"helloWorld\":\"Hello World\"}"
    }
}

在“test JSON Marshalling”测试中有两个版本。注释掉的版本和取消注释的版本产生完全相同的结果。

第二个测试验证TestClassA本身生成了正确的类型信息。

当包含具有泛型类型的父类时,问题仅会成为问题,随后丢失类型信息,并导致反序列化返回LinkedHashMap来表示TestClassA的内容。

TestClassA的样式如下所示:

@JsonRootName("TestClassA")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class TestClassA implements Serializable {
    private String helloWorld = "Hello World";
}

泛型类的样式如下所示:

@JsonRootName("BlockChainWrapper")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.PROPERTY, property = "@type")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class BlockChainWrapper<T> implements Serializable {
    private T payload;
}

为了完整起见,Jackson ObjectMapper的配置如下所示:

var mapper = new Jackson2ObjectMapperBuilder()
    .createXmlMapper(false)
    .modules(new JavaTimeModule(), new Jdk8Module())
    .serializationInclusion(JsonInclude.Include.NON_NULL)
    .build();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
英文:

I am having difficulty getting the type information for a generic property to be maintained when I use Jackson objectMapper to serialise the payload to a string value.

The result I would like to achieve is below.

{&quot;@type&quot;:&quot;BlockChainWrapper&quot;,&quot;@id&quot;:1,&quot;payload&quot;:{&quot;@type&quot;:&quot;TestClassA&quot;,&quot;@id&quot;:2,&quot;helloWorld&quot;:&quot;Hello World&quot;},&quot;nonce&quot;:255,&quot;signature&quot;:&quot;SGVsbG8gV29ybGQ=&quot;}

The result I actually get is as below (noting the missing @type info for the TestClassA.

{&quot;@type&quot;:&quot;BlockChainWrapper&quot;,&quot;@id&quot;:1,&quot;payload&quot;:{&quot;@id&quot;:2,&quot;helloWorld&quot;:&quot;Hello World&quot;},&quot;nonce&quot;:255,&quot;signature&quot;:&quot;SGVsbG8gV29ybGQ=&quot;}

The test I am using is:

class BlockChainWrapperTest extends Specification {

    def objectMapper = JacksonConfiguration.createObjectMapper()

    def &quot;test Json marshalling&quot;() {
        given: &quot;A test payload&quot;
        def payload = new BlockChainWrapper&lt;TestClassA&gt;(
                payload: new TestClassA(),
                nonce: 255,
                signature: &quot;Hello World&quot;.getBytes(Charset.defaultCharset())
        )

        when:
        //ObjectWriter w = objectMapper.writerFor(new TypeReference&lt;BlockChainWrapper&lt;TestClassA&gt;&gt;() { });
        //def result = w.writeValueAsString(payload)
        def result = objectMapper.writeValueAsString(payload)

        then:
        assert result == &quot;{\&quot;@type\&quot;:\&quot;BlockChainWrapper\&quot;,\&quot;@id\&quot;:1,\&quot;payload\&quot;:{\&quot;@type\&quot;:\&quot;TestClassA\&quot;,\&quot;@id\&quot;:2,\&quot;helloWorld\&quot;:\&quot;Hello World\&quot;},\&quot;nonce\&quot;:255,\&quot;signature\&quot;:\&quot;SGVsbG8gV29ybGQ=\&quot;}&quot;
    }

    def &quot;test TestClassA generates correct JSON&quot;() {
        given:
        def payload = new TestClassA()

        when:
        def result = objectMapper.writeValueAsString(payload)

        then:
        assert result == &quot;{\&quot;@type\&quot;:\&quot;TestClassA\&quot;,\&quot;@id\&quot;:1,\&quot;helloWorld\&quot;:\&quot;Hello World\&quot;}&quot;
    }
}

There are two versions in the "test JSON Marshalling" test. Both the commented out version, and the uncommented version produce exactly the same results.

The second test validates that TestClassA generates the correct type information in its own right.

The issue only becomes an issue when the parent class with the Generic type is incorporated, subsequently losing the type information, and causing deserialisation to return a LinkedHashMap for the contents of TestClassA instead.

TestClassA looks like:

@JsonRootName(&quot;TestClassA&quot;)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = &quot;@type&quot;)
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class TestClassA implements Serializable {

    private String helloWorld = &quot;Hello World&quot;;
}

The generic class looks like:

@JsonRootName(&quot;BlockChainWrapper&quot;)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.PROPERTY, property = &quot;@type&quot;)
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class BlockChainWrapper&lt;T&gt; implements Serializable {

    private T payload;

For completeness the Jackson ObjectMapper configuration is

var mapper = new Jackson2ObjectMapperBuilder()
    .createXmlMapper(false)
    .modules(new JavaTimeModule(), new Jdk8Module())
    .serializationInclusion(JsonInclude.Include.NON_NULL)
    .build();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

答案1

得分: 1

我认为我已经修复了你的问题。
我创建了一个 GitHub 项目来解决这个问题。
https://github.com/GaetanoPiazzolla/stackoverflow-question-61025761

简而言之,你还应该在你的 Payload 字段上加上 @JsonTypeInfo 注解:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type")
private T payload;
英文:

I think I've fixed your problem.
I've created a github project with the solution.
https://github.com/GaetanoPiazzolla/stackoverflow-question-61025761

In short, you should put the @JsonTypeInfo annotation also on your Payload field:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property=&quot;@type&quot;)
private T payload;

huangapple
  • 本文由 发表于 2020年4月4日 16:50:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/61025761.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定