Java JAXB 解组忽略命名空间将元素属性变为空
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1871060/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JAXB unmarshalling ignoring namespace turns element attributes into null
提问by user227614
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:
我正在尝试使用 JAXB 将 xml 文件解组为对象,但遇到了一些困难。实际项目的 xml 文件中有几千行,所以我以较小的规模重现了错误,如下所示:
The XML file:
XML文件:
<?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
用于生成 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>
Code snippet 1:
代码片段 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.
因此 XML 文档中的命名空间导致了问题,不幸的是,如果将其删除,它可以正常工作,但是由于该文件是由客户端提供的,因此我们被它困住了。我尝试了多种在 XSD 中指定它的方法,但似乎没有一种排列方式有效。
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.
由于我们无法控制的原因,我们只能使用 Java 1.5,而我们使用的是 JAXB 2.0,这很不幸,因为第二个代码块使用 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.
任何建议将不胜感激,替代方法是在解析文件之前将名称空间声明从文件中删除,这似乎不雅。
回答by Kristofer
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.
这篇文章中解释了此问题的解决方法:JAXB:如何在解组 XML 文档期间忽略命名空间?. 它解释了如何使用 SAX 过滤器从 XML 动态添加/删除 xmlns 条目。处理编组和解组。
回答by Pierre D
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.
感谢您的这篇文章和您的代码片段。这绝对让我走上了正确的道路,因为我也在努力处理一些供应商提供的 XML,而xmlns="http://vendor.com/foo"
这些XML到处都是。
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.
我的第一个解决方案(在我阅读您的帖子之前)是将 XML 放入字符串中,然后xmlString.replaceAll(" xmlns=", " ylmns=");
(恐怖,恐怖)。除了冒犯我的敏感性之外,从 InputStream 处理 XML 时也很痛苦。
My second solution, after looking at your code snippet: (I'm using Java7)
在查看您的代码片段后,我的第二个解决方案:(我使用的是 Java7)
// given an InputStream inputStream:
String packageName = docClass.getPackage().getName();
JAXBContext jc = JAXBContext.newInstance(packageName);
Unmarshaller u = jc.createUnmarshaller();
InputSource is = new InputSource(inputStream);
final SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
final XMLReader reader;
try {
reader = sax.newSAXParser().getXMLReader();
} catch (SAXException | ParserConfigurationException e) {
throw new RuntimeException(e);
}
SAXSource source = new SAXSource(reader, is);
@SuppressWarnings("unchecked")
JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal(source);
return doc.getValue();
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:
但是现在,我找到了第三个我更喜欢的解决方案,希望这对其他人有用:如何在模式中正确定义预期的命名空间:
<xsd:schema jxb:version="2.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns="http://vendor.com/foo"
targetNamespace="http://vendor.com/foo"
elementFormDefault="unqualified"
attributeFormDefault="unqualified">
With that, we can now remove the sax.setNamespaceAware(false);
line (update: actually, if we keep the unmarshal(SAXSource)
call, then we need to sax.setNamespaceAware(true)
. But the simpler way is to not bother with SAXSource
and the code surrounding its creation and instead unmarshal(InputStream)
which by default is namespace-aware. And the ouput of a marshal() also has the proper namespace too.
有了这个,我们现在可以删除该sax.setNamespaceAware(false);
行(更新:实际上,如果我们保留unmarshal(SAXSource)
调用,那么我们需要sax.setNamespaceAware(true)
。但更简单的方法是不打扰SAXSource
和围绕其创建的代码,而是unmarshal(InputStream)
默认情况下是命名空间感知的。 marshal() 的输出也有适当的命名空间。
Yeh. Only about 4 hours down the drain.
嗯。下水道只用了大约 4 个小时。
回答by Y.Y
Here is my solution for this Namespace related issue. We can trick JAXB by implementing our own XMLFilter and Attribute.
这是我对此命名空间相关问题的解决方案。我们可以通过实现我们自己的 XMLFilter 和 Attribute 来欺骗 JAXB。
class MyAttr extends AttributesImpl {
MyAttr(Attributes atts) {
super(atts);
}
@Override
public String getLocalName(int index) {
return super.getQName(index);
}
}
class MyFilter extends XMLFilterImpl {
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
super.startElement(uri, localName, qName, new VersAttr(atts));
}
}
public SomeObject testFromXML(InputStream input) {
try {
// Create the JAXBContext
JAXBContext jc = JAXBContext.newInstance(SomeObject.class);
// Create the XMLFilter
XMLFilter filter = new VersFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
//spf.setNamespaceAware(false);
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource is = new InputSource(input);
filter.parse(is);
return (SomeObject) unmarshallerHandler.getResult();
}catch (Exception e) {
logger.debug(ExceptionUtils.getFullStackTrace(e));
}
return null;
}
回答by Dmitry Avtonomov
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:
您可以使用XMLStreamReader
非命名空间感知的,它基本上会从您正在解析的 xml 文件中删除所有命名空间:
JAXBContext jc = JAXBContext.newInstance(your.ObjectFactory.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false); // this is the magic line
StreamSource source = new StreamSource(f);
XMLStreamReader xsr = xif.createXMLStreamReader(source);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Object unmarshal = unmarshaller.unmarshal(xsr);
Now the actual xml that gets fed into JAXB doesn't have any namespace info.
现在,输入 JAXB 的实际 xml 没有任何名称空间信息。
Important note (xjc)
重要说明 (xjc)
If you generated java classes from an xsd
schema using xjc
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.
如果您xsd
使用xjc
模式从架构生成 java 类,并且架构定义了命名空间,那么生成的注释将具有该命名空间,因此请手动删除它!否则 JAXB 将无法识别此类数据。
Places where the annotations should be changed:
需要修改注解的地方:
ObjectFactory.java
// change this line private final static QName _SomeType_QNAME = new QName("some-weird-namespace", "SomeType"); // to something like private final static QName _SomeType_QNAME = new QName("", "SomeType", ""); // and this annotation @XmlElementDecl(namespace = "some-weird-namespace", name = "SomeType") // to this @XmlElementDecl(namespace = "", name = "SomeType")
package-info.java
// change this annotation @javax.xml.bind.annotation.XmlSchema(namespace = "some-weird-namespace", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) // to something like this @javax.xml.bind.annotation.XmlSchema(namespace = "", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
对象工厂.java
// change this line private final static QName _SomeType_QNAME = new QName("some-weird-namespace", "SomeType"); // to something like private final static QName _SomeType_QNAME = new QName("", "SomeType", ""); // and this annotation @XmlElementDecl(namespace = "some-weird-namespace", name = "SomeType") // to this @XmlElementDecl(namespace = "", name = "SomeType")
包信息.java
// change this annotation @javax.xml.bind.annotation.XmlSchema(namespace = "some-weird-namespace", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) // to something like this @javax.xml.bind.annotation.XmlSchema(namespace = "", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
Now your JAXB code will expect to see everything without any namespaces and the XMLStreamReader
that we created supplies just that.
现在,您的 JAXB 代码将希望看到没有任何名称空间的所有内容,而XMLStreamReader
我们创建的内容正是如此。