Jackson 将 XML 与根序列化为抽象类

问题描述

我有一个应用程序,我需要在根级别使用不同的标签序列化 XML。

我使用以下场景实现了一个测试用例,通过相同的输入我得到了两个 xml。

<ClasstypeA>
    <fieldA>a</fieldA>
</ClasstypeA>

<ClasstypeB>
    <fieldB>b</fieldB>
</ClasstypeB>

为了序列化,我实现了这三个类:


@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
        @JsonSubTypes.Type(value = TestConcretedisconnectXML.class,name = "ClasstypeA"),@JsonSubTypes.Type(value = TestConcreteConnectXML.class,name = "ClasstypeB"),})
abstract class TestXml{

}

@Getter
@Setter
class TestConcretedisconnectXML extends TestXml{
    private String fieldA;
}

@Getter
@Setter
class TestConcreteConnectXML extends TestXml{
    private String fieldB;
}

使用下面的测试返回以下错误

    public void testDeserializeXML() throws Exception {

        objectMapper = new XmlMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        
        
        String message = "<ClasstypeA>\n" +
                "\t<fieldA>a</fieldA>\n" +
                "</ClasstypeA>";

        TestXml cxml =   this.objectMapper.readValue(message,TestXml.class);

        message = "<ClasstypeB>\n" +
                "\t<fieldB>b</fieldB>\n" +
                "</ClasstypeB>";

        cxml =   this.objectMapper.readValue(message,TestXml.class);


    }
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'fieldA' as a subtype of `br.com.eletra.filemode.uaa.converter.TestXml`: kNown type ids = [ClasstypeA,ClasstypeB]
 at [Source: (StringReader); line: 2,column: 2]

但是,在结构的开头插入任何标签时,序列化是成功的。

enter image description here

我用推导类型的jackson 2.12做了另一个测试。

@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
        @JsonSubTypes.Type(ClasstypeA.class),@JsonSubTypes.Type(ClasstypeB.class),@JsonSubTypes.Type(DefaultClass.class),})
abstract class TestXml{

}

@NoArgsConstructor
@Getter
@Setter
class ClasstypeA extends TestXml{
    private String fieldA;
}

@NoArgsConstructor
@Getter
@Setter
class ClasstypeB extends TestXml{
    private String fieldB;
}

@NoArgsConstructor
@Getter
@Setter
class DefaultClass extends TestXml{

}

但是抛出了一个没有详细信息的空错误

java.lang.NullPointerException
    at com.fasterxml.jackson.dataformat.xml.util.StaxUtil.sanitizeXmlTypeName(StaxUtil.java:81)
    at com.fasterxml.jackson.dataformat.xml.XmlTypeResolverBuilder.typeProperty(XmlTypeResolverBuilder.java:45)
    at com.fasterxml.jackson.dataformat.xml.XmlTypeResolverBuilder.typeProperty(XmlTypeResolverBuilder.java:25)
    at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector._findTypeResolver(JacksonAnnotationIntrospector.java:1517)
    at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.findTypeResolver(JacksonAnnotationIntrospector.java:572)
    at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.findTypeResolver(AnnotationIntrospectorPair.java:236)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findTypeDeserializer(BasicDeserializerFactory.java:1754)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:597)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4731)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4592)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3546)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3514)
    at br.com.eletra.filemode.uaa.converter.XmlConvertTest.testDeserializeXML(XmlConvertTest.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at junit.framework.TestCase.runTest(TestCase.java:177)
    at junit.framework.TestCase.runBare(TestCase.java:142)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:130)
    at junit.framework.TestSuite.runTest(TestSuite.java:241)
    at junit.framework.TestSuite.run(TestSuite.java:236)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:90)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)


Process finished with exit code -1

有什么建议可以实现这样的东西,而不在运行时插入标签吗?

解决方法

您可能会尝试使用 DEDUCTION 来查找子类型,因此根标记名称无关紧要,唯一使用的是字段:

@JsonTypeInfo(use=Id.DEDUCTION,defaultImpl = DefaultClassToUse.class)
...