忽略名称空间的JAXB解组将元素属性转换为null

我正在尝试使用JAXB将xml文件解组为对象,但遇到了一些困难。 实际项目在xml文件中有几千行,所以我在较小的范围内重现了错误,如下所示:

XML文件:

  

用于生成JAXB类的XSD文件

           

代码段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  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按需工作。

任何建议将不胜感激,另一种方法是在解析它之前将名称空间声明从文件中删除,这似乎是不优雅的。

关于JAXB的事情是,它实际上正确地实现了XML和XML模式。 这听起来是件好事,但正如你所发现的那样,JAXB往往有点……太字面了。

所以,在我看来你有一个XSD上写着“期待这里有一个目录”,然后你得到了一个XML,上面写着“这里是一个{x-schema:TamsDataSchema.xml}目录”,并且不出所料JAXB得到过分肛门并说“那不酷”。 我无法解决这个问题; 要么必须预先解析XML以删除命名空间,要么需要调整架构以允许它。

正如你所说的那样,任何一种解决方案都不优雅,但是当你试图将一个方形钉子放入一个圆孔时有时你需要有点不优雅(你基本上说“适合这个正方形/命名空间的钉子”圆形/非命名空洞“,所以……)

感谢您收到此post和您的代码段。 它肯定让我走上了正确的道路,因为我也在努力处理一些供应商提供的XML,这些XML在整个地方都有xmlns="http://vendor.com/foo"

我的第一个解决方案(在我阅读你的post之前)是在字符串中获取XML,然后是xmlString.replaceAll(" xmlns=", " ylmns="); (恐怖,恐怖)。 除了冒犯我的敏感性之外,从InputStream处理XML时也很痛苦。

在看了你的代码片段后我的第二个解决方案:(我正在使用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 doc = (JAXBElement)u.unmarshal(source); return doc.getValue(); 

但是现在,我找到了第三个解决方案,我更喜欢这个解决方案,并希望这对其他人有用:如何正确定义架构中预期的命名空间:

  

有了它,我们现在可以删除sax.setNamespaceAware(false); line(更新:实际上,如果我们保持unmarshal(SAXSource)调用,那么我们需要sax.setNamespaceAware(true) 。但更简单的方法是不要打扰SAXSource和围绕它创建的代码而是unmarshal(InputStream)默认情况下是名称空间感知。而marshal()的输出也有正确的名称空间。

叶。 只有大约4个小时的排水管。

如何忽略命名空间

您可以使用非名称空间感知的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); 

现在,输入JAXB的实际xml没有任何命名空间信息。


重要提示(xjc)

如果您使用xjcxsd模式生成java类,并且模式已定义了命名空间,则生成的注释将具有该命名空间,因此请手动删除它! 否则JAXB将无法识别此类数据。

应该更改注释的位置:

  • 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) 

现在,您的JAXB代码将期望看到没有任何名称空间的所有内容以及我们创建的XMLStreamReader

以下是此命名空间相关问题的解决方案。 我们可以通过实现自己的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; } 

这篇文章中解释了这个问题的解决方法: JAXB:如何在解组XML文档时忽略命名空间? 。 它解释了如何使用SAXfilter从XML动态添加/删除xmlns条目。 处理编组和解组。