如果给定一个w3c DOM(Java的默认实现,特别是)更改该DOM中每个元素/属性/节点的命名空间,我该怎么办?有效地,优选地。 DOM上似乎没有setNamespaceURI方法,这很不方便。
我已经尝试过XSL方法,但是他们无法在JAXP变换器中工作(虽然它们在Saxon9B中可以正常工作,但由于其他各种原因我无法使用它。)
基本上,我需要一个纯粹的核心java解决方案,它允许我获取一个文档并更改其命名空间。
答案 0 :(得分:9)
这在名称空间感知DOM上效率不高。您必须在要更改其名称空间的每个后代元素上使用DOM Level 3 Core方法Document.renameNode(javadoc)。 (通常不需要更改这么多的Attr节点,因为没有前缀的Attr节点的命名空间始终为null,而不是Element的命名空间。)
如果您要做的只是将一个命名空间替换为另一个命名空间,那么使用名称空间不知道的DOM可能会更快,只需更改有问题的xmlns属性即可。你应该能够通过将DOMConfiguration'namespaces'参数设置为false来获得一个名称空间不知道的DOM,但是我没有在Java中尝试过这个,而且这是一种模糊的东西,因为DOM imps会出错。< / p>
答案 1 :(得分:3)
基于我极度偏见的意见,你想要的东西将是一个巨大的痛苦。我可以看到类型转换地狱和许多递归for循环所需要的可靠性!啊,Java的默认实现,我如何讨厌你的NPE:s内部,逆向逻辑,简单操作所需的额外步骤!
所以是的,我的建议是对每个可能的节点类型进行类型转换的循环是递归的,根据我个人的经验,Java的默认实现很糟糕。
答案 2 :(得分:2)
如果intent只是更改名称空间,那么只需使用一些流编辑器将NS映射更改为URL。
Namspace或多或少是命名空间前缀和URI之间的绑定。为了快速更改名称空间,只需更改映射:
在: 的xmlns:myns名字= “我的名称空间URI”
在: 的xmlns:myns名字= “我的新的命名空间-URI”
如果intent只是改变命名空间,那么基本上改变映射就足够了。此外,如果XML Document具有默认命名空间,则更改默认命名空间URL值将更改整个文档的命名空间。
在: 的xmlns = “我的名称空间URI”
在: 的xmlns = “我的新的命名空间-URI”
答案 3 :(得分:2)
如果给定一个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允许您准确指定节点应该如何命名空间,因此不存在更改命名空间的全部一步调用。您需要在不考虑前缀和URI值的情况下使用DOM,而是在任何给定时间考虑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
声明可以留下来,这很混乱,但很大程度上无关紧要;可能是其他我没想过的东西。
答案 4 :(得分:1)
给定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;
}
答案 5 :(得分:0)
通过将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
答案 6 :(得分:0)
您可以将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将在复制期间处理所有脏工作,只留下命名空间替换逻辑。
答案 7 :(得分:0)
如果您可以使用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();