Java + DOM:如何设置(已创建)文档的基本命名空间? [英] Java+DOM: How do I set the base namespace of an (already created) Document?

查看:125
本文介绍了Java + DOM:如何设置(已创建)文档的基本命名空间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理已经创建的 文档对象。
我必须能够将其基本命名空间(属性名称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 to something 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.

编辑

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

推荐答案

今天我有同样的问题。我最终使用了 @ ivan_ivanovich_ivanoff answer ,但删除了递归并修复了一些错误。

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:如何设置(已创建)文档的基本命名空间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆