如何使用Java DOM从XML中删除命名空间?

14

我有如下代码

DocumentBuilderFactory dbFactory_ = DocumentBuilderFactory.newInstance();
Document doc_;
DocumentBuilder dBuilder = dbFactory_.newDocumentBuilder();
StringReader reader = new StringReader(s);
InputSource inputSource = new InputSource(reader);
doc_ = dBuilder.parse(inputSource);
doc_.getDocumentElement().normalize();

然后我可以这样做:

doc_.getDocumentElement();

我想获取我的第一个元素,但问题是它不是job,而是tns:job

我知道并尝试过使用:

dbFactory_.setNamespaceAware(true);

但那不是我正在寻找的,我需要完全摆脱命名空间的东西。

非常感谢您的帮助, 谢谢,

Josh


为什么你想要摆脱命名空间,而不是应对它们呢? - Tomalak
我有一些不支持它们的遗留代码。 - Grammin
1
如果是传统的POS系统,也许可以使用暴力剥离命名空间前缀的方法;即使是像正则表达式这样简单的东西也可以工作。一般来说这不是正确的做法,但有时候只能用垃圾对抗垃圾。 :) - StaxMan
9个回答

15

使用正则表达式函数。这将解决这个问题:

public static String removeXmlStringNamespaceAndPreamble(String xmlString) {
  return xmlString.replaceAll("(<\\?[^<]*\\?>)?", ""). /* remove preamble */
  replaceAll("xmlns.*?(\"|\').*?(\"|\')", "") /* remove xmlns declaration */
  .replaceAll("(<)(\\w+:)(.*?>)", "$1$3") /* remove opening tag prefix */
  .replaceAll("(</)(\\w+:)(.*?>)", "$1$3"); /* remove closing tags prefix */
}

25
仅仅使用正则表达式来移除所有命名空间并不是一件好事,即使这段代码可以工作。 - james.garriss
@james.garriss 我同意你的观点,但我还没有找到更好的解决方案... - Réda Housni Alaoui
@Tomalak的XSLT是更好的解决方案。它使用XML来处理XML。 - james.garriss

8

如果你非常必须这样做,你可以预处理XML以删除所有命名空间。但我建议不要这样做,因为从XML文档中删除命名空间本质上与从编程框架或库中删除命名空间相似 - 你可能会遇到名称冲突并且失去区分原本不同元素的能力。不过,这是你自己的选择。

这个XSLT转换可以从任何XML文档中删除所有命名空间。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node()">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </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:apply-templates select="node()|@*" />
    </xsl:attribute>
  </xsl:template>
</xsl:stylesheet>

将它应用于您的XML文档。在此网站上,有很多Java示例可以做到这一点。生成的文档将完全具有相同的结构和布局,只是没有命名空间。


7
对于元素和属性节点:
Node node = ...;
String name = node.getLocalName();

将会给你节点名称的本地部分。

参见Node.getLocalName()


有没有办法从 XML 中完全删除它们?还是它们会一直存在? - Grammin
2
正如Anon和Tomalak所提到的,您真的不希望从XML中剥离命名空间信息。这是对于您特殊情况的一个很好的解决办法,但我建议保留命名空间信息。 - robert_x44

3
不是简单地
dbFactory_.setNamespaceAware(true);

使用

dbFactory_.setNamespaceAware(false);

虽然我同意Tomalak的观点:总体而言,命名空间比有害更有帮助。为什么你不想使用它们呢?


编辑:这个答案并没有回答OP的问题,他的问题是如何去掉命名空间的前缀。RD01提供了正确的答案。


@Grammin - 那么问题是当您使用不支持命名空间的解析器时,仍然看到前缀吗?如果是的话,请查看RD01的答案。 - Anon

2

Tomalak,在你的XSLT中有一个修复(在第三个模板中):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node()">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

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

  <xsl:template match="@*">
    <!-- Here! -->
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>

  </xsl:template>
</xsl:stylesheet>

2
选择解决方案时需要考虑输入xml的大小。对于大型xml(约100k),如果输入来自Web服务,操作大字符串时还需要考虑垃圾回收的影响。我们以前使用String.replaceAll,在生产环境中由于replaceAll的实现方式而导致频繁的OOM,堆大小为1.5G。
您可以参考http://app-inf.blogspot.com/2013/04/pitfalls-of-handling-large-string.html了解我们的发现。
我不确定XSLT如何处理大型字符串对象,但我们最终手动解析字符串以一次解析中删除前缀,以避免创建额外的大型Java对象。
public static String removePrefixes(String input1) {
    String ret = null;
    int strStart = 0;
    boolean finished = false;
    if (input1 != null) {
        //BE CAREFUL : allocate enough size for StringBuffer to avoid expansion
        StringBuffer sb = new StringBuffer(input1.length()); 
        while (!finished) {

            int start = input1.indexOf('<', strStart);
            int end = input1.indexOf('>', strStart);
            if (start != -1 && end != -1) {
                // Appending anything before '<', including '<'
                sb.append(input1, strStart, start + 1);

                String tag = input1.substring(start + 1, end);
                if (tag.charAt(0) == '/') {
                    // Appending '/' if it is "</"
                    sb.append('/');
                    tag = tag.substring(1);
                }

                int colon = tag.indexOf(':');
                int space = tag.indexOf(' ');
                if (colon != -1 && (space == -1 || colon < space)) {
                    tag = tag.substring(colon + 1);
                }
                // Appending tag with prefix removed, and ">"
                sb.append(tag).append('>');
                strStart = end + 1;
            } else {
                finished = true;
            }
        }
        //BE CAREFUL : use new String(sb) instead of sb.toString for large Strings
        ret = new String(sb);
    }
    return ret;
}

2
public static void wipeRootNamespaces(Document xml) {       
    Node root = xml.getDocumentElement();
    NodeList rootchildren = root.getChildNodes();
    Element newroot = xml.createElement(root.getNodeName());

    for (int i=0;i<rootchildren.getLength();i++) {
        newroot.appendChild(rootchildren.item(i).cloneNode(true));
    }

    xml.replaceChild(newroot, root);
}

root.getLocalName(); (?) --> root.getLocalName();(?) - A. L.

1

不再使用TransformerFactory并在其上调用transform(这会注入空命名空间),而是按以下方式转换:

    OutputStream outputStream = new FileOutputStream(new File(xMLFilePath));
    OutputFormat outputFormat = new OutputFormat(doc, "UTF-8", true);
    outputFormat.setOmitComments(true);
    outputFormat.setLineWidth(0);

    XMLSerializer serializer = new XMLSerializer(outputStream, outputFormat);
    serializer.serialize(doc);
    outputStream.close();

0

我也遇到了命名空间问题,无法在Java中读取XML文件。以下是解决方案:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);// this is imp code that will deactivate namespace in xml
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("XML/"+ fileName);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接