Jackson XML解组和抽象类

时间:2014-09-22 11:36:50

标签: java xml jaxb jackson

我尝试使用Jackson 2.4.0 XmlMapper解组以下XML:

<root>
  <a/>
  <b/>
</root>

......以及以下POJO

class Root {
    @JacksonXmlElementWrapper(useWrapping = false)
    @JsonSubTypes({
        @JsonSubTypes.Type(name = "a", value = POJO_A.class),
        @JsonSubTypes.Type(name = "b", value = POJO_B.class)
    })
    public final List<AbstractPOJO> objects = new ArrayList<>();
}

我还尝试使用JAXB @XmlElements注释,结果相同,即:

Unrecognized field "a" (class Root), not marked as ignorable (1 known properties: "objects"])

所以杰克逊认为我的名单被称为&#34;对象&#34;而不是&#34; a&#34;和&#34; b&#34;。通常我会使用@JsonProperty("newName")来解决这个问题,但在这种情况下,我希望通过@JsonSubtypes@XmlElements注释来处理。

由于我无法修改输入XML,我还能做些什么,包括Jackson,Jackson XML还是JAXB注释?

更新:忘了说,问题是反序列化到同一个集合(因为我需要保持顺序,它们可以混合)。在单独的字段中执行它可以正常工作。

2 个答案:

答案 0 :(得分:1)

当我尝试peeskillet回答并更换杰克逊为JAXB unmarshaller时,使用@XmlElements / @XmlElementRefs工作(@XmlAnyElement没有出于某种原因,我得到了{{1而不是我自己的类。)

与杰克逊合作会很高兴,但与此同时,这也是让这项工作顺利进行的方法。

更新:

我做的是:

ElementNSImpl

我还向class Root { @XmlElements({ @XmlElement(name = "a", type = PojoA.class), @XmlElement(name = "b", type = PojoB.class) }) public final List<AbstractPOJO> objects = new ArrayList<>(); } 添加了@XmlRootElement(name = "a"),但我认为PojoA

时未使用@XmlElements

答案 1 :(得分:0)

  

&#34;由于我无法修改输入XML,我还能做些什么,包括Jackson,Jackson XML还是JAXB注释?&#34;

我对Jackson的Xml功能不太熟悉。但是使用JAXB Unmarshaller并对注释稍作修改,可以实现这一点。

对于List<AbstractPojo>,您可以使用@XmlAnyElement(lax = true)

使用lax = true

  

如果为true,那么当XmlAnyElement知道某个元素与标有JAXBContext的属性匹配时(例如,那个带有XmlRootElement且具有相同标记名称的类,或者&#39} ; s XmlElementDecl具有相同的标记名称),unmarshaller将急切地将此元素解组为JAXB对象,而不是将其解组为DOM

这基本上意味着,如果我们使用PojoA注释PojoB@XmlRootElement并将元素名称作为name属性(@XmlRootElement(name = "a"))传递,则按定义,这应该有用。

让我们试一试:

public abstract class AbstractPojo {
    // note this class is not annotated. It will be known in the context
    // as we're explicitly using the type int the Root class
}

@XmlRootElement(name = "a")
public class PojoA extends AbstractPojo {
}

@XmlRootElement(name = "b")
public class PojoB extends AbstractPojo {
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "root")
public class Root {

    @XmlAnyElement(lax = true)
    protected List<AbstractPojo> objects;

    public List<AbstractPojo>  getObjects() {
        if (objects == null) {
            objects = new ArrayList<>();
        }
        return this.objects;
    }
}

使用以下xml文件进行测试

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <a/> <b/> <b/> <a/> <a/> <b/> <b/>
</root>

以下测试程序

public class JaxbTest {
    private static final String FILE_NAME = "test.xml";

    public static void main(String[] args) throws Exception {
        JAXBContext context = JAXBContext.newInstance(Root.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        File f = new File(FILE_NAME);
        Root root = (Root)unmarshaller.unmarshal(f);
        List<AbstractPojo> list = root.getObjects();

        for (AbstractPojo p : list) {
            System.out.print( p instanceof PojoA ? "a " : "b ");
        }

        System.out.println();
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }
}

我们得到了我们正在寻找的结果。

  

a b b a a b b //以及我们为测试编组的xml文件内容


以下是一些很好的资源:


更新

  

好的,我明白为什么你会得到ElementNSImpl。这对我来说不起作用,我得到了ElementNSImpl对象。你知道发生了什么吗?

是的,我知道发生了什么。首先,我用xjc编译了一个xsd,它为我创建了一个ObjectFactory,它声明了元素。这就是为什么它为我工作的原因。

如果您不这样做,那么您应该明确地将PojoAPojoB放入上下文中。

JAXBContext.newInstance(Root.class, PojoA.class, PojoB.class);