【问题标题】:Jaxb ignore the namespace on unmarshallingJaxb 在解组时忽略命名空间
【发布时间】:2015-06-19 19:11:21
【问题描述】:

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

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

<foo xmlns="com.acme">
  <bar>[...]</bar>
<foo>

像这样绑定到 POJO:

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

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

  [...]
}

我发现之前的开发人员在解组器中对命名空间进行了硬编码,以使其正常工作。

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

<foo xmlns="com.xyz">
  <bar>[...]</bar>
<foo>

很明显,解组器无法解组,因为它需要一些{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>

似乎唯一可用的选项是 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;
}

2) 我也尝试在代码中配置解组器:

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");
}

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(...) {[...]}

这个可行!但是,我仍然希望能够自动装配 Unmarshaller,而不需要每次都需要这个丑陋的配置。

【问题讨论】:

    标签: java spring jaxb


    【解决方案1】:

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

    您正确地关闭了 SAX 阅读器中的命名空间,并且正如您所说的那样它有效。不明白你的问题,你的marshaller还是可以注入的,区别在于获取输入数据。

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

    在我的项目中,我使用 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>
    

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

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

    【讨论】:

      【解决方案2】:

      谢谢大家,这里分享了适用于我的代码的解决方案,我尝试使其通用每个命名空间包含“:”如果任何标签有“:”我编写代码它将从 xml 中删除,这用于跳过使用 jaxb 解组期间的命名空间。

      public class NamespaceFilter {
      
      private NamespaceFilter() {
      
      }
      
      private static final String COLON = ":";
      
      public static XMLReader nameSpaceFilter() throws SAXException {
          XMLReader xr = new XMLFilterImpl(XMLReaderFactory.createXMLReader()) {
              private boolean skipNamespace;
      
              @Override
              public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
                  if (qName.indexOf(COLON) > -1) {
                      skipNamespace = true;
                  } else {
                      skipNamespace = false;
                      super.startElement("", localName, qName, atts);
                  }
              }
      
              @Override
              public void endElement(String uri, String localName, String qName) throws SAXException {
                  if (qName.indexOf(COLON) > -1) {
                      skipNamespace = true;
                  } else {
                      skipNamespace = false;
                      super.endElement("", localName, qName);
                  }
              }
      
              @Override
              public void characters(char[] ch, int start, int length) throws SAXException {
                  if (!skipNamespace) {
                      super.characters(ch, start, length);
                  }
              }
          };
          return xr;
      }
      

      }

      用于解组

      XMLReader xr = NamespaceFilter.nameSpaceFilter();
      Source src = new SAXSource(xr, new InputSource("C:\\Users\\binal\\Desktop\\response.xml"));
      StringWriter sw = new StringWriter();
      Result res = new StreamResult(sw);
      TransformerFactory.newInstance().newTransformer().transform(src, res);
      JAXBContext jc = JAXBContext.newInstance(Tab.class);
      Unmarshaller u = jc.createUnmarshaller();
      String done = sw.getBuffer().toString();
      StringReader reader = new StringReader(done);
      Tab tab = (Tab) u.unmarshal(reader);
      
      System.out.println(tab);
      

      `

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-21
        • 2010-12-24
        • 1970-01-01
        • 2016-06-24
        • 2023-03-11
        • 2011-12-29
        • 2014-11-07
        相关资源
        最近更新 更多