Default namespace handling in 11.2.5
February 22, 2016 Leave a comment
In UniVerse 11.2 the XML libraries used by UV were updated. This updating appears to have broken the namespace handling when adding new nodes to a DOM with a default namespace.
The problem
If an XML document has a default namespace, such as this:
<TheBeginning xmlns="http://www.wordpress.com/webpath/20160222"> </TheBeginning>
the addition of nodes that do not explicitly reference a namespace produces an unexpected result:
<TheBeginning xmlns="http://www.wordpress.com/webpath/20160222">
<Header xmlns=""/>
</TheBeginning>
The Header node should look like this:
<TheBeginning xmlns="http://www.wordpress.com/webpath/20160222"> <Header/> </TheBeginning>
My thinking is that under the 11.2 XML module, the XDOMCreateNode() and XDOMAddChild() functions are expecting you to know exactly what you are doing. So when you add a node that has no namespace to a DOM that has a default the new node is added with no namespace.
The work-around
There is a way to work with the 11.2 XDOM functions to get nodes to have the same default namespace as the DOM to which they are added. It involves changing how the node name is structured.
The code used to create the Header node was:
$include UNIVERSE.INCLUDE XML.H nsPATH="http://www.wordpress.com/webpath/20160222" rootXML = '<TheBeginning ' rootXML := ' xmlns="':nsPATH:'"' rootXML := '></TheBeginning>' xmlStatus = XDOMOpen(rootXML,XML.FROM.STRING,domHandle) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end else xmlStatus = XDOMLocateNode(domHandle, XDOM.CHILD, XDOM.FIRST.CHILD, XDOM.ELEMENT.NODE, rootNode) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end else name.NODE = "Header" map.NS = nsPATH xmlStatus = XDOMCreateNode( rootNode, name.NODE, "", XDOM.ELEMENT.NODE, headerNode) if xmlStatus = XML.SUCCESS then xmlStatus = XDOMAddChild( rootNode, "", map.NS, headerNode, XDOM.NODUP) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end end XmlStatus= XDOMWrite(domHandle, recXml, XML.TO.STRING) if (xmlStatus # XML.ERROR) then crt recXml else crt "failed to build XML!" end end STOP HANDLE.ERROR: CRT 'HANDLE.ERROR' DEBUG return
In order to add a node that uses the default name space the name.NODE variable needs to be defined much the same as when adding a node with a namespace prefix.
In Creating XML Elements with a namespace prefix I cover how to add nodes with a namespace prefix. The work-around is very similar, so we will work through adding the Footer node as an example.
There is no namespace prefix involved in this DOM, so the structure of the name.NODE variable looks like this:
“:” {node_Name} “:” {namespace_PATH}
As we are dealing with the default namespace the map.NS variable, important for the namespace prefix handling, needs to be empty for this work-around to work. So the code to add the Footer node looks like this:
name.NODE = ":Footer:":nsPATH map.NS = "" xmlStatus = XDOMCreateNode( rootNode, name.NODE, "", XDOM.ELEMENT.NODE, headerNode) if xmlStatus = XML.SUCCESS then xmlStatus = XDOMAddChild( rootNode, "", map.NS, headerNode, XDOM.NODUP) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end end
Insert this above the XDOMWrite() function in the above program, and the output should look like this on an 11.2.x database:
<TheBeginning xmlns="http://www.wordpress.com/webpath/20160222">
<Header xmlns=""/>
<Footer/>
</TheBeginning>
This is a UV11.2.x only Solution
Beware that his logic only works on UV 11.2.x. As the XML libraries integrated in other version of UV behave differently, this work-around is only functional on UV 11.2.x servers.
To get this example to work on multiple UV versions you will need to leverage the compiler directives to test the UV version when compiling the code. The alternative is to code in a test of the UV version at runtime, but as there is no BASIC function that returns the current UV version this is a bit more cumbersome.
******************************************************************************* * * Program TEST_UV11XML_NAMESPACE * ***************************************************************************** $include UNIVERSE.INCLUDE XML.H nsPATH="http://www.wordpress.com/webpath/20160222" rootXML = '<TheBeginning ' rootXML := ' xmlns="':nsPATH:'"' rootXML := '></TheBeginning>' xmlStatus = XDOMOpen(rootXML,XML.FROM.STRING,domHandle) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end else xmlStatus = XDOMLocateNode(domHandle, XDOM.CHILD, XDOM.FIRST.CHILD, XDOM.ELEMENT.NODE, rootNode) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end else name.NODE = "Header" map.NS = nsPATH xmlStatus = XDOMCreateNode( rootNode, name.NODE, "", XDOM.ELEMENT.NODE, headerNode) if xmlStatus = XML.SUCCESS then xmlStatus = XDOMAddChild( rootNode, "", map.NS, headerNode, XDOM.NODUP) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end end $IFDEF U2__UNIVERSEv11 name.NODE = ":Footer:":nsPATH map.NS = "" $ELSE name.NODE = "Footer" $ENDIF xmlStatus = XDOMCreateNode( rootNode, name.NODE, "", XDOM.ELEMENT.NODE, headerNode) if xmlStatus = XML.SUCCESS then xmlStatus = XDOMAddChild( rootNode, "", map.NS, headerNode, XDOM.NODUP) if (xmlStatus = XML.ERROR) then gosub HANDLE.ERROR end end XmlStatus= XDOMWrite(domHandle, recXml, XML.TO.STRING) if (xmlStatus # XML.ERROR) then crt recXml else crt "failed to build XML!" end end STOP HANDLE.ERROR: CRT 'HANDLE.ERROR' DEBUG return