如何使用 java dom 从 xml 中删除命名空间?
Posted
技术标签:
【中文标题】如何使用 java dom 从 xml 中删除命名空间?【英文标题】:How do I remove namespaces from xml, using java dom? 【发布时间】:2011-06-07 08:44:53 【问题描述】:我有以下代码
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);
但这不是我想要的,我需要一些东西来完全摆脱命名空间。
任何帮助将不胜感激, 谢谢,
乔什
【问题讨论】:
为什么要摆脱命名空间,而不是处理它们? 我有一些旧代码不支持它们。 如果是传统的 POS,也许只是使用暴力剥离命名空间前缀;甚至像正则表达式这样简单的东西也可以工作。一般来说,这不是正确的方法,但有时废话就是用废话来对抗。 :) 【参考方案1】:使用正则表达式函数。这将解决这个问题:
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 */
【讨论】:
使用正则表达式删除所有命名空间并不是一件好事,即使这段代码有效。 @james.garriss 我同意你的观点,但我还没有找到更好的解决方案...... @Tomalak 的 XSLT 是一个更好的解决方案。它使用 XML 来处理 XML。【参考方案2】:对于元素和属性节点:
Node node = ...;
String name = node.getLocalName();
将为您提供节点名称的本地部分。
见Node.getLocalName()
【讨论】:
有没有办法将它们从 xml 中完全删除?还是他们会留下来? 正如 Anon 和 Tomalak 所提到的,您真的不想从 XML 中剥离命名空间信息。对于您的特定情况,这是一个很好的解决方法,但我会保持命名空间信息不变。【参考方案3】:如果您绝对必须这样做,您可以预处理 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 示例应该很多,即使在这个站点上也是如此。生成的文档将具有完全相同的结构和布局,只是没有命名空间。
【讨论】:
【参考方案4】:而不是
dbFactory_.setNamespaceAware(true);
使用
dbFactory_.setNamespaceAware(false);
虽然我同意 Tomalak 的观点:一般来说,命名空间是有益的而不是有害的。为什么不想使用它们?
编辑:这个答案没有回答 OP 的问题,即如何摆脱命名空间前缀。 RD01 给出了正确答案。
【讨论】:
@Grammin - 当您使用不支持命名空间的解析器时,您仍然看到 prefix 的问题是什么?如果是,那么请看 RD01 的回答。【参考方案5】: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>
【讨论】:
【参考方案6】: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);
【讨论】:
第 4 行 ... root.getLocalName(); (?)【参考方案7】:在选择方案时也需要考虑输入xml的大小。对于大小约为 100k 的大型 xml,如果您的输入来自 Web 服务,则在操作大字符串时还需要考虑垃圾收集的影响。我们之前使用过String.replaceAll,由于replaceAll的实现方式,在1.5G堆大小的生产环境中导致频繁OOM。
您可以参考http://app-inf.blogspot.com/2013/04/pitfalls-of-handling-large-string.html 了解我们的发现。
我不确定 XSLT 如何处理大型 String 对象,但我们最终手动解析字符串以在一次解析中删除前缀以避免创建额外的大型 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;
【讨论】:
【参考方案8】:我没有使用 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();
【讨论】:
【参考方案9】:我也遇到了命名空间问题,无法在 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);
【讨论】:
以上是关于如何使用 java dom 从 xml 中删除命名空间?的主要内容,如果未能解决你的问题,请参考以下文章
使用 DOM 解析器在 Java 中解析具有 2 个默认命名空间的 XML