问题描述
我正在处理已经创建的 文档对象。
我必须能够将其基本命名空间(属性名称xmlns)设置为某个值。
我的输入是DOM,类似于:
I am dealing with an already created Document object.I have to be able to set it's base namespace (attribute name "xmlns") to certain value.My input is DOM and is something like:
<root>...some content...</root>
我需要的是DOM,类似于:
What I need is DOM which is something like:
<root xmlns="myNamespace">...some content...</root>
就是这样。容易,不是吗? 错!我没有使用DOM!
That's it. Easy, isn't it? Wrong! Not with DOM!
我得到一个带有空xmlns的文档(它在任何其他属性名称!)
I get a document with empty xmlns (it works on any other attribute name!)
<root xmlns="">...</root>
2)使用renameNode(...)
第一个克隆文档:
Document input = /*that external Document whose namespace I want to alter*/;
DocumentBuilderFactory BUILDER_FACTORY_NS = DocumentBuilderFactory.newInstance();
BUILDER_FACTORY_NS.setNamespaceAware(true);
Document output = BUILDER_NS.newDocument();
output.appendChild(output.importNode(input.getDocumentElement(), true));
我真的缺少document.clone(),但也许只是我。 / em>
I'm really missing document.clone(), but perhaps it's just me.
现在重命名根节点:
output.renameNode(output.getDocumentElement(),"myNamespace",
output.getDocumentElement().getTagName());
现在不是 直截了当? ;)
Now isn't that straightforward? ;)
我现在得到的是:
<root xmlns="myNamespace">
<someElement xmlns=""/>
<someOtherElement xmlns=""/>
</root>
所以(像我们所有人都预料到的,对吗?)重命名根节点的命名空间
So (as all of us have expected, right?), this renames the namespace only of the the root node.
诅咒您,DOM!
有没有办法递归执行(没有编写自己的递归方法)?
Is there any way to do this recursively (without writing an own recursive method)?
请不要建议我做一些奇怪的解决方法,例如将DOM转换为
别的东西,改变命名空间,并将其转换回来。
我需要DOM,因为它是处理XML的最快标准方式。
Please don't advice me to do some fancy workaround, such as transforming DOM tosomething else, alter the namespace there, and transform it back.I need DOM because it's the fastest standard way to manipulate XML.
注意:我使用的是最新的JDK。
Note: I'm using the latest JDK.
编辑
从问题中删除错误的假设,这与命名空间前缀有关。
推荐答案
今天我有同样的问题。我最终使用了,但删除了递归并修复了一些错误。
I had the very same problem today. I ended up using parts of @ivan_ivanovich_ivanoff answer but removed the recursion and fixed some bugs.
非常重要:如果旧命名空间 null
您必须添加两个翻译,一个从 null
添加到新的 namespaceURI
另一个来自到新的
namespaceURI
。这是因为第一次调用 renameNode
将会更改现有的节点,它们具有一个 null
namespaceURI
到 xmlns =
。
Very important: if old namespace is null
you must add two translations, one from null
to your new namespaceURI
and another from ""
to your new namespaceURI
. This happens because the first call to renameNode
will change existing nodes that have a null
namespaceURI
to xmlns=""
.
使用示例:
Document xmlDoc = ...;
new XmlNamespaceTranslator()
.addTranslation(null, "new_ns")
.addTranslation("", "new_ns")
.translateNamespaces(xmlDoc);
// xmlDoc will have nodes with namespace null or "" changed to "new_ns"
$ b $完整的源代码如下:
Full source code follows:
public class XmlNamespaceTranslator {
private Map<Key<String>, Value<String>> translations = new HashMap<Key<String>, Value<String>>();
public XmlNamespaceTranslator addTranslation(String fromNamespaceURI, String toNamespaceURI) {
Key<String> key = new Key<String>(fromNamespaceURI);
Value<String> value = new Value<String>(toNamespaceURI);
this.translations.put(key, value);
return this;
}
public void translateNamespaces(Document xmlDoc) {
Stack<Node> nodes = new Stack<Node>();
nodes.push(xmlDoc.getDocumentElement());
while (!nodes.isEmpty()) {
Node node = nodes.pop();
switch (node.getNodeType()) {
case Node.ATTRIBUTE_NODE:
case Node.ELEMENT_NODE:
Value<String> value = this.translations.get(new Key<String>(node.getNamespaceURI()));
if (value != null) {
// the reassignment to node is very important. as per javadoc renameNode will
// try to modify node (first parameter) in place. If that is not possible it
// will replace that node for a new created one and return it to the caller.
// if we did not reassign node we will get no childs in the loop below.
node = xmlDoc.renameNode(node, value.getValue(), node.getNodeName());
}
break;
}
// for attributes of this node
NamedNodeMap attributes = node.getAttributes();
if (!(attributes == null || attributes.getLength() == 0)) {
for (int i = 0, count = attributes.getLength(); i < count; ++i) {
Node attribute = attributes.item(i);
if (attribute != null) {
nodes.push(attribute);
}
}
}
// for child nodes of this node
NodeList childNodes = node.getChildNodes();
if (!(childNodes == null || childNodes.getLength() == 0)) {
for (int i = 0, count = childNodes.getLength(); i < count; ++i) {
Node childNode = childNodes.item(i);
if (childNode != null) {
nodes.push(childNode);
}
}
}
}
}
// these will allow null values to be stored on a map so that we can distinguish
// from values being on the map or not. map implementation returns null if the there
// is no map element with a given key. If the value is null there is no way to
// distinguish from value not being on the map or value being null. these classes
// remove ambiguity.
private static class Holder<T> {
protected final T value;
public Holder(T value) {
this.value = value;
}
public T getValue() {
return value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Holder<?> other = (Holder<?>) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
private static class Key<T> extends Holder<T> {
public Key(T value) {
super(value);
}
}
private static class Value<T> extends Holder<T> {
public Value(T value) {
super(value);
}
}
}
这篇关于Java + DOM:如何设置(已创建)文档的基本命名空间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!