我有以下架构:
<xsd:schema xmlns:bar="http://www.foo.org/bar"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:annox="http://annox.dev.java.net"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
targetNamespace="http://www.foo.org/bar"
jaxb:extensionBindingPrefixes="annox" jaxb:version="2.1" elementFormDefault="qualified">
<xsd:element name="unit" type="bar:unit" />
<xsd:complexType name="unit">
<xsd:annotation>
<xsd:appinfo>
<annox:annotate>@javax.xml.bind.annotation.XmlRootElement(name="unit")</annox:annotate>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:any processContents="skip" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
当我解组这个XML时
<unit xmlns="http://www.foo.org/bar">
<text>Name</text>
</unit>
返回的对象是javax.xml.bind.JAXBElement<Unit>
,但是我希望得到org.foo.bar.Unit
。我需要这个,因为在我的情况下解组是由JAX-RS提供者或SpringWeb隐式发生的。
观察:
<xsd:any processContents="skip" />
声明,JAXB开始返回org.foo.bar.Unit
。<xsd:element name="unit" type="bar:unit" />
声明,JAXB开始返回org.foo.bar.Unit
(虽然需要在解组时禁用验证)。因此,我会说,鉴于XSD是证明问题的最小XSD。
问题:为什么JAXB将org.foo.bar.Unit
包装到JAXBElement
以上的组合?从我看到的情况来看,XSD类型unit
无法使标记名称与unit
不同,那么为什么JAXB需要这种工厂方法?
@XmlElementDecl(namespace = "http://www.foo.org/bar", name = "unit")
public JAXBElement<Unit> createUnit(Unit value) { ... }
证明JAXB 2.2.7问题的项目是here。运行时输出以下内容:
Running org.foo.bar.UnitTest
>>> Class is: javax.xml.bind.JAXBElement
>>> XML is: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><unit xmlns="http://www.foo.org/bar"><text>Name</text></unit>
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.318 sec <<< FAILURE!
答案 0 :(得分:4)
添加到伊恩的答案,
由complex
元素命名的任何root
元素都将使用@XmlElementDecl()
注释工厂方法。
您可以通过内联移动complex
类型声明来解决此问题。
<xsd:schema xmlns= "http://www.foo.org/bar" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:annox="http://annox.dev.java.net" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
targetNamespace="http://www.foo.org/bar" jaxb:extensionBindingPrefixes="annox"
jaxb:version="2.1" elementFormDefault="qualified">
<xsd:element name="unit">
<xsd:complexType>
<xsd:annotation>
<xsd:appinfo>
<annox:annotate>@javax.xml.bind.annotation.XmlRootElement(name="unit")
</annox:annotate>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:any processContents="skip" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
ObjectFactory.class (此处未生成JAXBElement
工厂方法)
@XmlRegistry
public class ObjectFactory {
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.foo.bar
*
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link Unit }
*
*/
public Unit createUnit() {
return new Unit();
}
}
测试类:
@Test
public void testUnmarshalling() throws JAXBException, SAXException {
JAXBContext context = JAXBContext.newInstance(Unit.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setSchema(SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
.newSchema(new StreamSource(getClass().getClassLoader().getResourceAsStream("common.xsd"))));
Object unit = unmarshaller.unmarshal(getClass().getResourceAsStream("unit.xml"));
System.out.println(">>> Class is: " + unit.getClass().getName());
StringWriter writer = new StringWriter();
context.createMarshaller().marshal(unit, writer);
System.out.println(">>> XML is: " + writer.toString());
//assertTrue(unit instanceof Unit);
}
测试xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<unit xmlns="http://www.foo.org/bar">
<text>Name</text>
</unit>
输出
>>> Class is: org.foo.bar.Unit
>>> XML is: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><unit xmlns="http://www.foo.org/bar"><text>Name</text></unit>
答案 1 :(得分:3)
从我看来,XSD类型单元无法使标签名称与单元不同,那么为什么JAXB需要这种工厂方法呢?
相反 - 当您的架构通过引用使用命名的复杂类型时,您将始终获得JAXBElement
。对于命名的复杂类型,类型可能总是用于不同的元素(可能在另一个导入模式中),或者元素可能使用命名类型的子类型而不是顶层类型本身。 / p>
当全局xsd:element
声明具有嵌套的匿名complexType
时,将使用展开的根元素,因为在那种情况下,unmarshaller知道这些替换不会发生。
答案 2 :(得分:1)
如果您正在做这样的事情:
JAXBContext jaxbContext = JAXBContext.newInstance(Unit.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
JAXBElement<Unit> root = jaxbUnmarshaller.unmarshal(new StreamSource(
file), Unit.class);
Unit unit = root.getValue();
尝试一下:
Unit unit = JAXBIntrospector.getValue(jaxbUnmarshaller.unmarshal(new StreamSource(
file), Unit.class);
答案 3 :(得分:0)
使用此方法可以使用javax.xml.bind.JAXBElement实例或@XmlRootElement注释Java类的实例:
public <T> T unmarshal(Source queryResults, String modelPackages) {
T resultObject = null;
try {
JAXBContext jc = JAXBContext.newInstance(modelPackages);
Unmarshaller u = jc.createUnmarshaller();
resultObject = (T) JAXBIntrospector.getValue(u.unmarshal(queryResults));
} catch (JAXBException e) {
LOG.error(e.getMessage(), e);
} catch (ClassCastException e) {
LOG.error(e.getMessage(), e);
}
return resultObject;
}
编辑:
你有理由,我推出了这个代码,因为这是我为一个项目所做的代码,而且我觉得它更可重复使用。
所以对你提问:
为什么JAXB将org.foo.bar.Unit包装到JAXBElement中以进行上述组合?
因为您告诉它使用<xsd:any processContents="skip" />
执行此操作:)
好吧,当你把它放在你的XSD中时,就像你使用注释一样:
@XmlAnyElement(lax=false)
使用那个标签/注释你要对JAXB说:&#39;这里有一些东西,一堆节点,(@ XMLAnyElement)你不应该解析;让它作为DOM对象,请(lax = false)&#39;试试:
processContents=lax
这样它应该尝试将你的xml解析为域对象,如果它能找到它或者JAXBElement,它将返回Unit或者:
processContents=strict
将尝试将其解析为您的域对象
答案 4 :(得分:0)
经过一些谷歌搜索后,我找到了答案,这基本上由Kohsuke Kawaguchi在Why does JAXB put @XmlRootElement sometimes but not always中给出。事实证明,添加@XmlRootElement
注释的决定是生成辅助工厂方法的对手。一个应该启用<xjc:simple />
优化,JAXB将假设所有元素都是根元素:
<xsd:schema xmlns:bar="http://www.foo.org/bar"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
targetNamespace="http://www.foo.org/bar"
jaxb:extensionBindingPrefixes="xjc" jaxb:version="2.1" elementFormDefault="qualified">
<xsd:annotation>
<xsd:appinfo>
<jaxb:globalBindings>
<xjc:simple />
</jaxb:globalBindings>
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="unit" type="bar:unit" />
<xsd:complexType name="unit">
<xsd:sequence>
<xsd:any processContents="skip" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>