JAXB 解组忽略命名空间将元素属性变成 null
我正在尝试使用 JAXB 将 xml 文件解组为对象,但遇到了一些困难。实际项目的 xml 文件中有几千行,因此我以较小的规模重现了该错误,如下所示:
XML 文件:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<catalogue title="some catalogue title"
publisher="some publishing house"
xmlns="x-schema:TamsDataSchema.xml"/>
用于生成 JAXB 类的 XSD 文件
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="catalogue" type="catalogueType"/>
<xsd:complexType name="catalogueType">
<xsd:sequence>
<xsd:element ref="journal" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="title" type="xsd:string"/>
<xsd:attribute name="publisher" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>
代码片段 1:
final JAXBContext context = JAXBContext.newInstance(CatalogueType.class);
um = context.createUnmarshaller();
CatalogueType ct = (CatalogueType)um.unmarshal(new File("file output address"));
抛出错误:
javax.xml.bind.UnmarshalException: unexpected element (uri:"x-schema:TamsDataSchema.xml", local:"catalogue"). Expected elements are <{}catalogue>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:642)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:247)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:242)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:116)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1049)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:478)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:459)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:148)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
...etc
所以命名空间XML 文档导致了问题,不幸的是,如果将其删除,它可以正常工作,但由于该文件是由客户端提供的,因此我们只能使用它。我尝试了多种在 XSD 中指定它的方法,但似乎没有一种排列有效。
我还尝试使用以下代码来解组忽略命名空间:
Unmarshaller um = context.createUnmarshaller();
final SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
final XMLReader reader = sax.newSAXParser().getXMLReader();
final Source er = new SAXSource(reader, new InputSource(new FileReader("file location")));
CatalogueType ct = (CatalogueType)um.unmarshal(er);
System.out.println(ct.getPublisher());
System.out.println(ct.getTitle());
工作正常,但无法解组元素属性和打印
null
null
由于超出我们控制范围的原因,我们仅限于使用 Java 1.5 并且我们正在使用 JAXB 2.0,这是不幸的,因为第二个代码块使用 Java 1.6 按需要工作。
任何建议将不胜感激,替代方案是在解析文件之前将名称空间声明从文件中删除,这似乎不优雅。
I'm trying to use JAXB to unmarshal an xml file into objects but have come across a few difficulties. The actual project has a few thousand lines in the xml file so i've reproduced the error on a smaller scale as follows:
The XML file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<catalogue title="some catalogue title"
publisher="some publishing house"
xmlns="x-schema:TamsDataSchema.xml"/>
The XSD file for producing JAXB classes
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="catalogue" type="catalogueType"/>
<xsd:complexType name="catalogueType">
<xsd:sequence>
<xsd:element ref="journal" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="title" type="xsd:string"/>
<xsd:attribute name="publisher" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>
Code snippet 1:
final JAXBContext context = JAXBContext.newInstance(CatalogueType.class);
um = context.createUnmarshaller();
CatalogueType ct = (CatalogueType)um.unmarshal(new File("file output address"));
Which throws the error:
javax.xml.bind.UnmarshalException: unexpected element (uri:"x-schema:TamsDataSchema.xml", local:"catalogue"). Expected elements are <{}catalogue>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:642)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:247)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:242)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:116)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1049)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:478)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:459)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:148)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
...etc
So the namespace in the XML document is causing issues, unfortunately if it's removed it works fine, but as the file is supplied by the client we're stuck with it. I've attempted numerous ways of specifying it in the XSD but none of the permutations seem to work.
I also attempted to unmarshal ignoring namespace using the following code:
Unmarshaller um = context.createUnmarshaller();
final SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
final XMLReader reader = sax.newSAXParser().getXMLReader();
final Source er = new SAXSource(reader, new InputSource(new FileReader("file location")));
CatalogueType ct = (CatalogueType)um.unmarshal(er);
System.out.println(ct.getPublisher());
System.out.println(ct.getTitle());
which works fine but fails to unmarshal element attributes and prints
null
null
Due to reasons beyond our control we're limited to using Java 1.5 and we're using JAXB 2.0 which is unfortunate because the second code block works as desired using Java 1.6.
any suggestions would be greatly appreciated, the alternative is cutting the namespace declaration out of the file before parsing it which seems inelegant.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
感谢您的这篇文章和您的代码片段。它确实让我走上了正确的道路,因为我也疯狂地试图处理一些供应商提供的 XML,这些 XML 到处都是
xmlns="http://vendor.com/foo"
。我的第一个解决方案(在阅读您的文章之前)是将 XML 放入字符串中,然后使用
xmlString.replaceAll(" xmlns=", " ylmns=");
(恐怖,恐怖)。除了冒犯我的感受之外,处理来自 InputStream 的 XML 时也很痛苦。我的第二个解决方案,在查看您的代码片段之后:(我正在使用 Java7)
但是现在,我找到了我更喜欢的第三个解决方案,希望这对其他人可能有用:如何在schema:
这样,我们现在可以删除
sax.setNamespaceAware(false);
行(更新:实际上,如果我们保留unmarshal(SAXSource)
调用,那么我们需要但更简单的方法是不关心SAXSource
及其创建周围的代码,而是使用unmarshal(InputStream)
。 > 默认情况下,marshal() 的输出也具有正确的命名空间 是的。
Thank you for this post and your code snippet. It definitely put me on the right path as I was also going nuts trying to deal with some vendor-provided XML that had
xmlns="http://vendor.com/foo"
all over the place.My first solution (before I read your post) was to take the XML in a String, then
xmlString.replaceAll(" xmlns=", " ylmns=");
(the horror, the horror). Besides offending my sensibility, in was a pain when processing XML from an InputStream.My second solution, after looking at your code snippet: (I'm using Java7)
But now, I found a third solution which I like much better, and hopefully that might be useful to others: How to define properly the expected namespace in the schema:
With that, we can now remove the
sax.setNamespaceAware(false);
line (update: actually, if we keep theunmarshal(SAXSource)
call, then we need tosax.setNamespaceAware(true)
. But the simpler way is to not bother withSAXSource
and the code surrounding its creation and insteadunmarshal(InputStream)
which by default is namespace-aware. And the ouput of a marshal() also has the proper namespace too.Yeh. Only about 4 hours down the drain.
如何忽略命名空间
您可以使用非命名空间感知的
XMLStreamReader
,它基本上会从您正在解析的 xml 文件中删除所有命名空间:现在,实际的 xml 被馈送到 JAXB 中没有任何命名空间信息。
重要说明 (xjc)
如果您使用
xjc
从xsd
架构生成 java 类,并且该架构定义了命名空间,则生成的注释将具有该命名空间,因此请将其删除手动!否则 JAXB 将无法识别此类数据。需要修改注解的地方:
ObjectFactory.java
package-info.java
现在,您的 JAXB 代码将期望看到没有任何名称空间的所有内容,而我们创建的
XMLStreamReader
正好提供了这一点。How to ignore the namespaces
You can use an
XMLStreamReader
that is non-namespace aware, it will basically trim out all namespaces from the xml file that you're parsing:Now the actual xml that gets fed into JAXB doesn't have any namespace info.
Important note (xjc)
If you generated java classes from an
xsd
schema usingxjc
and the schema had a namespace defined, then the generated annotations will have that namespace, so delete it manually! Otherwise JAXB won't recognize such data.Places where the annotations should be changed:
ObjectFactory.java
package-info.java
Now your JAXB code will expect to see everything without any namespaces and the
XMLStreamReader
that we created supplies just that.这是我针对此命名空间相关问题的解决方案。我们可以通过实现我们自己的 XMLFilter 和 Attribute 来欺骗 JAXB。
Here is my solution for this Namespace related issue. We can trick JAXB by implementing our own XMLFilter and Attribute.
本文解释了此问题的解决方法:JAXB:如何在解组 XML 文档期间忽略名称空间?。它解释了如何使用 SAX 过滤器从 XML 动态添加/删除 xmlns 条目。类似地处理编组和解编组。
There is a workaround for this issue explained in this post: JAXB: How to ignore namespace during unmarshalling XML document?. It explains how to dynamically add/remove xmlns entries from XML using a SAX Filter. Handles marshalling and unmarshalling alike.