Parsing XML to add specific node values to listbox

I am using a HTTPSecureSocket to send a request to a server that contains information about phones. The request is formed correctly and I receive the following information

https://scghq.com:5101/ns/sharing/jfh9L

This code is in a method that is called when that data appears as a response to the request

vEvent is a variable that holds Event Request made to the Cisco UCM server
vInfo is a Variant that holds the response from the Cisco UCM server
This is a short version of a method that looks at the request and response.

//IF WE’RE DEALING WITH A PHONE COMMAND
If (vObservable isa stdHTTPSS) AND (stdHTTPSS(vObservable).ObservableCallbackCommand = “get_phone_id”) Then

Select Case vEvent

Case “InvalidItem”
System.DebugLog("\n*********************************************************\n")
System.DebugLog(vInfo.StringValue)
Printable_Phone_Info_LB.AddRow(stdHTTPSS(vObservable).ObservableCallbackTag,“Not Found”)

Case “ValidItem”
Var xml as new XmlDocument
Var nodeList as XmlNodeList
Var root As XmlNode
Var node As XmlNode

// Load XML
Try
  xml.LoadXml(vInfo.StringValue)
Catch e As XmlException
  MessageBox("XML error: " + e.Message)
  Return
End Try

root = xml.DocumentElement
// Get all the Phone Info nodes
nodeList = xml.Xql("//phone") // Find all Phone Info

If root.ChildCount > 0 Then
  
  // Add Phone Info to ListBox
  For i As Integer = 0 To nodeList.Length - 1
    node = nodeList.Item(i)
    Printable_Phone_Info_LB.AddRow(node.GetAttribute("name"),  _
    node.GetAttribute("model"),  _
    node.GetAttribute("description"),  _
    node.GetAttribute("devicePoolName"),  _
    node.GetAttribute("phoneTemplateName"),  _
    node.GetAttribute("label"))
  Next
  'This prints 2 columns of info as a test Printable_Phone_Info_LB.AddRow(stdHTTPSS(vObservable).ObservableCallbackTag,"Found")
End

End Select
End If

Where I attempt to parse this data // Add Phone Info to ListBox and place it on a ListBox nothing is displayed. I have attempted several modifications to grab the information returned associated with the attributes identified above.
How do I properly iterate through to get the information I am attempting to place in the ListBox ‘Printable_Phone_Info_LB’

You’re not getting the desired results because the items you are looking for are not attributes of the phone nodes, but rather children. You’ll need to iterate the children of the phone node and get their value instead.

For example (simple to show the first one, no Nil checking and not tested). You’ll want to build out the Select with each of the children you need, store them in temporary variables like this, then add it to your listbox.

node = nodeList.Item(i)
Dim phoneName As String
Dim xChild as XmlNode = node.FirstChild
While xChild <> Nil
    Select Case xChild.name
    Case "name"
        phoneName = xChild.Value
    End Select
    xChild = xChild.NextSibling
Wend

Attributes are within the node’s angle brackets. So, at the example you pointed to, ctiid and uuid are attributes.

I appreciate your response and you got me thinking. I found a solution but I am not certain this is the best approach. I came into this thinking like I could walk through this in a similar manner as I do for DBs with many tables. I could not see how to do that and perhaps it is my learning curve on how to do that with XML data.

I have data returned from a request. This link is a file of that returned data. Phone Data Returned
I could be wrong but to me this seems to be a complex data structure. The first data I need is easy to get as it is exactly as you suggested. However there is data in lines, speeddials, and busylampfields that may or may not have data buried inside. Each of the mnemonics mentioned have no entries if nothing exists or indicate the members underneath. In code I walk through this returned data like this

If (vObservable isa stdHTTPSS) AND (stdHTTPSS(vObservable).ObservableCallbackCommand = "get_phone_id") then
  Select Case vEvent
  Case "InvalidItem"
    //System.DebugLog("\n*********************************************************\n")
    System.DebugLog(Str(stdHTTPSS(vObservable).LastErrorCode))
    System.DebugLog(vInfo)
    Printable_Phone_Info_LB.AddRow(stdHTTPSS(vObservable).ObservableCallbackTag,"Not Found")
    Error_TF.AddText(Str(vInfo))
    
  Case "ValidItem"
    Var xml as new XmlDocument
    Var Line_xml as new XmlDocument
    Var nodeList, Line_nodeList as XmlNodeList
    Var root, Line_root As XmlNode
    Var node, Line_node As XmlNode
    
    // Print response from Cisco UCM to debug
    System.DebugLog(vInfo.StringValue)
    
    // Load XML
    Try
      xml.LoadXml(vInfo.StringValue)
    Catch e As XmlException
      MessageBox("XML error: " + e.Message)
      Return
    End Try
    
    root = xml.DocumentElement
    nodeList = xml.Xql("//phone") // Get all the Phone Info 
    
    If root.ChildCount > 0 Then
      
      Printable_Phone_Info_LB.AddRow
      
      For i As Integer = 0 To nodeList.Length - 1
        node = nodeList.Item(i)
        
        Var phoneName As String
        Var xChild as XmlNode = node.FirstChild
        
        While xChild <> Nil
          Select Case xChild.name
          Case "name"
            phoneName = xChild.LastChild.Value
            Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, 0) = phoneName
            
          Case "model"
            phoneName = xChild.LastChild.Value
            Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, 1) = phoneName
            
          Case "description"
            phoneName = xChild.LastChild.Value
            Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, 2) = phoneName
            
          Case "devicePoolName"
            phoneName = xChild.LastChild.Value
            Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, 3) = phoneName
            
          Case "phoneTemplateName"
            phoneName = xChild.LastChild.Value
            Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, 4) = phoneName
            
          Case "lines"
            // Parse through XML to find "line" which must have at least 1 member
            Try
              Line_xml.LoadXml(xChild.ToString)
              Line_root = Line_xml.DocumentElement
              Line_nodeList = Line_xml.Xql("//line") // Get all Phone Info
              
              Var Line_Members As Integer = Line_xml.Xql("//line").Length // How many members?
              
              For xx As Integer = 0 to Line_Members - 1
                Line_node = Line_nodeList.Item(xx)
                Var Next_Child As XmlNode = Line_node.FirstChild
                Var my_nextSib As XmlNode = Next_Child.NextSibling
                Var my_nextSib1 As XmlNode = my_nextSib.NextSibling
                Var my_nextSib2 As XmlNode = my_nextSib1.NextSibling
                Var Next_Child1 As XmlNode = my_nextSib2.FirstChild
                Var Next_Child2 As XmlNode = Next_Child1.FirstChild
                
                If xx = 0 Then
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = Next_Child2.Value
                Else
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = _
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) + ", " + _
                  Next_Child2.Value
                End
              Next
              
            Catch e As XmlException
              MessageBox("XML error: " + e.Message)
              Return
            End Try
            
          Case "busyLampFields"
            // Check Attribute count becuase there could be 0 - No Speed Dial info
            If xChild.AttributeCount > 0 Then
              Line_xml.LoadXml(xChild.ToString)
              Line_root = Line_xml.DocumentElement
              Line_nodeList = Line_xml.Xql("//busyLampField") // Get all Speedial Info
              
              Var busyLampField As Integer = Line_xml.Xql("//busyLampField").Length // How many members?
              
              // Parse through XML to find "speeddials dirn" which could be empty but exists
              For xx As Integer = 0 to busyLampField - 1
                Line_node = Line_nodeList.Item(xx)
                Var Next_Child As XmlNode = Line_node.FirstChild
                Var Next_Child1 As XmlNode = Next_Child.FirstChild
                
                If xx = 0 Then
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, BLF_column) =  _
                  Next_Child1.Value
                Else
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, BLF_column) = _
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, BLF_column) + ", " + _
                  Next_Child1.Value
                End
              Next
            End
            
          Case "speeddials"
            // Check Attribute count becuase there could be 0 - No Speed Dial info
            If xChild.AttributeCount > 0 Then
              Line_xml.LoadXml(xChild.ToString)
              Line_root = Line_xml.DocumentElement
              Line_nodeList = Line_xml.Xql("//speeddial") // Get all Speedial Info
              
              Var SpeedDial_Members As Integer = Line_xml.Xql("//speeddial").Length // How many members?
              
              // Parse through XML to find "speeddials dirn" which could be empty but exists
              For xx As Integer = 0 to SpeedDial_Members - 1
                Line_node = Line_nodeList.Item(xx)
                Var Next_Child As XmlNode = Line_node.FirstChild
                Var Next_Child1 As XmlNode = Next_Child.FirstChild
                
                If xx = 0 Then
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, SPD_column) =  _
                  Next_Child1.Value
                  
                Else
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, SPD_column) = _
                  Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, SPD_column) + ", " + _
                  Next_Child1.Value
                End
              Next
            End
          End Select
          xChild = xChild.NextSibling
        Wend
      Next
      // Set the column widths
      SetColumnWidths(Printable_Phone_Info_LB)
      
    End
  End Select
End If

For me in particular the sections ‘lines’, ‘busyLampFields’, ‘speeddials’ are what I am wondering could be improved on. Basically I learned how to do this with the help of the debugger figuring out how to walk into the data and get the information I needed.

If this is how it is then that’s fine. Just looking to better understand and see if I can improve on how I can get needed data.

Thanks

Carl…

You do not need to keep “loading” line_xml. Instead you can just create the desired nodeList with a new .xql query… so instead of using “//someNodeName” you can use “child::” and other Xpath Axes to find children of a given node. See: XPath Axes

@Jim_Meyer - thanks. Here is what I came up with next

For Lines: Instead of this

For xx As Integer = 0 to Line_Members - 1
  Line_node = Line_nodeList.Item(xx)
  Var Next_Child As XmlNode = Line_node.FirstChild
  Var my_nextSib As XmlNode = Next_Child.NextSibling
  Var my_nextSib1 As XmlNode = my_nextSib.NextSibling
  Var my_nextSib2 As XmlNode = my_nextSib1.NextSibling
  Var Next_Child1 As XmlNode = my_nextSib2.FirstChild
  Var Next_Child2 As XmlNode = Next_Child1.FirstChild

  If xx = 0 Then
    Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = Next_Child2.Value
  Else
    Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = _
    Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) + ", " + _
    Next_Child2.Value
  End
Next

I came up with this

For xx As Integer = 0 to Line_Members - 1
  Var Line_Member1 As XmlNodeList = Line_xml.Xql("//line//dirn//pattern")
  Var Line_Member_Node As XmlNode = Line_Member1.Item(0).FirstChild
  
  If xx = 0 Then
    Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = Line_Member_Node.Value
  Else
    Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = _
    Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) + ", " + _
    Line_Member_Node.Value
  End
Next

That enables me to grab the specific value and allows me to grab multiple entries if they exist via the loop.

Heres a further update

Case "lines"
  // Parse through XML to find "line" which must have at least 1 member
  Try
    Line_xml.LoadXml(xChild.ToString)
            
    For xx As Integer = 0 to Line_xml.Xql("//line").Length  - 1   // Get all Phone 'Line' Info using Line_xml.Xql("//line")
        Var Line_Member_Node As XmlNode = Line_xml.Xql("//line//dirn//pattern").Item(xx).FirstChild 'Line_Member1.Item(0).FirstChild
      
      If xx = 0 Then
        Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = Line_Member_Node.Value
      Else
        Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = _
        Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) + ", " + _
        Line_Member_Node.Value
      End
    Next
    
  Catch e As XmlException
    MessageBox("XML error: " + e.Message)
    Return
  End Try

Here is how I would do it:

dim i as integer
dim x as new XmlDocument
dim xnl as XmlNodeList

dim s as string

x.LoadXml(SpecialFolder.Desktop.Child(“x.xml”)) // since I put a copy of your file on my desktop

// “try” just in case something goes wrong on the xql line… but should only happen if you have a syntax error in the parameter/xpath… if it does not find anything it just comes up with an empty nodeList.

try
xnl = x.Xql("//lines/line/dirn/pattern")
for i = 0 to xnl.Length - 1
try // “try” here in case the node does not have a FirstChild.Value
s = xnl.Item(i).FirstChild.Value
catch
s = “error”
end try
next
catch
MsgBox(“Bad xql”)
end try

Your “For xx As Integer = 0 to Line_xml.Xql(”//line").Length - 1" is forcing the xql to run for each “//line”… execute the xql once and then step through the nodelist.

I have used one xql to get directly down to each “pattern”… but I could have use multiple nested “xql” and “for” loops to work my way down to that level… which works well if you need values in any of the parent nodes… like or the “uuid” attribute in the node… but you can still get to those by using “parent”.

Hope this makes sense.

@Ed_Palmer - Thanks for getting me started

@Jim_Meyer - Thanks for your suggestions. The updated code piece works a bit faster which means when I request many phone devices this could add up to hours in time saved

Case "lines"
  // Parse through XML to find "line" which must have at least 1 member
  Var xnl as XmlNodeList
  Var s as string
  
  // “try” just in case something goes wrong on the xql line… 
  // but should only happen if you have a syntax error in the parameter/xpath… 
  // if it does not find anything it just comes up with an empty nodeList.
  Try
    // UCM v10.5 - members of lines
    // index, label, display, dirn(pattern), ringSetting, consecutiveRingSetting, ringSettingIdlePickupAlert, ringSettingActivePickupAlert, displayAscii
    // e164Mask, dialPlanWizardId, dialPlanWizardId, mwlPolicy, mwlPolicy, maxNumCalls, busyTrigger, 
    // callInfoDisplay(callerName-Boolean, callerNumber-Boolean, redirectedNumber-Boolean, dialedNumber-Boolean, 
    // recordingProfileName, monitoringCssName, recordingFlag, audibleMwi, speedDial, partitionUsage, associatedEndusers, missedCallLogging, recordingMediaSource
    // 2-24-2021 currently we are using dirn only
    // when using this as an upgrade to another phone model parse these into our Cisco UC Prodcutivity Tools© PDS®
    
    xnl = xml.Xql("//lines/line/dirn/pattern")
    For i = 0 To xnl.Length - 1
      Try // “try” here in case the node does not have a FirstChild.Value
        s = xnl.Item(i).FirstChild.Value
        If i = 0 Then
          Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = xnl.Item(i).FirstChild.Value
        Else
          Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) = _
          Printable_Phone_Info_LB.CellValueAt(Printable_Phone_Info_LB.LastRowIndex, LNE_column) + ", " + _
          xnl.Item(i).FirstChild.Value
        End
        
      Catch
        s = "error"
      End Try
    Next
    
  Catch e As XmlException
    MessageBox("XML error: " + e.Message)
    Return
  End Try
1 Like