简体   繁体   English

Jackson XML 抽象类型的反序列化导致具有 null 值的字段

[英]Jackson XML deserialization of abstract type results in fields with null values

When trying to deserialize XML to an object that extends an abstract base class, I'm seeing that the list of references contains the expected number of elements, but all the fields on those objects are null.当尝试将 XML 反序列化为扩展抽象基 class 的 object 时,我看到引用列表包含预期数量的元素,但这些对象上的所有字段都是 null。

This only happens if I create an XML reader for the abstract class. If I deserialize directly to the concrete implementation all the fields have the expected value.仅当我为抽象 class 创建一个 XML 读取器时才会发生这种情况。如果我直接反序列化为具体实现,则所有字段都具有预期值。

I've added the minimum working example below我在下面添加了最低限度的工作示例

Expected output (as json for readability)预期为 output(作为 json 以提高可读性)

{
 "References": [ { "id": "1", "Type": "Secondary Image" } ]
}

Actual output (as json for readability)实际 output(作为 json 以提高可读性)

{
  "References": [ { "id": null, "Type": null } ]
}

Test Data测试数据

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Foo TypeID="ConcreteA">
    <Reference ID="1" Type="Secondary Image">
        <Values/>
    </Reference>
</Foo>

Abstract base class摘要基地 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;
    }
}

Concrete Implementation具体实施

public class ConcreteClassA extends AbstractBase {}

Test Cases测试用例

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 ""; }
    }
}

Turns out there are multiple problems, that I've managed to solve.事实证明,我已经设法解决了多个问题。

  1. The Jackson version I was using (2.11) had some problems with multiple elements using the same tag, not in a wrapper.我使用的 Jackson 版本 (2.11) 在使用相同标签的多个元素方面存在一些问题,而不是在包装器中。 This is something I was aware of, and is the reason why my setter does "addAll" instead of just setting the list这是我所知道的,也是我的设置器执行“addAll”而不只是设置列表的原因

This problem was solved by upgrading to 2.12, which means that it's no longer necessary to do this to handle unwrapped elements.此问题已通过升级到 2.12 得到解决,这意味着不再需要执行此操作来处理展开的元素。

  1. Jackson failed to properly deserialize the items, because the setter accepts a list, which apparently breaks due to some java generic mumbo jumbo (I was never able to figure out exactly why, just that it happens). Jackson 未能正确反序列化项目,因为设置器接受了一个列表,该列表显然由于一些 java 通用的胡言乱语而中断(我一直无法弄清楚为什么,只是它发生了)。

I solved this by having the setter accept a single element, and then adding that to the list我通过让 setter 接受单个元素,然后将其添加到列表中来解决这个问题

@JsonSetter(value = "Reference")
public AbstractBase setReferences(Reference reference) {
    this.references.add(references);
    return this;
}

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

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