英文:
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.
{"@type":"BlockChainWrapper","@id":1,"payload":{"@type":"TestClassA","@id":2,"helloWorld":"Hello World"},"nonce":255,"signature":"SGVsbG8gV29ybGQ="}
The result I actually get is as below (noting the missing @type info for the TestClassA.
{"@type":"BlockChainWrapper","@id":1,"payload":{"@id":2,"helloWorld":"Hello World"},"nonce":255,"signature":"SGVsbG8gV29ybGQ="}
The test I am using is:
class BlockChainWrapperTest extends Specification {
def objectMapper = JacksonConfiguration.createObjectMapper()
def "test Json marshalling"() {
given: "A test payload"
def payload = new BlockChainWrapper<TestClassA>(
payload: new TestClassA(),
nonce: 255,
signature: "Hello World".getBytes(Charset.defaultCharset())
)
when:
//ObjectWriter w = objectMapper.writerFor(new TypeReference<BlockChainWrapper<TestClassA>>() { });
//def result = w.writeValueAsString(payload)
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\"}"
}
}
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("TestClassA")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class TestClassA implements Serializable {
private String helloWorld = "Hello World";
}
The generic class looks like:
@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;
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="@type")
private T payload;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论