好的 friend ...我很难缠这个头了。我有一个XML文档,表示类型XmlTestXmlTest具有类型children的属性List<XmlTest>children是未包装的XML集合。

问题是,当我将XML源反序列化为XmlTest实例时,它失败并显示:gist

java.lang.IllegalStateException: Current state not XML_START_ELEMENT (1) but 6
    at com.fasterxml.jackson.dataformat.xml.deser.XmlTokenStream.repeatStartElement(XmlTokenStream.java:228)
    at com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser.addVirtualWrapping(FromXmlParser.java:280)
    at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer._configureParser(WrapperHandlingDeserializer.java:140)
    at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:108)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:230)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:207)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:23)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:464)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:376)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:977)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:276)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:109)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034)
    at a.test.utilities.XmlHelpers.fromXml(XmlHelpers.java:19)
    at a.UnitTest.test(UnitTest.java:22)

这是我正在使用的XML文档:gist
<test id="0">
    <test id="0.1">
        <test id="0.1.1" />
    </test>
    <test id="0.2" />
    <test id="0.3">
        <test id="0.3.1" />
    </test>
</test>

这是引起异常的单元测试:gist
package api.core.jasper;

import static org.junit.Assert.fail;

import java.util.List;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.google.common.collect.Lists;

public class UnitTest {

  @Test
  public void test() throws Exception {
    final ObjectMapper mapper = new XmlMapper();

    final XmlTest before =
        new XmlTest("0", Lists.newArrayList(new XmlTest("0.1", null),
            new XmlTest("0.2", Lists.newArrayList(new XmlTest("0.2.1", null)))));
    final String xml = mapper.writeValueAsString(before);
    final XmlTest after = mapper.readValue(xml, XmlTest.class);

    fail();
  }

  @JacksonXmlRootElement(localName = "test")
  public static class XmlTest {
    private final String id;
    private final List<XmlTest> children;

    @JsonCreator
    public XmlTest(@JsonProperty("id") final String id, @JsonProperty("tests") final List<XmlTest> children) {
      this.id = id;
      this.children = children;
    }

    @JsonProperty("id")
    @JacksonXmlProperty(localName = "id", isAttribute = true)
    public String id() {
      return id;
    }

    @JsonProperty("children")
    @JacksonXmlElementWrapper(useWrapping = false)
    @JacksonXmlProperty(localName = "test")
    public List<XmlTest> children() {
      return children;
    }
  }

}

如何将该XML文档反序列化为XmlTest实例?

编辑1

我发现更改XML文档中根元素的名称将导致成功的反序列化。这是一个有趣的行为,但对我来说不是一个选择,我无法控制应用程序中文档的构造。

这将按预期反序列化:gist
<root id="0">
    <test id="0.1">
        <test id="0.1.1" />
    </test>
    <test id="0.2" />
    <test id="0.3">
        <test id="0.3.1" />
    </test>
</root>

最佳答案

如果您编写自定义解串器,则可以使用。

package api.core.jasper;

import java.io.IOException;
import java.util.List;

import org.junit.Test;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;

public class UnitTest {

  @Test
  public void test() throws Exception {
    final ObjectMapper mapper = new XmlMapper();

    final XmlTest before =
        new XmlTest("0", Lists.newArrayList(new XmlTest("0.1", null),
            new XmlTest("0.2", Lists.newArrayList(new XmlTest("0.2.1", null)))));
    System.out.println(before);
    final String xml = mapper.writeValueAsString(before);
    System.out.println(xml);
    final XmlTest after = mapper.readValue(xml, XmlTest.class);
    System.out.println(after);
  }

  @JsonDeserialize(using = XmlTestDeserializer.class)
  @JacksonXmlRootElement(localName = "test")
  public static class XmlTest {
    @JacksonXmlProperty(localName = "id", isAttribute = true)
    public String id;

    @JacksonXmlElementWrapper(useWrapping = false)
    @JacksonXmlProperty(localName = "test")
    public List<XmlTest> children;

    public XmlTest(final String id, final List<XmlTest> children) {
      this.id = id;
      this.children = Optional.fromNullable(children).or(Lists.<XmlTest>newArrayList());
    }

    @Override
    public String toString() {
      return Objects.toStringHelper(this)
          .add("id", id)
          .add("children", children)
          .toString();
    }
  }

  public static class XmlTestDeserializer extends StdDeserializer<XmlTest> {

    protected XmlTestDeserializer() {
      super(XmlTest.class);
    }

    @Override
    public XmlTest deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException,
        JsonProcessingException {
      if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
        throw new IOException("Invalid token, expected START_OBJECT");
      }

      String id = null;
      final List<XmlTest> children = Lists.newArrayList();

      while (jp.nextToken() != JsonToken.END_OBJECT) {
        final String key = jp.getCurrentName();
        jp.nextToken();

        if ("id".equals(key)) {
          id = jp.readValueAs(String.class);
        } else if ("test".equals(key)) {
          final XmlTest child = jp.readValueAs(XmlTest.class);
          if (child != null) {
            children.add(child);
          }
        }
      }

      jp.close();

      return new XmlTest(id, children);
    }
  }
}

测试控制台输出:
XmlTest{id=0, children=[XmlTest{id=0.1, children=[]}, XmlTest{id=0.2, children=[XmlTest{id=0.2.1, children=[]}]}]}
<test id="0"><test id="0.1"/><test id="0.2"><test id="0.2.1"/></test></test>
XmlTest{id=0, children=[XmlTest{id=0.1, children=[]}, XmlTest{id=0.2, children=[XmlTest{id=0.2.1, children=[]}]}]}

10-02 18:50