Jackson XML 抽象类型的反序列化导致具有空值的字段
Jackson XML deserialization of abstract type results in fields with null values
当尝试将 XML 反序列化为扩展抽象基础 class 的对象时,我看到引用列表包含预期数量的元素,但这些元素的所有字段对象为空。
这只有在我为摘要 class 创建 XML reader 时才会发生。如果我直接反序列化到具体实现,所有字段都具有预期值。
我在下面添加了最低限度的工作示例
预期输出(为了便于阅读,json)
{
"References": [ { "id": "1", "Type": "Secondary Image" } ]
}
实际输出(为了便于阅读,json)
{
"References": [ { "id": null, "Type": null } ]
}
测试数据
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Foo TypeID="ConcreteA">
<Reference ID="1" Type="Secondary Image">
<Values/>
</Reference>
</Foo>
抽象基础class
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "TypeID", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = ConcreteClassA.class, name = "ConcreteA")
})
public abstract class AbstractBase {
@JacksonXmlProperty(localName = "TypeID", isAttribute = true)
private String typeId;
@JsonIgnore
private List<Reference> references = new ArrayList<>();
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "Reference")
public List<Reference> getReferences() {
return references;
}
@JsonSetter
public AbstractBase setReferences(List<Reference> references) {
this.references.addAll(references);
return this;
}
}
具体实现
public class ConcreteClassA extends AbstractBase {}
测试用例
public class DeserializationTest {
@Test
public void deserializedAbstractClass_fieldsShouldNotBeNull() throws JsonProcessingException {
var mapper = new XmlMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.deactivateDefaultTyping()
.registerModule(new JacksonXmlModule());
var xmlData = readTestData();
var reader = mapper.readerFor(AbstractBase.class);
var deserializedObject = reader.readValue(xmlData);
assert(deserializedObject instanceof ConcreteClassA);
var concreteClassA = (ConcreteClassA) deserializedObject;
assert(concreteClassA.getReferences().get(0).getId() != null);
}
@Test
public void deserializedConcreteClass_fieldsShouldNotBeNull() throws JsonProcessingException {
var mapper = new XmlMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.configure(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL, true)
.registerModule(new JacksonXmlModule());
var xmlData = readTestData();
var reader = mapper.readerFor(ConcreteClassA.class);
var deserializedObject = reader.readValue(xmlData);
assert(deserializedObject instanceof ConcreteClassA);
var concreteClassA = (ConcreteClassA) deserializedObject;
assert(concreteClassA.getReferences().get(0).getId() != null);
}
private String readTestData() {
try {
var datafile = getClass().getClassLoader().getResource("TestData.xml");
return Files.lines(Paths.get(datafile.toURI())).collect(Collectors.joining());
} catch (Exception e) { return ""; }
}
}
原来有多个问题,我已经设法解决了。
- 我使用的 Jackson 版本 (2.11) 在多个元素使用相同标签而不是包装器时存在一些问题。这是我所知道的,也是我的 setter 执行“addAll”而不只是设置列表的原因
此问题已通过升级到 2.12 解决,这意味着不再需要执行此操作来处理展开的元素。
- Jackson 未能正确反序列化项目,因为 setter 接受一个列表,显然由于一些 java 通用的胡言乱语而中断(我一直无法弄清楚原因,只是它发生了)。
我通过让 setter 接受单个元素,然后将其添加到列表中来解决这个问题
@JsonSetter(value = "Reference")
public AbstractBase setReferences(Reference reference) {
this.references.add(references);
return this;
}
当尝试将 XML 反序列化为扩展抽象基础 class 的对象时,我看到引用列表包含预期数量的元素,但这些元素的所有字段对象为空。
这只有在我为摘要 class 创建 XML reader 时才会发生。如果我直接反序列化到具体实现,所有字段都具有预期值。
我在下面添加了最低限度的工作示例
预期输出(为了便于阅读,json)
{
"References": [ { "id": "1", "Type": "Secondary Image" } ]
}
实际输出(为了便于阅读,json)
{
"References": [ { "id": null, "Type": null } ]
}
测试数据
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Foo TypeID="ConcreteA">
<Reference ID="1" Type="Secondary Image">
<Values/>
</Reference>
</Foo>
抽象基础class
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "TypeID", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = ConcreteClassA.class, name = "ConcreteA")
})
public abstract class AbstractBase {
@JacksonXmlProperty(localName = "TypeID", isAttribute = true)
private String typeId;
@JsonIgnore
private List<Reference> references = new ArrayList<>();
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "Reference")
public List<Reference> getReferences() {
return references;
}
@JsonSetter
public AbstractBase setReferences(List<Reference> references) {
this.references.addAll(references);
return this;
}
}
具体实现
public class ConcreteClassA extends AbstractBase {}
测试用例
public class DeserializationTest {
@Test
public void deserializedAbstractClass_fieldsShouldNotBeNull() throws JsonProcessingException {
var mapper = new XmlMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.deactivateDefaultTyping()
.registerModule(new JacksonXmlModule());
var xmlData = readTestData();
var reader = mapper.readerFor(AbstractBase.class);
var deserializedObject = reader.readValue(xmlData);
assert(deserializedObject instanceof ConcreteClassA);
var concreteClassA = (ConcreteClassA) deserializedObject;
assert(concreteClassA.getReferences().get(0).getId() != null);
}
@Test
public void deserializedConcreteClass_fieldsShouldNotBeNull() throws JsonProcessingException {
var mapper = new XmlMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.configure(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL, true)
.registerModule(new JacksonXmlModule());
var xmlData = readTestData();
var reader = mapper.readerFor(ConcreteClassA.class);
var deserializedObject = reader.readValue(xmlData);
assert(deserializedObject instanceof ConcreteClassA);
var concreteClassA = (ConcreteClassA) deserializedObject;
assert(concreteClassA.getReferences().get(0).getId() != null);
}
private String readTestData() {
try {
var datafile = getClass().getClassLoader().getResource("TestData.xml");
return Files.lines(Paths.get(datafile.toURI())).collect(Collectors.joining());
} catch (Exception e) { return ""; }
}
}
原来有多个问题,我已经设法解决了。
- 我使用的 Jackson 版本 (2.11) 在多个元素使用相同标签而不是包装器时存在一些问题。这是我所知道的,也是我的 setter 执行“addAll”而不只是设置列表的原因
此问题已通过升级到 2.12 解决,这意味着不再需要执行此操作来处理展开的元素。
- Jackson 未能正确反序列化项目,因为 setter 接受一个列表,显然由于一些 java 通用的胡言乱语而中断(我一直无法弄清楚原因,只是它发生了)。
我通过让 setter 接受单个元素,然后将其添加到列表中来解决这个问题
@JsonSetter(value = "Reference")
public AbstractBase setReferences(Reference reference) {
this.references.add(references);
return this;
}