Import XML data using Excel VBA - excel

I'm trying to import specific data from and XML file to an Excel sheet.
The code I'm using is this.
Dim oXMLFile As New DOMDocument60
Dim books As IXMLDOMNodeList
Dim results() As String
Dim i As Integer, booksUBound As Integer
Dim book As IXMLDOMNode, title As IXMLDOMNode, author As IXMLDOMNode
oXMLFile.Load "C:\example.xml"
Set books = oXMLFile.SelectNodes("/OUT_MESSAGE/LINES/OUT_MESSAGE_LINE")
booksUBound = books.Length - 1
ReDim results(booksUBound, 1)
For i = 0 To booksUBound
Set book = books(i)
Set title = book.SelectSingleNode("C00")
If Not title Is Nothing Then results(i, 0) = title.Text
Next
Dim wks As Worksheet
Set wks = ActiveSheet
wks.Range(wks.Cells(1, 1), wks.Cells(books.Length, 2)) = results
Which works with this XML
<?xml version="1.0" encoding="UTF-8"?>
<OUT_MESSAGE>
<LINES>
<OUT_MESSAGE_LINE>
<C00>1231231</C00>
<C01>3213213</C01>
</OUT_MESSAGE_LINE>
<OUT_MESSAGE_LINE>
<C00>1231234</C00>
<C01>3213214</C01>
</OUT_MESSAGE_LINE>
</LINES>
</OUT_MESSAGE>
My problem is that my XML file looks like this.
<?xml version="1.0" encoding="UTF-8"?>
<OUT_MESSAGE xmlns="urn:randomaddress-com:schema:test_out_message" xmlns:xsi="http://www.randomurl.com/123">
<LINES>
<OUT_MESSAGE_LINE>
<C00>1231231</C00>
<C01>3213213</C01>
</OUT_MESSAGE_LINE>
<OUT_MESSAGE_LINE>
<C00>1231234</C00>
<C01>3213214</C01>
</OUT_MESSAGE_LINE>
</LINES>
</OUT_MESSAGE>
Which I originally thought I could simply get to work by replacing
Set books = oXMLFile.SelectNodes("/OUT_MESSAGE/LINES/OUT_MESSAGE_LINE")
With
Set books = oXMLFile.SelectNodes("/OUT_MESSAGE xmlns='urn:randomaddress-com:schema:test_out_message' xmlns:xsi='http://www.randomurl.com/123'/LINES/OUT_MESSAGE_LINE")
But that gives me a runtime error.
If anyone know what changes I have to do to the original code that would be much appreciated.

This worked for me:
Dim xDoc, nodes, oNode
Set xDoc = CreateObject("MSXML2.DOMDocument.6.0")
'Note: added an `x=` to the default namespace so we can reference it later
xDoc.setProperty "SelectionNamespaces", _
"xmlns:x='urn.randomaddress.com.schema.test_out_message'"
xDoc.LoadXML Sheet2.Range("A4").Value 'load XML from sheet
'use the "x" prefix we added above
Set nodes = xDoc.SelectNodes("/x:OUT_MESSAGE/x:LINES/x:OUT_MESSAGE_LINE")
Debug.Print nodes.Length ' = 1
For Each oNode In nodes
Debug.Print oNode.SelectSingleNode("x:C00").nodeTypedValue
Debug.Print oNode.SelectSingleNode("x:OBJSTATE").nodeTypedValue
'etc
Next oNode
using this XML:
<?xml version="1.0"?>
<OUT_MESSAGE xmlns="urn.randomaddress.com.schema.test_out_message"
xmlns:xsi="http://www.randomurl.com/123">
<LINES>
<OUT_MESSAGE_LINE>
<C00>321312</C00>
<C01>12312312</C01>
<OBJSTATE>Posted</OBJSTATE>
<OBJEVENTS>Accept^Reject^</OBJEVENTS>
<STATE>Posted</STATE>
</OUT_MESSAGE_LINE>
</LINES>
</OUT_MESSAGE>

Related

XML and XPath handling in VBA

I'm trying to parse a XML into a spreadsheet using VBA, and for some reason I can't to the node that I want using XPath, here how my XML looks like:
<?xml version="1.0" encoding="UTF-8"?>
<cteProc xmlns="http://www.somesite.com" versao="3.00">
<CTe xmlns="http://www.somesite.com">
<infCte Id="an id" versao="3.00">
<ide>
<cUF>23</cUF>
<cCT>00000557</cCT>
<CFOP>6932</CFOP>
<natOp>some text </natOp>
<mod>57</mod>
</ide>
<compl>
<xObs>TEXT</xObs>
</compl>
</infCte>
</CTe>
</cteProc>
I'm trying to get at least to the ide node, so I can loop over the rest and get the information I want.
My code looks like this:
Public Sub parseXml()
Dim oXMLFile As MSXML2.DOMDocument60
Dim nodes As MSXML2.IXMLDOMNodeList
path2 = "C:\Users\me\Desktop\adoc.xml"
Set oXMLFile = New MSXML2.DOMDocument60
oXMLFile.Load (path2)
Set nodes = oXMLFile.DocumentElement.SelectNodes("/CTe")
So I tried to print the length of the nodes, I get this:
debug.print nodes.length
> 0
if I loop over like this:
Public Sub parseXml()
Dim oXMLFile As MSXML2.DOMDocument60
Dim nodes As MSXML2.IXMLDOMNodeList
Dim node As MSXML2.IXMLDOMNode
path2 = "C:\Users\me\Desktop\adoc.xml"
Set oXMLFile = New MSXML2.DOMDocument60
oXMLFile.Load (path2)
Set nodes = oXMLFile.DocumentElement.ChildNodes
For Each node In nodes
Debug.Print node.BaseName
Next node
I get this:
> CTe
So, If I do a giant loop I can get the information I want, but I think there must be a simpler sulution for this.
Since your XML uses namespaces, XPath also needs to deal with namespaces.
The following works for me using your XML:
Public Sub parseXml()
Dim oXML As MSXML2.DOMDocument60
Dim oNodes As MSXML2.IXMLDOMNodeList
Dim oItem As MSXML2.IXMLDOMNode
Dim path2 As String
path2 = "P:\adoc.xml"
Set oXML = New MSXML2.DOMDocument60
oXML.Load path2
oXML.setProperty "SelectionLanguage", "XPath"
oXML.setProperty "SelectionNamespaces", "xmlns:ssc=""http://www.somesite.com"""
Set oNodes = oXML.DocumentElement.SelectNodes("ssc:CTe")
For Each oItem In oNodes
MsgBox oItem.nodeName
Next
End Sub
There using
oXMLFile.setProperty "SelectionNamespaces", "xmlns:ssc=""http://www.somesite.com"""
I define a prefix ssc for the namespace
http://www.somesite.com.
The scc is my own choice (somesite.com). This prefix is needed for the XPATH in selectNodes method to work properly.
If you don't want defining the namespace, you would must use the local-name() XPath function. For example:
Set oNodes = oXML.DocumentElement.SelectNodes("*[local-name() = 'CTe']")

Create new XML Element as a Parent Node of Existing Element

I would like to create a new XML element to make my existing XML node as a child node of this new element. The structure of my current XML file is:
<?xml version="1.0" encoding="utf-8"?>
<component>
<type name="A"></type>
<type name="B"></type>
</component>
My idea is to create new element "masterType" and make it as a parent node of existing "type" element.
<?xml version="1.0" encoding="utf-8"?>
<component>
<masterType>
<type name="A"></type>
<type name="B"></type>
</masterType>
</component>
My question is, how can I make this new element as a parent node of my existing xml node? What happens if I used insertBefore(), the "masterType" already ends before the element "type".
<?xml version="1.0" encoding="utf-8"?>
<component>
<masterType>
</masterType>
<type name="A"></type>
<type name="B"></type>
</component>
Here's my code
Dim fileName As String
fileName = ActiveSheet.OLEObjects("TextBox1").Object.Text
XMLFileName = fileName
Dim Found As Boolean
Dim docXMLDOM As DOMDocument
Dim nodeType As IXMLDOMNodeList
Dim nodElement As IXMLDOMElement
Dim nodNewElement As IXMLDOMElement
Dim nodReference As IXMLDOMElement
Set docXMLDOM = New DOMDocument
docXMLDOM.Load XMLFileName
Set nodeType = docXMLDOM.getElementsByTagName("type")
For Each nodElement In nodeType
If nodElement.Attributes.getNamedItem("name").Text = "A" Then
Set nodReference = nodElement
Set nodNewElement = docXMLDOM.createElement("masterType")
nodElement.ParentNode.InsertBefore nodNewElement, nodElement
Exit For
End If
Next
docXMLDOM.Save XMLFileName
Simplified example:
Sub AddParentNode()
Dim docXMLDOM As MSXML2.DOMDocument60
Dim els As IXMLDOMNodeList
Dim masterEl As IXMLDOMElement
Dim el As IXMLDOMElement
Set docXMLDOM = New MSXML2.DOMDocument60
docXMLDOM.LoadXML Range("A1").Value 'for testing
Debug.Print "*** Before ***"
Debug.Print docXMLDOM.XML
Set els = docXMLDOM.getElementsByTagName("type")
If els.Length > 0 Then
'create the new parent element
Set masterEl = docXMLDOM.createElement("masterType")
els(1).ParentNode.appendChild masterEl
End If
'append each "type" element into the new parent node
For Each el In els
masterEl.appendChild el.CloneNode(True)
el.ParentNode.RemoveChild el
Next
Debug.Print "*** After ***"
Debug.Print docXMLDOM.XML
End Sub

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

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

XPath not working properly in Excel VBA with DOMDocument

We have XML data in the format below received from BACS Clearing:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by Oracle Reports version 10.1.2.3.0 -->
<?xml-stylesheet href="file:///o:/Dev/Development Projects 2014/DP Team Utilities/D-02294 DDI Voucher XML Conversion Tool/DDIVoucherStylesheet.xsl" type="text/xsl" ?>
<VocaDocument xmlns="http://www.voca.com/schemas/messaging" xmlns:msg="http://www.voca.com/schemas/messaging" xmlns:cmn="http://www.voca.com/schemas/common" xmlns:iso="http://www.voca.com/schemas/common/iso" xmlns:env="http://www.voca.com/schemas/envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.voca.com/schemas/messaging http://www.voca.com/schemas/messaging/Voca_AUDDIS_AdviceofDDI_v1.0.xsd">
<Data>
<Document type="AdviceOfDDIReport" created="2014-08-19T00:59:15" schemaVersion="1.0">
<StreamStart>
<Stream>
<AgencyBankParameter>234</AgencyBankParameter>
<BankName>LLOYDS BANK PLC</BankName>
<BankCode>9876</BankCode>
<AgencyBankName>BANK OF CYPRUS UK LTD</AgencyBankName>
<AgencyBankCode>5432</AgencyBankCode>
<StreamCode>01</StreamCode>
<VoucherSortCode>SC998877</VoucherSortCode>
<VoucherAccountNumber>12348765</VoucherAccountNumber>
</Stream>
</StreamStart>
<DDIVouchers>
<Voucher>
<TransactionCode> NEW</TransactionCode>
<OriginatorIdentification><ServiceUserName>A SERVICE NAME </ServiceUserName><ServiceUserNumber>223344</ServiceUserNumber></OriginatorIdentification>
<PayingBankAccount><BankName>A SMALL BANK UK LTD</BankName><AccountName>AN INDIVIDUAL </AccountName><AccountNumber>77553311</AccountNumber><UkSortCode>SC776655</UkSortCode></PayingBankAccount>
<ReferenceNumber>BACS001122 </ReferenceNumber>
<ContactDetails><PhoneNumber>021 223344</PhoneNumber><FaxNumber> </FaxNumber><Address><cmn:AddresseeName>a name</cmn:AddresseeName><cmn:PostalName>a place</cmn:PostalName><cmn:AddressLine>an address</cmn:AddressLine><cmn:TownName>A Town</cmn:TownName><cmn:CountyIdentification> </cmn:CountyIdentification><cmn:CountryName>UNITED KINGDOM</cmn:CountryName><cmn:ZipCode>AA1 2BB</cmn:ZipCode></Address></ContactDetails>
<ProcessingDate>2014-08-19</ProcessingDate>
<BankAccount><FirstLastVoucherCode>FirstLast</FirstLastVoucherCode><AgencyBankCode>7890</AgencyBankCode><SortCode>SC223344</SortCode><AccountNumber>99886655</AccountNumber><TotalVouchers>1</TotalVouchers></BankAccount>
</Voucher>
<Voucher>
...
and when I load the xml into the XPathVisualizer tool it works fine with an XPath expression like this:
VocaDocument/Data/Document/DDIVouchers/Voucher
But when I use the same xpath in VBA in MS Excel to retrieve the values into a worksheet it is not working.
Here is the code I am using in MS Execl VBA:
Dim nodeList As IXMLDOMNodeList
Dim nodeRow As IXMLDOMNode
Dim nodeCell As IXMLDOMNode
Dim rowCount As Integer
Dim cellCount As Integer
Dim rowRange As Range
Dim cellRange As Range
Dim sheet As Worksheet
Dim dom As DOMDocument60
Dim xpathToExtractRow As String
xpathToExtractRow = "VocaDocument/Data/Document/DDIVouchers/Voucher"
' OTHER XPath examples
' xpathToExtractRow = "VocaDocument/Data/Document/StreamStart/Stream/BankName"
' xpathToExtractRow = "VocaDocument/Data/Document/DDIVouchers/Voucher/ContactDetails/Address/cmn:AddresseeName" ' NOTICE cmn namespace!
' xpathToExtractRow = "VocaDocument/Data/Document/DDIVouchers/Voucher/ProcessingDate
Set domIn = New DOMDocument60
domIn.setProperty "SelectionLanguage", "XPath"
domIn.load (Application.GetOpenFilename("XML Files (*.xml), *.xml", , "Please select the xml file"))
Set sheet = ActiveSheet
Set nodeList = domIn.DocumentElement.SelectNodes(xpathToExtractRow)
Set nodeRow = domIn.DocumentElement.SelectSingleNode(xpathToExtractRow) '"/*/Data//StreamStart/Stream/*").nodeName
rowCount = 0
Workbooks.Add
For Each nodeRow In nodeList
rowCount = rowCount + 1
cellCount = 0
For Each nodeCell In nodeRow.ChildNodes
cellCount = cellCount + 1
Set cellRange = sheet.Cells(rowCount, cellCount)
cellRange.Value = nodeCell.Text
Next nodeCell
Next nodeRow
End Sub
so what am I missing, to I need to add namespaces to the DOM Object or something? And if so, whould I add all the namesspaces using xmlDoc.setProperty("SelectionNamespaces", ?
thanks
You need to register the default namespace :
xmlDoc.setProperty "SelectionNamespaces", "xmlns:ns='http://www.voca.com/schemas/messaging'"
Then you need to use the registered namespace prefix at the beginning of all nodes in the scope where default namespace declared :
ns:VocaDocument/ns:Data/ns:Document/ns:DDIVouchers/ns:Voucher
That's because descendant nodes inherit default namespace from ancestor automatically, unless a different default namespace declared at the descendant level, or a prefix that point to different namespace used.

Resources