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

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


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.

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="".

Example of usage:

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"

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);        }    }}


In addition to setting the prefix, you must also declare your namespace somewhere.

[EDIT] If you look into the package org.w3c.dom, you'll notice that there is no support for namespaces whatsoever except that you can create a Document node with a namespace URI:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setNamespaceAware(true);DocumentBuilder builder = factory.newDocumentBuilder();DOMImplementation DOMImplementation = builder.getDOMImplementation();Document doc = DOMImplementation.createDocument(    "http://www.somecompany.com/2005/xyz", // namespace    "root",    null /*DocumentType*/);Element root = doc.getDocumentElement();root.setPrefix("xyz");root.setAttribute(    "xmlns:xyz",    "http://www.somecompany.com/2005/xyz");

With the standard W3C DOM API of Java 5 (and up), it's not possible to modify the namespace of a node.

But the W3C DOM API is just a couple of interfaces. So what you should try is to look at the implementation (i.e. the actual class of your document instance), cast it to the real type. This type should have additional methods and if you're lucky, you can use those to modify the namespace.


Well, here goes the recursive "solution":
(I still hope that someone might find a better way to do this)

public static void renameNamespaceRecursive(Document doc, Node node,        String namespace) {    if (node.getNodeType() == Node.ELEMENT_NODE) {        System.out.println("renaming type: " + node.getClass()            + ", name: " + node.getNodeName());        doc.renameNode(node, namespace, node.getNodeName());    }    NodeList list = node.getChildNodes();    for (int i = 0; i < list.getLength(); ++i) {        renameNamespaceRecursive(doc, list.item(i), namespace);    }}

Seems to work, although I don't know if it's correct to rename only the node typeELEMENT_NODE, or if other node types must be renamed.