6

I am creating several XML files via Java and up to this point everything worked fine, but now I've run into a problem when trying to create a file with namespace prefixed nodes, i.e, stuff like <tns:node> ... </tns:node> using a refactored version of my code that's already working for normal xml files without namespaces.

The error getting thrown is:

org.w3c.dom.DOMException: INVALID_CHARACTER_ERR: Ungültiges XML-Zeichen angegeben. 

Sorry for the German in there, it says "invalid XML-sign specified".

The codeline where the error occurs:

Element mainRootElement = doc.createElement("tns:cmds xmlns:tns=\"http://abc.de/x/y/z\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://abc.de/x/y/z xyzschema.xsd\"");

To eliminate the possibility of the error resulting in escaping that rather long string or something among those lines I also tried just using Element mainRootElement = doc.createElement("tns:cmds");, however, this results in the same error.

That's why I figure it has something to do with the namespace declaration, i.e., the : used to do it, as that's the only "invalid" character I could think of in that string.

Can anyone confirm this is the source of the problem? If so, is there an easy solution to it? Can Java DOM use namespaced tags at all?

Edit: Whole method for reference

private void generateScriptXML()
    {
        DocumentBuilderFactory icFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder icBuilder;
        try
        {
            icBuilder = icFactory.newDocumentBuilder();
            Document doc = icBuilder.newDocument();

            Element mainRootElement = doc.createElement("tns:cmds xmlns:tns=\"http://abc.de/x/y/z\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://abc.de/x/y/z xyzschema.xsd\"");

            doc.appendChild(mainRootElement);
            mainRootElement.appendChild(getAttributes(doc,"xxx", "yyy", "zzz"));
            mainRootElement.appendChild(getAttributes(doc,"aaa", "bbb", "ccc"));
            mainRootElement.appendChild(getAttributes(doc,"ddd", "eee", "fff"));
            ...
            ...

            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            DOMSource source = new DOMSource(doc);
            StreamResult streamResult = new StreamResult(new File(vfsPath));
            transformer.transform(source, streamResult);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
daZza
  • 1,669
  • 1
  • 29
  • 51

1 Answers1

11

Wrong method, try the *NS variants:

Element mainRootElement = doc.createElementNS(
   "http://abc.de/x/y/z", // namespace
   "tns:cmds" // node name including prefix
);

First argument is the namespace, second the node name including the prefix/alias. Namespace definitions will be added automatically for the namespace if needed. It works to set them as attributes, too.

The namespace in your original source is http://abc.de/x/y/z. With the attribute xmlns:tns="http://abc.de/x/y/z" the alias/prefix tns is defined for the namespace. The DOM api will implicitly add namespaces for nodes created with the *NS methods.

xmlns and xml are reserved/default namespace prefixes for specific namespaces. The namespace for xmlns (namespace definitions) is http://www.w3.org/2000/xmlns/.

To add an xmlns:* attribute with setAttributeNS() use the xmlns namespace:

mainRootElement.setAttributeNS(
  "http://www.w3.org/2000/xmlns/", // namespace
  "xmlns:xsi", // node name including prefix
  "http://www.w3.org/2001/XMLSchema-instance" // value
);

But even that is not needed. Just like for elements, the namespace definition will be added implicitly if you add an attribute node using it.

mainRootElement.setAttributeNS(
  "http://www.w3.org/2001/XMLSchema-instance", // namespace
  "xsi:schemaLocation", // node name including prefix
  "http://abc.de/x/y/z xyzschema.xsd" // value
);

Namespaces Prefixes

If you see a nodename like xsi:schemaLocation you can resolve by looking for the xmlns:xsi attribute. This attribute is the namepace definition. The value is the actual namespace. So if you have an attribute xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" the node name can be resolved to {http://www.w3.org/2001/XMLSchema-instance}schemaLocation (Clark notation).

If you want to create the node you need 3 values:

  1. the namespace: http://www.w3.org/2001/XMLSchema-instance
  2. the local node name: schemaLocation
  3. the prefix: xsi

The prefix is optional for element nodes, but mandatory for attribute nodes. The following three XMLs resolve all to the element node name {http://abc.de/x/y/z}cmds:

  • <tns:cmds xmlns:tns="http://abc.de/x/y/z"/>
  • <cmds xmlns="http://abc.de/x/y/z"/>
  • <other:cmds xmlns:other="http://abc.de/x/y/z"/>
ThW
  • 19,120
  • 3
  • 22
  • 44
  • I tried it with the ...NS methods and the creation of the root node seems to work. However, I am getting an error when I try to add the attributes: `org.w3c.dom.DOMException: NAMESPACE_ERR: You tried to create or change an object in a way that is incorrect in regards to namespaces` (loosely translated). I updated the main post with the new code – daZza Feb 13 '15 at 10:58
  • You need to provide the namespace of the node in createElementNS() - always. – ThW Feb 13 '15 at 11:09
  • But the `tns:cmds` node doesn't have a namespace itself? Only the three attributes `xmlns:xsi`, `xsi:schemaLocation` and `xmlns:tns` do? Or maybe I'm just not understand the namespace thing correctly – daZza Feb 13 '15 at 11:10
  • You currently creating the element for the namespace `` (empty) with the prefix `tns`. You're trying to set attributes with the prefix `xmlns` for different namespace. `xmlns` is a reserved prefix. – ThW Feb 13 '15 at 11:16
  • Ok, changed it again and updated the original post. `tns:cmds` and `xsi:schemaLocation` work properly now, however, the NAMESPACE_ERR is still thrown for the other attribute `xmlns:xsi`. How can that be? It's constructred exactly the same as the first attribute which works. – daZza Feb 13 '15 at 11:18
  • Awesome, many thanks for the in-depth explanation! It works fine now – daZza Feb 13 '15 at 11:49