Traversing an XML node’s attributes
October 5, 2010 2 Comments
UV 10.2
The XDOM API in UniVerse provides the XDOMLocateNode() to traverse up and down, and iterate across the nodes in an XML document.
This works well, until you need to iterate through the attributes of an XML document. The XDOMLocateNode() will find the first child attribute, but there is no way to use it to iterate through all the attributes. This is because attributes are NOT child nodes of elements.
How to traverse a node’s attributes?
Using XPath 1.0 syntax and the XDOMEvaluate() function (which is in the U2 Basic Extensions manual – it is just not in the index!), it is possible to query the quantity of attributes on a node. It is then possible to query the name of each of the numbered attributes.
In the example below, the show.attribs subroutine uses two XPath 1.0 queries:
- The first (assigned to xpath.ATTR.COUNT) is passed to the XDOMEvaluate() function, and returns the quantity of attributes in the node.
- The second (assigned to xpath.ATTR.NODE) is passed to the XDOMEvaluate() function, and returns the name of the specified attribute number.
Once the name of the attribute is known, the XDOMGetAttribute() function returns a XDOM handle to the attribute node, and XDOMGetNodeValue() returns the value of the attribute node.
Example
$include UNIVERSE.INCLUDE XML.H sample.XML = '<?xml version="1.0" encoding="UTF-8"?>' sample.XML := '<animals>' sample.XML := ' <animal species="giraffe" size="large" horns="yes">' sample.XML := ' <legs>4</legs>' sample.XML := ' <color>tan/orange</color>' sample.XML := ' <tail>Y</tail>' sample.XML := ' </animal>' sample.XML := ' <animal species="rhino" size="large" horns="yes" dangerlevel="extreme">' sample.XML := ' <legs>4</legs>' sample.XML := ' <color>mud</color>' sample.XML := ' <tail>Y</tail>' sample.XML := ' </animal>' sample.XML := ' <animal species="rodent" size="tiny" cutenesslevel="medium">' sample.XML := ' <legs>4</legs>' sample.XML := ' <color>fawn</color>' sample.XML := ' <tail>Y</tail>' sample.XML := ' </animal>' sample.XML := '</animals>' nsMAP = "" doc.ID = "Sample2.xml" path.LOOK = "//animals" stat = XDOMOpen(sample.XML,XML.FROM.STRING,dom.HANDLE) if stat # XML.ERROR then stat = XDOMLocate(dom.HANDLE,path.LOOK,nsMAP,dom.ANIMALS) if stat # XML.ERROR then * Found the root node. Use XDomLocateNode to step down to first child stat = XDOMLocateNode( dom.ANIMALS, XDOM.CHILD, XDOM.FIRST.CHILD, XDOM.ELEMENT.NODE, dom.ANIMALS.CHILD) if stat # XML.ERROR then * Found first child element loop stat = XDOMGetNodeName(dom.ANIMALS.CHILD, name.NODE) if stat # XML.ERROR then crt "Name = ":squote(name.NODE) gosub show.attribs end * Find the next node nstat = XDOMLocateNode( dom.ANIMALS.CHILD, XDOM.NEXT.SIBLING, 1, XDOM.ELEMENT.NODE, dom.ANIMALS.CHILD) while stat # XML.ERROR and nstat # XML.ERROR do repeat cstat = XDOMClose(dom.ANIMALS.CHILD) end cstat = XDOMClose(dom.ANIMALS) end cstat = XDOMClose(dom.HANDLE) end if stat then crt "BAD: STATUS() = ":stat stat = XMLGetError( xmlERR, xmlDESC) crt "[":xmlERR:"] ":xmlDESC end stop * This is where we show all the node's attributes show.attribs: qty.ATTRIB = 0 * Get the count of attributes for this node xpath.ATTR.COUNT = "count(@*)" stat = XDOMEvaluate( dom.ANIMALS.CHILD, xpath.ATTR.COUNT, nsMAP, qty.ATTRIB) if stat # XML.ERROR and qty.ATTRIB then * There ARE attributes for this node for num.ATTRIB = 1 to qty.ATTRIB while stat # XML.ERROR * Use XPath 1.0 syntax to get the name of each attribute xpath.ATTR.NAME = "name(@*[position()=":num.ATTRIB:"])" name.ATTRIB = '' stat = XDOMEvaluate(dom.ANIMALS.CHILD,xpath.ATTR.NAME,nsMAP, name.ATTRIB) if stat # XML.ERROR then * Now we have the attirbute name, we can get the value crt " ":name.ATTRIB:" = ": stat = XDOMGetAttribute(dom.ANIMALS.CHILD, name.ATTRIB, node.ATTRIB) if stat # XML.ERROR then * Got the attribute node, get the value stat = XDOMGetNodeValue(node.ATTRIB, val.ATTRIB) crt dquote(val.ATTRIB): cstat = XDOMClose(node.ATTRIB) end crt end next num.ATTRIB end return