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

use*_*614 17 java attributes namespaces jaxb jdk1.5

我正在尝试使用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"/>
Run Code Online (Sandbox Code Playgroud)

用于生成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>
Run Code Online (Sandbox Code Playgroud)

代码段1:

final JAXBContext context = JAXBContext.newInstance(CatalogueType.class);
um = context.createUnmarshaller();
CatalogueType ct = (CatalogueType)um.unmarshal(new File("file output address"));
Run Code Online (Sandbox Code Playgroud)

哪个引发错误:

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
Run Code Online (Sandbox Code Playgroud)

因此,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());
Run Code Online (Sandbox Code Playgroud)

哪个工作正常,但无法解组元素属性和打印

null
null
Run Code Online (Sandbox Code Playgroud)

由于我们无法控制的原因,我们仅限于使用Java 1.5,我们正在使用JAXB 2.0,这是不幸的,因为第二个代码块使用Java 1.6按需工作.

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

Pie*_*e D 15

感谢您发布此帖子和您的代码段.它肯定让我走上了正确的道路,因为我也在努力处理一些供应商提供的XML,xmlns="http://vendor.com/foo"这些XML已经到处都是.

我的第一个解决方案(在我阅读你的帖子之前)是将字符串中的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<T> doc = (JAXBElement<T>)u.unmarshal(source);
return doc.getValue();
Run Code Online (Sandbox Code Playgroud)

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

<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">
Run Code Online (Sandbox Code Playgroud)

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

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


Dmi*_*mov 8

如何忽略名称空间

您可以使用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);
Run Code Online (Sandbox Code Playgroud)

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


重要说明(xjc)

如果您xsd使用xjc模式从架构生成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")
    
    Run Code Online (Sandbox Code Playgroud)
  • 包信息.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)
    
    Run Code Online (Sandbox Code Playgroud)

现在,您的JAXB代码将期望看到没有任何名称空间的所有内容,而XMLStreamReader我们创建的名称空间就提供了这一点。


Kri*_*fer 1

这篇文章中解释了此问题的解决方法:JAXB: How toignoring namespace while unmarshalling XML document? 。它解释了如何使用 SAX 过滤器从 XML 动态添加/删除 xmlns 条目。类似地处理编组和解编组。