Walking a Parsed XML DOM in LotusScript: DocumentNode, ElementNode, NodeList, NamedNodeMap
You’ve fed an XML file through NotesDOMParser and called Process. Now you have a tree in memory and need to pull values out of it — the root element, every <item> under it, the id attribute on each. The navigation surface is a small family of node classes, and while it follows the W3C DOM shape, the LotusScript binding has a few sharp edges worth knowing before you write the loop.
This is the first of three pieces on the Domino DOM node classes: navigation here, the content-carrying node types next, and the DTD/declaration tier plus SAX error handling after that.
TL;DR
- After
Set domParser = session.CreateDOMParser(inStream, outStream)anddomParser.Process, the root isdomParser.Document— aNotesDOMDocumentNode. “There is only one document node in a DOM tree.” - The root element is
documentNode.DocumentElement; the document node itself is above it. GetElementsByTagName(name)(on the document or any element) returns aNotesDOMNodeListof matching elements.GetItemis 1-based — iterateFor i = 1 To list.NumberOfEntries. This is the single biggest trap coming from JavaScript/Java DOM.- An element’s attributes come from its
Attributesproperty as aNotesDOMNamedNodeMap— also 1-basedGetItem. To fetch one attribute by name, use the element’sGetAttribute(name)/GetAttributeNode(name)— there is noGetItemByName. - There is no
ChildNodesNodeList property — walk children withFirstChild+NextSibling(+NumberOfChildNodes). - All these classes are new with Release 6, not supported in COM.
The shape of the tree
Every node — document, element, text, attribute — derives from NotesDOMNode, which carries the navigation properties: NodeName, NodeType, NodeValue, ParentNode, FirstChild, LastChild, NextSibling, PreviousSibling, Attributes, NumberOfChildNodes, and the all-important IsNull guard. You discriminate node kinds by comparing NodeType to the DOMNODETYPE_* constants:
| Constant | Value |
|---|---|
DOMNODETYPE_ELEMENT_NODE | 1 |
DOMNODETYPE_ATTRIBUTE_NODE | 2 |
DOMNODETYPE_TEXT_NODE | 3 |
DOMNODETYPE_COMMENT_NODE | 8 |
DOMNODETYPE_DOCUMENT_NODE | 9 |
(The full list runs 0–13; the others come up in the later articles.)
The NotesDOMDocumentNode sits at the top — “the root of the document tree” — and adds one key property over the base node: DocumentElement, which jumps you straight to the root element. The NotesDOMElementNode adds TagName and the attribute accessors GetAttribute / GetAttributeNode.
Querying and reading attributes
GetElementsByTagName “Returns a NotesDOMNodeList of all descendant elements with a given tag name, in the order in which they are encountered.” The list exposes NumberOfEntries and a 1-based GetItem:
Dim session As New NotesSessionDim inStream As NotesStream, outStream As NotesStreamSet inStream = session.CreateStreamCall inStream.WriteText(|<order id="A1"><line sku="X9">Widget</line><line sku="Z2">Gadget</line></order>|)inStream.Position = 0Set outStream = session.CreateStream
Dim domParser As NotesDOMParserSet domParser = session.CreateDOMParser(inStream, outStream)domParser.Process
Dim docNode As NotesDOMDocumentNodeSet docNode = domParser.Document
Dim root As NotesDOMElementNodeSet root = docNode.DocumentElementPrint "Root element: " & root.TagName & ", id=" & root.GetAttribute("id")
Dim lines As NotesDOMNodeListSet lines = root.GetElementsByTagName("line")Dim i As IntegerFor i = 1 To lines.NumberOfEntries ' 1-based! Dim el As NotesDOMElementNode Set el = lines.GetItem(i) Print "line sku=" & el.GetAttribute("sku")Next(Adapted — the parser requires a NotesStream, so the XML is written into one rather than passed as a string.)
To enumerate all attributes of an element rather than fetch one by name, go through its Attributes property — a NotesDOMNamedNodeMap, “used by methods of the NotesDOMNode class for returning the list of an element node’s attributes.” Same 1-based GetItem:
Dim attrs As NotesDOMNamedNodeMapSet attrs = root.AttributesFor i = 1 To attrs.NumberOfEntries Print attrs.GetItem(i).NodeName & " = " & attrs.GetItem(i).NodeValueNextThere’s no GetItemByName on the map — when you know the attribute name, the element’s GetAttribute(name) is the direct route.
The full recursive walk
When you don’t know the structure ahead of time, recurse with FirstChild / NextSibling, branching on NodeType. This is the official pattern:
Sub walkTree(node As NotesDOMNode) If node.IsNull Then Exit Sub Select Case node.NodeType Case DOMNODETYPE_DOCUMENT_NODE Dim child As NotesDOMNode Set child = node.FirstChild Dim n As Integer n = node.NumberOfChildNodes While n > 0 Call walkTree(child) Set child = child.NextSibling n = n - 1 Wend Case DOMNODETYPE_ELEMENT_NODE Print "Element: " & node.NodeName Dim kid As NotesDOMNode Set kid = node.FirstChild Dim m As Integer m = node.NumberOfChildNodes While m > 0 Call walkTree(kid) Set kid = kid.NextSibling m = m - 1 Wend Case DOMNODETYPE_TEXT_NODE Print "Text: " & node.NodeValue End SelectEnd SubThe shape — IsNull guard, Select Case node.NodeType, walk children by count via FirstChild/NextSibling — is exactly what the official NotesDOMParser walk-tree example uses. Note there’s no node.ChildNodes to iterate; NumberOfChildNodes + sibling-stepping is how you traverse.
What about Java and SSJS?
This is the unusual case where there’s no clean Domino-API counterpart. The NotesDOM* node family is a LotusScript (and Java-agent) facility in the Domino API; the standard route for XML DOM work in Java and XPages is the platform’s own org.w3c.dom / JAXP stack, not a lotus.domino class. So relatedJava and relatedSsjs are intentionally empty here — if you’re doing this in Java, you’d reach for javax.xml.parsers.DocumentBuilder and the W3C interfaces rather than a Domino class, which (unlike the Domino binding) are 0-based and use getData() / getChildNodes() in the canonical W3C shape.