如何使用@XmlElements将不同的对象放入同一个列表中?
问题描述:
我想用jackson
XML映射到地图下面的XML(我有控制的,并从web服务获得)来一个Java bean:如何使用@XmlElements将不同的对象放入同一个列表中?
<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
,这会产生以下豆:
@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
,但都没有工作。
答
的解决方案是利用:xsdtojava
生成期间
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
</dependency>
并采用-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;
答
我做了一个新的测试:
的XSD:
让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;
}
}
(像你这样)的XJC插件生成
<?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>
的JAVA
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输入,就像我从webservice那里得到的一样!我只能重写我的java bean,它有什么看起来像不变的XML? – membersound
此外,我想依靠'xsdtojava'代替自己定义jaxb模型,因为类可能需要在未来再次自动生成。我能否实现两个列表的自动生成(如你的例子)而不是一个'@ XmlElements'列表? – membersound
好吧,你不能修改你的输入xml,但你可以检查你的代码:你的XmlTest对我来说是不正确的输入XSD。矿正在工作。 – Tuco