Jaxb忽略了解组时的命名空间

Arn*_*lle 9 java spring jaxb

我使用Jaxb2和Spring.我正在尝试解组由我的两个客户发送的一些XML.

到目前为止,我只需要处理一个发送了一些像这样的xml的客户:

<foo xmlns="com.acme">
  <bar>[...]</bar>
<foo>
Run Code Online (Sandbox Code Playgroud)

绑定到这样的POJO:

@XmlType(name = "", propOrder = {"bar"})
@XmlRootElement(name = "Foo")
public class Foo {

  @XmlElement(name = "Bar")
  private String bar;

  [...]
}
Run Code Online (Sandbox Code Playgroud)

我发现之前的开发人员在unmarshaller中硬编码了命名空间以使其工作.

现在,第二个客户发送相同的XML但更改名称空间!

<foo xmlns="com.xyz">
  <bar>[...]</bar>
<foo>
Run Code Online (Sandbox Code Playgroud)

显然,unmarshaller没有解组,因为它期望一些{com.acme}foo而不是{com.xyz}foo.不幸的是,要求客户更改XML不是一种选择.

我尝试了什么:

1)application-context.xml,我搜索了一个配置,它允许我忽略命名空间但找不到:

<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
  <property name="packagesToScan">
    <list>
      <value>com.mycompany.mypkg</value>
    </list>
  </property>
  <property name="marshallerProperties">
    <map>
      <entry key="???"><value type="java.lang.Boolean">false</value></entry>
    </map>
  </property>
</bean>
Run Code Online (Sandbox Code Playgroud)

似乎唯一可用的选项是Jaxb2Marshaller的Javadoc中列出的选项:

/**
 * Set the JAXB {@code Marshaller} properties. These properties will be set on the
 * underlying JAXB {@code Marshaller}, and allow for features such as indentation.
 * @param properties the properties
 * @see javax.xml.bind.Marshaller#setProperty(String, Object)
 * @see javax.xml.bind.Marshaller#JAXB_ENCODING
 * @see javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUT
 * @see javax.xml.bind.Marshaller#JAXB_NO_NAMESPACE_SCHEMA_LOCATION
 * @see javax.xml.bind.Marshaller#JAXB_SCHEMA_LOCATION
 */
public void setMarshallerProperties(Map<String, ?> properties) {
    this.marshallerProperties = properties;
}
Run Code Online (Sandbox Code Playgroud)

2)我还尝试在代码中配置unmarshaller:

try {
  jc = JAXBContext.newInstance("com.mycompany.mypkg");

  Unmarshaller u = jc.createUnmarshaller();
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  dbf.setNamespaceAware(false);//Tried this option.

  DocumentBuilder db = dbf.newDocumentBuilder();
  Document doc = db.parse(xmlFile.toFile());
  u.unmarshal(new DOMSource(doc));
  return (Foo)u.unmarshal(new StreamSource(xmlFile.toFile()));
} catch (ParserConfigurationException | SAXException | IOException | JAXBException e) {
  LOGGER.error("Erreur Unmarshalling CPL");
}
Run Code Online (Sandbox Code Playgroud)

3)与SAXParser不同的形式:

try {
  jc = JAXBContext.newInstance("com.mycompany.mypkg");
  Unmarshaller um = jc.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(xmlFile.toFile())));
  return (Foo)um.unmarshal(er);
}catch(...) {[...]}
Run Code Online (Sandbox Code Playgroud)

这一个有效!但是,我仍然希望能够自动装配Unmarshaller,而不必每次都需要这种丑陋的conf.

Zie*_*elu 2

命名空间意识是文档读取器/构建器/解析器的功能,而不是编组器的功能。来自不同命名空间的 XML 元素代表不同的实体 == 对象,因此编组器不能忽略它们。

您正确地关闭了 SAX 阅读器中的名称空间,正如您所说,它有效。我不明白你的问题,你的编组器仍然可以被注入,不同之处在于获取输入数据。

文档生成器的相同技巧也应该有效(我稍后将对其进行测试),我怀疑您仍在使用具有“硬编码”名称空间的编组器,但您的文档是免费的名称空间。

在我的项目中我使用 XSLT 来解决类似的问题。设置命名空间感知绝对是更简单的解决方案。但是,使用 XSLT,我可以有选择地仅删除一些名称空间,而且我的输入 xml 并不总是相同(忽略名称空间),有时我必须重命名一些元素,因此 XSLT 为我提供了额外的灵活性。

要删除命名空间,您可以使用这样的 xslt 模板:

<xsl:stylesheet version="1.0" xmlns:e="http://timet.dom.robust.ed" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:template match="/"> 
    <xsl:copy>
        <xsl:apply-templates />
    </xsl:copy>
</xsl:template>  

<xsl:template match="*">
    <xsl:element name="{local-name()}">
        <xsl:apply-templates select="@* | node()" />
    </xsl:element>
</xsl:template>

<xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
        <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>

<xsl:template match="text() | processing-instruction() | comment()">
    <xsl:copy />
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

然后在 Java 中,在解组之前,我转换输入数据:

Transformer transformer = TransformerFactory.newInstance().newTransformer(stylesource);
Source source = new DOMSource(xml);
DOMResult result = new DOMResult();
transformer.transform(source, result);
Run Code Online (Sandbox Code Playgroud)