使用 Jackson 将对象列表从 XML 反序列化为 Java POJO

问题描述 投票:0回答:1

这是我的 XML:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<licenseSummary>
  <dependencies>
    <dependency>
      <groupId>Hello World 1</groupId>
    </dependency>
    <dependency>
      <groupId>Hello World 2</groupId>
    </dependency>
  </dependencies>
</licenseSummary>

这是我的模型:

@Getter
@Setter
@JacksonXmlRootElement(localName = "licenseSummary")
@EqualsAndHashCode
public class LicenseSummary {

    @NonNull
    @JacksonXmlProperty(localName = "dependency")
    @JacksonXmlElementWrapper(localName = "dependencies")
    List<Dependency> dependencies;

    @JsonCreator
    public LicenseSummary(@NonNull List<Dependency> dependencies) {
        this.dependencies = dependencies;
    }
}

@Getter
@Setter
@EqualsAndHashCode
public class Dependency {

    @NonNull
    @JacksonXmlProperty(localName = "groupId")
    private String groupId;

    @JsonCreator
    public Dependency(@NonNull String groupId) {
        this.groupId = groupId;
    }
}

当我尝试将上述 XML 反序列化到模型 POJO 中时,出现以下错误:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<de.symate.maven.plugins.licenseaggregator.model.Dependency>` from Object value (token `JsonToken.FIELD_NAME`)
 at [Source: (BufferedInputStream); line: 3, column: 3]

    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1767)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1541)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1488)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.java:402)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:254)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1490)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
    at com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:104)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4917)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3897)
    at de.symate.maven.plugins.licenseaggregator.model.LicenseSummaryTest.xmlDeserializationWorks(LicenseSummaryTest.java:30)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    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$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)

有人可以帮助我吗?

有趣的是,当我创建模型类的 POJO 并使用完全相同的映射器对其进行序列化时,上面发布的 XML 就是结果。但尝试反序列化它不起作用:(

java xml jackson fasterxml
1个回答
0
投票

据我了解https://github.com/FasterXML/jackson-dataformat-xml/issues/517,问题出在注释

@JacksonXmlElementWrapper
中,构造函数参数没有正确支持该注释(特别是如果您想要使用包装元素反序列化集合结构)

<items>
   <item></item>
   <item></item>
</items>

这就是我可以使用的解决方案。 Jackson 正常工作所需的默认构造函数可以设置为受保护,因此它不会被暴露,并且没有人可以滥用它来创建浅对象。

@Getter
@Setter
@JacksonXmlRootElement(localName = "licenseSummary")
@EqualsAndHashCode
public class LicenseSummary {

    @NonNull
    @JacksonXmlProperty(localName = "dependency")
    @JacksonXmlElementWrapper(localName = "dependencies")
    List<Dependency> dependencies;

    public LicenseSummary(@NonNull List<Dependency> dependencies) {
        this.dependencies = dependencies;
    }

    /**
     * Protected no-args constructor for Jackson, because @JacksonXmlElementWrapper does not work properly on constructor arguments.
     */
    @JsonCreator
    protected LicenseSummary() {}
}

@Getter
@Setter
@EqualsAndHashCode
public class Dependency {

    @NonNull
    @JacksonXmlProperty(localName = "groupId")
    private String groupId;

    @JsonCreator
    public Dependency(@JacksonXmlProperty(localName = "groupId") @NonNull String groupId) {
        this.groupId = groupId;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.