如何更改DOM中每个节点上的命名空间?
给定一个w3c DOM(特别是Java的默认实现),如何更改该DOM中每个元素/属性/节点的名称空间?有效地,最好。 DOM似乎没有setNamespaceURI方法,这是不方便的。如何更改DOM中每个节点上的命名空间?
我已经尝试过XSL方法,但是他们在JAXP变换器中工作失败(尽管他们在Saxon9B中正常工作,但由于各种其他原因无法使用)。
基本上,我需要一个纯粹的核心Java解决方案,它将允许我接收一个文档并更改其命名空间。
基于我极度偏见的观点,你想要的将是一个巨大的屁股疼痛。我可以看到类型转换地狱和众多递归循环需要做到这一点已经可靠了!啊,Java的默认实现,我如何讨厌你的NPE:在内部,反向逻辑,简单操作所需的额外步骤!
所以,是的,我的建议是针对每种可能的节点类型进行类型转换的循环递归,基于我个人的经验,Java的默认实现很糟糕。
我认为这里的其他一些解决方案更优雅,但这就是我最终做的:) – 2009-08-23 17:25:19
虽然有点有趣,但这实际上不应该被接受的答案...... – 2012-07-12 13:33:22
这对于支持名称空间的DOM没有效率。您必须在每个想要更改名称空间的后代元素上使用DOM Level 3 Core方法Document.renameNode(javadoc)。 (您通常不需要更改这么多的Attr节点,因为没有前缀的Attr节点的名称空间始终为空,而不是元素的名称空间。)
如果您只想将一个名称空间替换为另一方面,使用名称空间不知道的DOM可能会更快,并且只需更改相关的xmlns属性即可。您应该能够通过将DOMConfiguration'namespaces'参数设置为false来获得不支持命名空间的DOM,但是我没有在Java中尝试过这种方法,这是一种晦涩的小东西,DOM imp会出错。
如果意图只是更改名称空间,那么只需使用一些流编辑器将NS映射更改为URL即可。
Namspace或多或少是名称空间前缀和URI之间的绑定。为了迅速改变命名空间,只需更改映射:
前: 的xmlns:myns名字= “我的名称空间URI”
后: 的xmlns:myns名字= “我的新名称空间URI”
如果意图仅仅是改变命名空间,基本上改变映射就足够了。此外,如果XML文档具有默认名称空间,那么更改默认名称空间URL值将会更改整个文档的命名空间。
之前: 的xmlns =“我的名称空间URI”
后: 的xmlns =“我的新名称空间URI”
+1纯Java实现 – 2009-08-23 13:28:51
尼斯的想法,但此时需要重新序列化为字节流并重新解析,这从性能角度来看是不可接受的。我已经有一个DOM,我必须使用它。 – 2009-08-23 17:28:05
该命名空间的每一个元素上更改没有定义命名空间前缀通过将targetnamespace属性应用于根元素。这样做还需要您使用命名空间前缀来更改每个元素。您可以手动更改此前缀或编写一些脚本逻辑,让您的DOM树只在必要时才能应用。
这里是关于targetNamespace属性和nonamespaceschema属性更多阅读:
http://www.xml.com/pub/a/2000/11/29/schemas/part1.html?page=8 http://www.computerpoweruser.com/editorial/article.asp?article=articles%2Farchive%2Fc0407%2F48c07%2F48c07.asp
我怎样才能给出一个W3C DOM(Java的默认实现,特别是)改变的每个命名空间该DOM中的元素/属性/节点?有效地,最好。
我不认为有一个有效的解决方案也是健壮的。你不能只在根元素上重命名一些东西。考虑到这些文件:
文档1
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="urn:all" xmlns:f="urn:fleet" xmlns:m="urn:mission">
<f:starfleet>
<m:bold>
<f:ship name="Enterprise" />
</m:bold>
</f:starfleet>
</root>
文档2
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="urn:all">
<starfleet xmlns="urn:fleet">
<bold xmlns="urn:mission">
<ship xmlns="urn:fleet" name="Enterprise" />
</bold>
</starfleet>
</root>
文档3
<?xml version="1.0" encoding="UTF-8"?>
<r:root xmlns:r="urn:all">
<r:starfleet xmlns:r="urn:fleet">
<r:bold xmlns:r="urn:mission">
<r:ship xmlns:r="urn:fleet" name="Enterprise" />
</r:bold>
</r:starfleet>
</r:root>
这三个文件是在一个命名空间感知的DOM等价的。您可以针对其中的任何一个运行相同的namespaced XPath queries。
由于DOM允许您精确指定节点应该如何命名空间,所以没有全面的一步调用来更改命名空间。您需要走DOM,不仅考虑前缀和URI值,而且还要考虑它们在任何给定时间的scope。
这XSLT可以与Transformer可以用来改变的命名空间urn:fleet
元素被命名空间作为urn:new
:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="urn:fleet" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="f:*">
<xsl:variable name="var.foo" select="local-name()" />
<xsl:element namespace="urn:new" name="{$var.foo}">
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
注意事项:进一步的调整将被要求处理的命名空间属性;悬挂urn:fleet
声明可以留下,这是凌乱的,但很大程度上无关紧要;可能其他的东西,我没有想到。
这就是我最初使用的,几乎一字不变,但事实证明,我们客户对XML的使用是......原始的。或者愚蠢。你选。为了基督的缘故,如果XML没有正确放置,他们的工具就会中断。 – 2009-08-23 17:26:26
您可以将您的DOM树复制到另一棵树,并在处理过程中进行一些调整。 例如,使用org.apache.xml.utils.DOMBuilder为ContentHandler的实施,可以覆盖这样的方式方法:
public void startElement(String ns, String localName, String name, Attributes atts) throws SAXException {
super.startElement("new_namespace", localName, name, atts);
}
DOMBuilder将复制留给你唯一的命名空间替换逻辑中处理所有的脏活。
如果确定与使用Xerces类,你可以创建一个的DOMParser替换属性和元素的URI与你搞掂的URI:
import org.apache.xerces.parsers.DOMParser;
public static class MyDOMParser extends DOMParser {
private Map<String, String> fixupMap = ...;
@Override
protected Attr createAttrNode(QName attrQName)
{
if (fixupMap.containsKey(attrQName.uri))
attrQName.uri = fixupMap.get(attrQName.uri);
return super.createAttrNode(attrQName);
}
@Override
protected Element createElementNode(QName qName)
{
if (fixupMap.containsKey(qName.uri))
qName.uri = fixupMap.get(qName.uri);
return super.createElementNode(qName);
}
}
的其他地方,你可以解析为一个DOM文档:
DOMParse p = new MyDOMParser(...);
p.parse(new InputSource(inputStream));
Document doc = p.getDocument();
给出DOM文档的此代码将返回一个新的DOM文档,其中已经应用了一组给定的名称空间URI转换(uriMap)。这些键必须是源文档中的URI,这些值是目标文档中的替换URI。未知名称空间URI通过不变。它知道改变的xmlns的值:*属性,但不会改变,可能碰巧有空间URI作为其值的其他属性(例如XSD targetNamespace的)
private static Node makeClone(Node kid, Node to, Map<String, String> uriMap) {
Document doc = to.getNodeType() == Node.DOCUMENT_NODE ?
(Document) to :
to.getOwnerDocument();
if (kid.getNodeType() == Node.ELEMENT_NODE) {
String newURI =
uriMap.containsKey(kid.getNamespaceURI()) ?
uriMap.get(kid.getNamespaceURI()) :
kid.getNamespaceURI();
Element clone = doc.createElementNS(newURI, kid.getNodeName());
to.appendChild(clone);
for (int i = 0; i < kid.getAttributes().getLength(); i++) {
Attr attr = (Attr) kid.getAttributes().item(i);
String newAttrURI =
uriMap.containsKey(attr.getNamespaceURI()) ?
uriMap.get(attr.getNamespaceURI()) :
attr.getNamespaceURI();
String newValue = attr.getValue();
if (attr.getNamespaceURI() != null &&
attr.getNamespaceURI().equals(
"http://www.w3.org/2000/xmlns/") &&
uriMap.containsKey(attr.getValue()))
newValue = uriMap.get(attr.getValue());
clone.setAttributeNS(newAttrURI, attr.getNodeName(), newValue);
}
return clone;
}
Node clone = kid.cloneNode(false);
doc.adoptNode(clone);
to.appendChild(clone);
return clone;
}
private static void copyKidsChangingNS(Node from, Node to,
Map<String, String> uriMap) {
NodeList kids = from.getChildNodes();
for (int i = 0; i < kids.getLength(); i++) {
Node kid = kids.item(i);
Node clone = makeClone(kid, to, uriMap);
copyKidsChangingNS(kid, clone, uriMap);
}
}
public static Document changeDocNS(Document doc, Map<String, String> uriMap)
throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document newDoc = db.newDocument();
copyKidsChangingNS(doc, newDoc, uriMap);
return newDoc;
}
XSL也许是最简单的解决方案,并应工作在JAXP。你尝试了什么,它是如何失败的? – skaffman 2009-09-29 22:25:31