java Jaxb 在解组时忽略命名空间

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29622877/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 15:36:59  来源:igfitidea点击:

Jaxb ignore the namespace on unmarshalling

javaspringjaxb

提问by Arnaud Denoyelle

I use Jaxb2 and Spring. I am trying to unmarshal some XML that are sent by 2 of my customers.

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

Up to now, I only had to handle one customer which sent some xml like this :

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

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

that is bound to a POJO like this :

绑定到这样的 POJO:

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

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

  [...]
}

I discovered that the previous developer hardcoded the namespace in the unmarshaller in order to make it work.

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

Now, the second customer sends the same XML but changes the namespace!

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

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

Obviously, the unmarshaller fails to unmarshall because it expects some {com.acme}fooinstead of {com.xyz}foo. Unforunately, asking the customer to change the XML is not an option.

显然,解组器无法解组,因为它期望 some{com.acme}foo而不是{com.xyz}foo。不幸的是,要求客户更改 XML 不是一种选择。

What I tried :

我试过的:

1)In application-context.xml, I searched for a configuration which would allow me to ignore the namespace but could not find one :

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>

it seems that the only available options are the ones listed in the Jaxb2Marshaller's Javadoc :

似乎唯一可用的选项是 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)I also tried to configure the unmarshaller in the code :

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)Different form with a SAXParser :

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

This one works!But still, I would prefer to be able to autowire the Unmarshaller without needing this ugly conf everytime.

这个有效!但是,我仍然希望能够自动装配 Unmarshaller,而无需每次都使用这个丑陋的 conf。

采纳答案by Zielu

Namesapce awareness is feature of the document reader/builder/parser not marshallers. XML elements from different namespaces represents different entities == objects, so marshallers cannot ignore them.

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

You correctly switched off the namespaces in your SAX reader and as you said it worked. I don't understand your problem with it, your marshaller still can be injected, the difference is in obtaining the input data.

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

The same trick with document builder should also work (I will test it later on), I suspect that you were still using the marshaller with "hardcoded" namespace but your document was namespace free.

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

In my project I use XSLT to solve similar issue. Setting namespace awarness is definitely easier solution. But, with XSLT I could selectviely remove only some namespaces and also my my input xml are not always identical (ignoring namespaces) and sometimes I have to rename few elements so XSLT gives me this extra flexibility.

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

To remove namespaces you can use such xslt template:

要删除命名空间,您可以使用这样的 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>

Then in Java before unmarshalling I transform the input data:

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

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

回答by Binal Parekh

Thank you all, here shared my solution which works for my code , i try to make it generic every namespace contain ": " i write code if any tag have ":" it will remove from xml , This is used to skip namespace during unmarshalling using jaxb.

谢谢大家,这里分享了适用于我的代码的解决方案,我尝试使其通用每个命名空间包含“:”我编写代码如果任何标签有“:”它将从 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;
}

}

}

for unmarshalling

用于解组

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

`

`