Parse SOAP XML Response in VBA - excel

We are trying to parse the below response and get the value "123456" from "result" tag using VBA Code but we are not getting anything:
Response Received:
------=_Part_119884_965967558.1620391101235
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
Content-Transfer-Encoding: 8bit
Content-ID: <e119e24d-e0b6-4ceb-a00a-dd094ef91ef7>
<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<env:Header>
<wsa:Action>http://xxxx</wsa:Action>
<wsa:MessageID>urn:uuid:xxxx</wsa:MessageID>
</env:Header>
<env:Body>
<ns0:uploadFileResponse
xmlns:ns0="http://xxxx">
<result
xmlns="http://xxxx">123456
</result>
</ns0:uploadFileResponse>
</env:Body>
</env:Envelope>
------=_Part_119884_965967558.1620391101235--
VBA Code:
Dim xmldoc As Object
Dim xmlnode As Object
Set xmldoc = CreateObject("Microsoft.XMLDOM")
xmldoc.SetProperty "SelectionLanguage", "XPath"
xmldoc.async = False
xmldoc.LoadXML .ResponseText
For Each xmlnode In xmldoc.SelectNodes("//*[contains(name(),'result')]")
Debug.Print "Document ID: "; xmlnode.text
Next
Please help

You need to clean-up the response.
Dim xmldoc As Object
Dim xmlnode As Object
Set xmldoc = CreateObject("Microsoft.XMLDOM")
xmldoc.SetProperty "SelectionLanguage", "XPath"
xmldoc.async = False
Dim xml As String
xml = .ResponseText
xml = Mid(xml, InStr(1, xml, "<?xml "))
xml = Left(xml, InStr(1, xml, "Envelope>") + Len("Envelope"))
If xmldoc.LoadXML(xml) = True Then
For Each xmlnode In xmldoc.SelectNodes("//*[contains(name(),'result')]")
Debug.Print "Document ID: "; xmlnode.Text
Next
Else
MsgBox "XML failed to load", vbCritical
End If

Related

How can I parse values from a dynamic webpage using Excel VBA when XML/XPath doesn't seem to work?

I am attempting to scrape values from a collection of webpages using an XPath to parse what I want out of the XML. I grab the full XPath from the element using Chrome but then when I use in the code it doesnt seem to select the node I am looking for. Also when I execute the XPath statement in the console it also does not return the node. Some other element XPaths work in console but not in VBA. Am I missing something? My simple test XML works ok. My attempts to use namespace in the XPath were also not successful. Code below with an example of one of the webpages and one of the elements of interest:
Sub test()
testXML = "<test example='hello'>hello</test>"
Dim oXMLHTTP As Object
Dim sPageHTML As String
Dim sURL As String
Dim XmlResponse As String
Dim strXML As String
Dim xNode As MSXML2.IXMLDOMNode
Dim xmlElement As MSXML2.IXMLDOMElement
Dim XDoc As MSXML2.DOMDocument60
sURL = "https://www.bestplaces.net/crime/zip-code/alaska/anchorage/99510"
Set oXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oXMLHTTP.SetOption(2) = 13056 'Disable CA messages
oXMLHTTP.Open "GET", sURL, False
oXMLHTTP.send
XmlResponse = oXMLHTTP.responseText
'strXML = testXML
strXML = XmlResponse
Set XDoc = New MSXML2.DOMDocument60
'XDoc.setProperty "SelectionNamespaces", "xmlns:a='http://www.w3.org/1999/xhtml'"
'XDoc.setProperty "SelectionNamespaces", "xmlns:a='http://www.w3.org/2000/svg'"
'XDoc.setProperty "SelectionNamespaces", "xmlns:a='http://www.w3.org/1999/xlink'"
XDoc.LoadXML (strXML)
'Set xNode = XDoc.SelectSingleNode("/test")
Set xNode = XDoc.SelectSingleNode("/html/body/form/div[7]/div[2]/div[2]/div[3]/div/div/div/div/div/svg/g[6]/g[1]/text/tspan[2]")
If xNode Is Nothing Then
MsgBox "Nothing"
Else: MsgBox xNode.text
End If
End Sub
You are getting html back. A quick look at the page source shows that value is populated dynamically, but should be available by regex out of responseText; so your xpath wouldn't work even if converted to equivalent path for html parser.
Option Explicit
Public Sub GetValue()
Dim http As Object, s As String, re As Object
Set http = CreateObject("MSXML2.XMLHTTP")
Set re = CreateObject("VBScript.RegExp")
With http
.Open "GET", "https://www.bestplaces.net/crime/zip-code/alaska/anchorage/99510", False
.setRequestHeader "User-Agent", "Mozilla/5.0"
.send
s = .responseText
End With
With re
.Pattern = "data:\s?\[(.*?),"
Debug.Print .Execute(s)(0).SubMatches(0)
End With
End Sub
Regex explanation:

xpath query with backslashes return empty

I am trying to select a single MSXML2 node in excel using XPath predicates. I am able to select it just fine when I supply a string without backslashes. But as soon as I try with a file path string, the expression returns nothing.
Here is my XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Directory>
<Document>
<Path/>
<Status/>
<Notes/>
</Document>
<Document>
<Path>C:\Users\Ivelin\Desktop\Workspace\Requests\File.xlsm</Path>
<Status>Started</Status>
<Notes/></Document>
<Document>
<Path>TEST</Path>
<Status>Started</Status>
<Notes/>
</Document>
</Directory>
This works:
Dim Stat As IXMLDOMNode
Dim strPath
strPath = "/Directory/Document[Path='TEST']/Status/text()"
MsgBox (strPath)
Set Stat = XDoc.SelectSingleNode(strPath)
MsgBox (Stat.NodeValue)
This returns null:
Dim Stat As IXMLDOMNode
Dim strPath
strPath = "/Directory/Document[Path='C:\Users\Ivelin\Desktop\Workspace\Requests\File.xlsm']/Status/text()"
MsgBox (strPath)
Set Stat = XDoc.SelectSingleNode(strPath)
MsgBox (Stat.NodeValue)
I tried different suggestions, double backslashes etc. but no luck. Since I am interested in file names/paths, I don't really have other option, but to use backslashes.
Any pointers on how to solve this are welcome.
I see nothing wrong with your xpath. Perhaps the error lies elsewhere. I used the following loading your xml from file; no problem.
Option Explicit
Public Sub test()
Dim xmlDoc As Object, item As Object
Set xmlDoc = CreateObject("MSXML2.DOMDocument") 'New MSXML2.DOMDocument60
With xmlDoc
.validateOnParse = True
.setProperty "SelectionLanguage", "XPath"
.async = False
If Not .Load("C:\Users\User\Desktop\Test.xml") Then
Err.Raise .parseError.ErrorCode, , .parseError.reason
End If
End With
Dim path As String
path = "/Directory/Document[Path='C:\Users\Ivelin\Desktop\Workspace\Requests\File.xlsm']/Status/text()"
Set item = xmlDoc.SelectSingleNode(path)
Debug.Print item.Text
End Sub

Get the values of id2

<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<id xmlns="id.services/">
<ids1>
<response xmlns="">
<ids xmlns="ids">
<Id-info xmlns="" id0="123" id1="0" id2="2345" />
<Id-info xmlns="" id0="456" id1="1" id2="6789" />
</ids>
</response>
</ids1>
</id>
</s:Body>
</s:Envelope>
How can I get the values of id2 using vba excel?. This is the code that I have tried
Dim xmlDoc As DOMDocument30
Set xmlDoc = New DOMDocument30
xmlDoc.Load ("C:test.xml")
Dim id As String
id = xmlDoc.SelectSingleNode("//ids/Id-info").Attributes.getNamedItem("id2").Text
You will only access one value with that.
Try
Option Explicit
Public Sub test()
Dim xmlDoc As Object, items As Object, node As IXMLDOMElement
Set xmlDoc = CreateObject("MSXML2.DOMDocument") 'New MSXML2.DOMDocument60
With xmlDoc
.validateOnParse = True
.setProperty "SelectionLanguage", "XPath"
.async = False
If Not .Load("C:\Users\User\Desktop\Test.xml") Then
Err.Raise .parseError.ErrorCode, , .parseError.reason
End If
End With
Set items = xmlDoc.SelectNodes("//Id-info")
If Not items Is Nothing Then
For Each node In items
Debug.Print node.getAttribute("id2")
Next
End If
End Sub

Excel VBA - XML DomDocument return attribute values

I am working with the following XML response in Excel VBA.
<XXXXX docType="GetSegmentSpeed" copyright="Copyright XXXXX Inc." versionNumber="12.9" createdDate="2018-11-26T15:08:37Z" statusId="0" statusText="" responseId="06d3aad3-c3aa-40a5-9d2c-f1ac8f713729">
<SegmentSpeedResultSet coverage="255">
<SegmentSpeedResults timestamp="2018-11-26T15:08:36Z">
<Segment code="213423027" type="XDS" speed="53" average="34" reference="40" score="30" c-value="63" travelTimeMinutes="0.649" speedBucket="3"/>
<Segment code="213423023" type="XDS" speed="53" average="38" reference="41" score="30" c-value="58" travelTimeMinutes="0.603" speedBucket="3"/>
<Segment code="213423026" type="XDS" speed="52" average="34" reference="39" score="30" c-value="71" travelTimeMinutes="0.486" speedBucket="3"/>
<Segment code="213423050" type="XDS" speed="52" average="34" reference="39" score="30" c-value="71" travelTimeMinutes="0.48" speedBucket="3"/>
<Segment code="213423051" type="XDS" speed="52" average="35" reference="39" score="30" c-value="78" travelTimeMinutes="0.486" speedBucket="3"/>
</SegmentSpeedResults>
</SegmentSpeedResultSet>
</XXXXX>
I want to find the total of the travelTimeMinutes attributes of Segments.
To begin with, I thought I would try and get the value for the first segment. This is my code:
Sub SegSetTimes()
' Declare Private Variables
Dim SegString As String 'Segment set to be used for calculation
Dim hReq As New WinHttpRequest 'HttpRequest path
Dim strResp As String 'Response String
Dim xmlDoc As MSXML2.DOMDocument60 'DomDocument for parsing XML
' Import Segment Set
SegString = Join(WorksheetFunction.Transpose(Range("A2", Range("A2").End(xlDown)).Value), "|XDS,")
' Call for real-time segment information
hReq.Open "Get", "http://eu.api.XXXXX.com/Traffic/XXXXX.ashx?Action=GetSegmentSpeed" & "&token=" & AuthToken & "&Segments=" & SegString
hReq.Send
' Create string from response text
strResp = hReq.ResponseText
' Import response text into DomDocument for parsing within VBA
Set xmlDoc = New MSXML2.DOMDocument60
If Not xmlDoc.LoadXML(strResp) Then
MsgBox "Load Error"
End If
Dim n As IXMLDOMNodeList
Set n = xmlDoc.SelectNodes("//XXXXX/SegmentSpeedResultSet/SegmentSpeedResults")
Dim TT As Single
TT = n.Item(0).Attributes.getNamedItem("travelTimeMinutes")
End Sub
It fails with the following error:
Run-time error '91': Object variable or With block variable not set'
When stepping through in Locals, my IXMLDOMNodeList n looks correct. I just cannot see how to get at the values I want to.
Does anybody have any suggestions?
Reading in from a file I use an XPath to get the relevant nodes and then extract the value using getAttribute
Public Sub testing()
Dim xmlDoc As New MSXML2.DOMDocument60, items As Object, item As IXMLDOMElement, total As Double
Set xmlDoc = New MSXML2.DOMDocument60
xmlDoc.Load "C:\Users\User\Desktop\Test.xml"
Set items = xmlDoc.SelectNodes("//Segment[#travelTimeMinutes]")
For Each item In items
total = total + item.getAttribute("travelTimeMinutes")
Next
Debug.Print total
End Sub
Alternatively, consider running XSLT to retrieve the sum() across all nodes without looping:
XSLT (save as .xsl file, a special .xml file to be referenced in VBA)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/XXXXX">
<result>
<xsl:value-of select="format-number(sum(descendant::Segment/#travelTimeMinutes), '#.###')"/>
</result>
</xsl:template>
</xsl:stylesheet>
XSLT Demo
VBA
Dim xmlDoc As New MSXML2.DOMDocument60, items As Object, item As IXMLDOMElement, total As Double
' NEW REFERENCES
Dim xslDoc As New MSXML2.DOMDocument60, newDoc As New MSXML2.DOMDocument60
' RETRIEVE WEB REQUEST
...same code as above...
' LOAD XML AND XSL FILES
xmlDoc.async = False
xml.LoadXML strResp
xslDoc.async = False
xslDoc.Load "C:\Path\to\XSLT\File.xsl"
' TRANSFORM XML
xmlDoc.transformNodeToObject xslDoc, newDoc
' OUTPUT RESULT (NO LOOPING)
Debug.Print newDoc.SelectSingleNode("/result").Text
' 2.704

How to read XML attributes using VBA to Excel?

Here is my code..
<?xml version="1.0" ?>
<DTS:Executable xmlns:DTS="www.microsoft.com/abc" DTS:ExecutableType="xyz">
<DTS:Property DTS:Name="PackageFormatVersion">3</DTS:Property>
<DTS:Property DTS:Name="VersionComments" />
<DTS:Property DTS:Name="CreatorName">FirstUser</DTS:Property>
<DTS:Property DTS:Name="CreatorComputerName">MySystem</DTS:Property>
</DTS:Executable>
In this I am able to read Elements using "abc.baseName" and its value using "abc.Text".
It gives me result as
Property 3
Property
Property FirstUser
In this how can I read "PackageFormatVersion" as 3? i.e., I know some value is 3 but what that value is how could I know??
I mean I have to select which attribute I want to read.
Refer either to the element's .Text property or the .nodeTypeValue property :
Sub TestXML()
Dim xmlDoc As Object 'Or enable reference to Microsoft XML 6.0 and use: MSXML2.DOMDocument
Dim elements As Object
Dim el As Variant
Dim xml$
xml = "<?xml version=""1.0"" ?>"
xml = xml & "<DTS:Executable xmlns:DTS=""www.microsoft.com/abc"" DTS:ExecutableType=""xyz"">"
xml = xml & "<DTS:Property DTS:Name=""PackageFormatVersion"">3</DTS:Property>"
xml = xml & "<DTS:Property DTS:Name=""VersionComments"" />"
xml = xml & "<DTS:Property DTS:Name=""CreatorName"">FirstUser</DTS:Property>"
xml = xml & "<DTS:Property DTS:Name=""CreatorComputerName"">MySystem</DTS:Property>"
xml = xml & "</DTS:Executable>"
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
'## Use the LoadXML method to load a known XML string
xmlDoc.LoadXML xml
'## OR use the Load method to load xml string from a file location:
'xmlDoc.Load "C:\my_xml_filename.xml"
'## Get the elements matching the tag:
Set elements = xmlDoc.getElementsByTagName("DTS:Property")
'## Iterate over the elements and print their Text property
For Each el In elements
Debug.Print el.Text
'## Alternatively:
'Debug.Print el.nodeTypeValue
Next
End Sub
I know some value is 3 but what that value is how could I know??
You can review the objects in the Locals window, and examine their properties:
Here is an alternative, which seems clunkier to me than using the GetElementsByTagName but if you need to traverse the document, you could use something like this:
Sub TestXML2()
Dim xmlDoc As MSXML2.DOMDocument
Dim xmlNodes As MSXML2.IXMLDOMNodeList
Dim xNode As MSXML2.IXMLDOMNode
Dim cNode As MSXML2.IXMLDOMNode
Dim el As Variant
Dim xml$
xml = "<?xml version=""1.0"" ?>"
xml = xml & "<DTS:Executable xmlns:DTS=""www.microsoft.com/abc"" DTS:ExecutableType=""xyz"">"
xml = xml & "<DTS:Property DTS:Name=""PackageFormatVersion"">3</DTS:Property>"
xml = xml & "<DTS:Property DTS:Name=""VersionComments"" />"
xml = xml & "<DTS:Property DTS:Name=""CreatorName"">FirstUser</DTS:Property>"
xml = xml & "<DTS:Property DTS:Name=""CreatorComputerName"">MySystem</DTS:Property>"
xml = xml & "</DTS:Executable>"
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
'## Use the LoadXML method to load a known XML string
xmlDoc.LoadXML xml
'## OR use the Load method to load xml string from a file location:
'xmlDoc.Load "C:\my_xml_filename.xml"
'## Get the elements matching the tag:
Set xmlNodes = xmlDoc.ChildNodes
'## Iterate over the elements and print their Text property
For Each xNode In xmlDoc.ChildNodes
If xNode.NodeType = 1 Then ' only look at type=NODE_ELEMENT
For Each cNode In xNode.ChildNodes
Debug.Print cNode.nodeTypedValue
Debug.Print cNode.Text
Next
End If
Next
End Sub
Sub TestXML()
Set Reference to Microsoft XML 6.0
Dim Init As Integer
Dim xmlDoc As MSXML2.DOMDocument
Dim elements As Object
Dim el As Variant
Dim Prop As String
Dim NumberOfElements As Integer
Dim n As IXMLDOMNode
Init = 5
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
xmlDoc.Load ("C:\Users\Saashu\Testing.xml")
Set elements = xmlDoc.getElementsByTagName("DTS:Property")
Prop = xmlDoc.SelectSingleNode("//DTS:Property").Attributes.getNamedItem("DTS:Name").Text
NumberOfElements = xmlDoc.getElementsByTagName("DTS:Property").Length
For Each n In xmlDoc.SelectNodes("//DTS:Property")
Prop = n.Attributes.getNamedItem("DTS:Name").Text
Prop = Prop & " :: " & n.Text
ActiveSheet.Cells(Init, 9).Value = Prop
Init = Init + 1
Next
End Sub
This code still needs refinement as my requirement is to display only some of those attributes like CreatorName and CreatorComputerName,not all.
Thanks to David,for helping me in this issue.

Resources