使用通用 属性 的 Class 的 Jackson 序列化丢失了通用 属性 的类型信息
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: "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\"}"
}
}
"test JSON Marshalling"测试有两个版本。注释掉的版本和未注释的版本都产生完全相同的结果。
第二个测试验证 TestClassA 是否生成了正确的类型信息。
只有当具有通用类型的父 class 被合并时,这个问题才会成为一个问题,随后丢失类型信息,并导致反序列化为 return 一个 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";
}
通用 class 看起来像:
@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);
我想我已经解决了你的问题。
我已经使用该解决方案创建了一个 github 项目。
https://github.com/GaetanoPiazzolla/Whosebug-question-61025761
简而言之,您应该将 @JsonTypeInfo 注释也放在您的 Payload 字段上:
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type")
private T payload;
当我使用 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: "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\"}"
}
}
"test JSON Marshalling"测试有两个版本。注释掉的版本和未注释的版本都产生完全相同的结果。
第二个测试验证 TestClassA 是否生成了正确的类型信息。
只有当具有通用类型的父 class 被合并时,这个问题才会成为一个问题,随后丢失类型信息,并导致反序列化为 return 一个 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";
}
通用 class 看起来像:
@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);
我想我已经解决了你的问题。 我已经使用该解决方案创建了一个 github 项目。 https://github.com/GaetanoPiazzolla/Whosebug-question-61025761
简而言之,您应该将 @JsonTypeInfo 注释也放在您的 Payload 字段上:
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type")
private T payload;