如何使用@XmlElements将不同的对象放入同一个列表中?

时间:2017-10-18 11:05:37

标签: java xml spring jaxb jackson

我想使用jackson xml mapper将以下xml(我必须控制并从web服务获取)映射到java bean:

<foo>
    <first><val>some</val></first>
    <first><val>somemore</val></first>
    <second><testval>test</testval></second>
</foo>

我提供的架构是:

<xs:schema>
    <xs:include schemaLocation="firstType.xsd"/>
    <xs:include schemaLocation="secondType.xsd"/>
    <xs:element name="foo">
        <xs:complexType>
            <xs:sequence maxOccurs="unbounded">
                <xs:element ref="first" minOccurs="0"/>
                <xs:element ref="second" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

使用xsdtojava,生成以下bean:

@XmlRootElement(name = "foo")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlTest {
    @XmlElements({
        @XmlElement(name = "first", type = FirstType.class),
        @XmlElement(name = "second", type = SecondType.class)
    })
    @JsonSubTypes({
        @JsonSubTypes.Type(name = "first", value = FirstType.class),
        @JsonSubTypes.Type(name = "second" , value = SecondType.class)
    })
    private List<IType> items;

    //grouping interface
    interface IType {

    }

    @XmlRootElement(name = "first")
    @XmlAccessorType(XmlAccessType.FIELD)
    class FirstType implements IType {
        private String val;
    }

    @XmlRootElement(name = "second")
    @XmlAccessorType(XmlAccessType.FIELD)
    class SecondType implements IType {
        private String testval;
    }
}

但我的测试无法转换xml!

public static void main(String[] args) throws Exception {
        String xml =
                "<foo>" +
                        "<first><val>some</val></first>" +
                        "<second><testval>test</testval></second>" +
                "</foo>";


    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();

    ObjectMapper mapper = builder
            .modules(new JaxbAnnotationModule(), new JacksonXmlModule())
            .defaultUseWrapper(false)
            .createXmlMapper(true)
            .build();

    XmlTest unmarshal = mapper.readValue(xml, XmlTest.class);
    System.out.println(unmarshal.items); //prints 'null'
}

项目的结果列表总是null,但为什么? 我尝试了@XmlElements@JsonSubTypes,但都没有效果。

1 个答案:

答案 0 :(得分:0)

我做了一个新测试:

XSD :(和你的一样)

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/test" xmlns:tns="http://www.example.org/test" elementFormDefault="qualified">

    <element name="foo">
        <complexType>
            <sequence maxOccurs="unbounded">
                <element name="first" type="tns:FirstType"
                    maxOccurs="unbounded" minOccurs="0">
                </element>
                <element name="second" type="tns:SecondType"
                    maxOccurs="unbounded" minOccurs="0">
                </element>
            </sequence>
        </complexType>
    </element>

    <complexType name="FirstType">
        <sequence>
            <element name="val" type="string"></element>
        </sequence>
    </complexType>

    <complexType name="SecondType">
        <sequence>
            <element name="testval" type="string"></element>
        </sequence>
    </complexType>
</schema>

xjc插件生成的JAVA:

Foo.class:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "firstAndSecond"
})
@XmlRootElement(name = "foo")
public class Foo {

    @XmlElements({
        @XmlElement(name = "second", type = SecondType.class),
        @XmlElement(name = "first", type = FirstType.class)
    })
    protected List<Object> firstAndSecond;

    /**
     * Gets the value of the firstAndSecond property.
     * 
     * <p>
     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a <CODE>set</CODE> method for the firstAndSecond property.
     * 
     * <p>
     * For example, to add a new item, do as follows:
     * <pre>
     *    getFirstAndSecond().add(newItem);
     * </pre>
     * 
     * 
     * <p>
     * Objects of the following type(s) are allowed in the list
     * {@link SecondType }
     * {@link FirstType }
     * 
     * 
     */
    public List<Object> getFirstAndSecond() {
        if (firstAndSecond == null) {
            firstAndSecond = new ArrayList<Object>();
        }
        return this.firstAndSecond;
    }

    public Foo withFirstAndSecond(Object... values) {
        if (values!= null) {
            for (Object value: values) {
                getFirstAndSecond().add(value);
            }
        }
        return this;
    }

    public Foo withFirstAndSecond(Collection<Object> values) {
        if (values!= null) {
            getFirstAndSecond().addAll(values);
        }
        return this;
    }

}

FirstType类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "FirstType", propOrder = {
    "val"
})
public class FirstType {

    @XmlElement(required = true)
    protected String val;

    /**
     * Gets the value of the val property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getVal() {
        return val;
    }

    /**
     * Sets the value of the val property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setVal(String value) {
        this.val = value;
    }

    public FirstType withVal(String value) {
        setVal(value);
        return this;
    }

}

SeconType类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SecondType", propOrder = {
    "testval"
})
public class SecondType {

    @XmlElement(required = true)
    protected String testval;

    /**
     * Gets the value of the testval property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getTestval() {
        return testval;
    }

    /**
     * Sets the value of the testval property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setTestval(String value) {
        this.testval = value;
    }

    public SecondType withTestval(String value) {
        setTestval(value);
        return this;
    }

}

解组在JAXB中完美运行:

String xml = "<foo>" + "<first><val>some</val></first><second><testval>test</testval></second>" + "</foo>";

Unmarshaller un = JAXBContext.newInstance(Foo.class).createUnmarshaller();

Foo unmarshal = (Foo) un.unmarshal(new StringReader(xml));
System.out.println(unmarshal.getFirstAndSecond());
System.out.println(unmarshal.getFirstAndSecond().size());

但它不适用于Jackson2 ...... 我已经在网上做了一些研究,我看过有关Jackson处理XmlElements注释的错误的讨论

您会看到链接https://github.com/FasterXML/jackson-databind/issues/374