Java+DOM : How do I set the base namespace of an (already created) Document?

标签 java xml dom namespaces document

我正在处理一个已创建 文档对象。 我必须能够将它的基本命名空间(属性名称“xmlns”)设置为特定值。 我的输入是 DOM,类似于:

<root>...some content...</root>

我需要的是 DOM,它类似于:

<root xmlns="myNamespace">...some content...</root>

就是这样。很简单,不是吗? 错了!不适用于 DOM!

我尝试了以下方法:

1) 使用 doc.getDocumentElement().setAttribute("xmlns","myNamespace")

我得到一个包含空 xmlns 的文档(它适用于任何其他属性名称!)

<root xmlns="">...</root>

2) 使用重命名节点(...)

首先克隆文档:

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(),但也许只有我自己。

现在重命名根节点:

output.renameNode(output.getDocumentElement(),"myNamespace",
    output.getDocumentElement().getTagName());

现在不是很简单吗? ;)

我现在得到的是:

<root xmlns="myNamespace">
    <someElement xmlns=""/>
    <someOtherElement xmlns=""/>
</root>

所以(正如我们所有人所预料的,对吗?),这仅重命名根节点的命名空间。

诅咒你,DOM!

有什么方法可以递归地执行此操作(无需编写自己的递归方法)?

请帮忙;)

请不要建议我做一些花哨的解决方法,比如将 DOM 转换为 别的东西,改变那里的 namespace ,然后将其转换回来。 我需要 DOM,因为它是操作 XML 的最快的标准方式。

注意:我使用的是最新的 JDK。

编辑
从问题中删除了与命名空间前缀有关的错误假设。

最佳答案

我今天遇到了同样的问题。我最终使用了 @ivan_ivanovich_ivanoff answer 的部分内容但删除了递归并修复了一些错误。

非常重要:如果旧命名空间为 null,您必须添加两个翻译,一个是从 null 到新的 namespaceURI 和另一个从 "" 到您的新 namespaceURI。发生这种情况是因为第一次调用 renameNode 会将具有 null namespaceURI 的现有节点更改为 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"

完整源代码如下:

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 : How do I set the base namespace of an (already created) Document?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1492428/

相关文章:

java - JOptionPane 自定义 JButton 选择后未关闭

java - 方法中的多个 Try/Catch block 是否应该组合

Java保存字符串

xml - 声明为 DTD 的 DOCTYPE 如何影响 XSLT 转换?

Javascript 性能 - 迭代 dom 并添加监听器

java 使用JAXP读取xml根节点描述

html - 找出浏览器在打印时插入分页符的位置

java - Character.isWhitespace(char) 和 Character.isSpaceChar(char) 有什么区别

java - 我在使用嵌套 `if` 语句时遇到问题

java - 替换基本 fragment 及其布局时 fragment 重叠