【问题标题】:How to different objects into the same list using @XmlElements?如何使用@XmlElements 将不同的对象放入同一个列表中?
【发布时间】:2017-10-18 11:05:37
【问题描述】:

我想使用jackson xml 映射器将以下 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,但都没有成功。

【问题讨论】:

    标签: java xml spring jaxb jackson


    【解决方案1】:

    我做了一个新的测试:

    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

    【讨论】:

    • 好吧,但正如所写,我不能修改 xml 输入,因为我是从 web 服务中获得的!我只能重写我的 java bean,未修改的 xml 会是什么样子?
    • 此外,我想依靠xsdtojava generation 而不是自己定义 jaxb 模型,因为将来可能需要再次自动生成类。我可以自动生成两个列表(如您的示例)而不是一个 @XmlElements 列表吗?
    • 好吧,你不能修改你的输入 xml,但你可以查看你的代码:你的 XmlTest 对我来说是不正确的输入 XSD。矿山正在工作。
    • 我不这么认为,但不是 100% 确定
    • 这是 Jackson2 的问题,我已经用 JAXB 进行了测试,它工作正常(请参阅我的上次编辑)。我不知道如何解决杰克逊的问题
    【解决方案2】:

    解决方案是利用:

    <dependency>
        <groupId>org.jvnet.jaxb2_commons</groupId>
        <artifactId>jaxb2-basics</artifactId>
    </dependency>
    

    在xsdtojava 生成期间使用-Xsimplify。

    并为该元素显式定义绑定:

    <jaxb:bindings
            xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
            xmlns:simplify="http://jaxb2-commons.dev.java.net/basic/simplify"
            jaxb:extensionBindingPrefixes="xjc simplify"
            jaxb:version="2.1">
    
          <jaxb:bindings schemaLocation="xsd/test.xsd">
                    <jaxb:bindings multiple="true" node="//xs:element[@name='foo']//xs:complexType//xs:sequence">
                        <simplify:as-element-property/>
                    </jaxb:bindings>
          </jaxb:binding>
    </jaxb:bindings>
    

    这将为每种类型生成两个单个元素:

    private List<FirstType> firstType;
    private List<SecondType> secondType;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多