VBA XML
XML, or eXtensible Markup Language, is used to structure, serialize, and transmit data between applications. The MSXML2 library is used to work with XML in VBA. Functions in the MSXML2 library often use the XPath language to navigate an XML document.
Validate And Load XML
XSD, or XML Schema Definition, files are used to define the structure and rules of an XML document. The MSXML2 library can be used to validate XML files against XSD schemas. When creating an XSD document, be sure to use a validation tool to validate the XML with the XSD and make sure the XML and XSD are both well-formed before relying on VBA code for validation. XML files can be loaded from the user's local machine or from a website.
Option Explicit
'modXML - Requires a reference to "Microsoft XML, v6.0
Public Function ValidateXML(XMLPath$, XSDPath$, XSDNamespace$) As Boolean
Dim XMLDoc As MSXML2.DOMDocument60
Dim XSDDoc As MSXML2.DOMDocument60
Dim SCache As MSXML2.XMLSchemaCache60
Dim XMLErr As MSXML2.IXMLDOMParseError
Set XMLDoc = New MSXML2.DOMDocument60
XMLDoc.async = False
XMLDoc.Load XMLPath
Set XSDDoc = New MSXML2.DOMDocument60
XSDDoc.async = False
XSDDoc.resolveExternals = True
XSDDoc.Load XSDPath
Set SCache = New MSXML2.XMLSchemaCache60
SCache.Add XSDNamespace, XSDDoc
Set XMLDoc.Schemas = SCache
Set XMLErr = XMLDoc.Validate()
ValidateXML = (XMLErr.ErrorCode = 0)
If Not ValidateXML Then
PrintXMLParseErrorInfo XMLErr
End If
End Function
Public Function LoadXML(XMLPath$, XSDPath$, XSDNamespace$) As MSXML2.DOMDocument60
Dim XMLDoc As MSXML2.DOMDocument60
Dim XSDDoc As MSXML2.DOMDocument60
Dim SCache As MSXML2.XMLSchemaCache60
Dim XMLErr As MSXML2.IXMLDOMParseError
Set XSDDoc = New MSXML2.DOMDocument60
XSDDoc.async = False
XSDDoc.resolveExternals = True
XSDDoc.Load XSDPath
Set SCache = New MSXML2.XMLSchemaCache60
SCache.Add XSDNamespace, XSDDoc
Set XMLDoc = New MSXML2.DOMDocument60
XMLDoc.async = False
XMLDoc.resolveExternals = True
XMLDoc.validateOnParse = True
Set XMLDoc.Schemas = SCache
If XMLDoc.Load(XMLPath) Then
Debug.Print "XML Loaded"
Else
Debug.Print "XML Not Loaded"
End If
If XMLDoc.parseError.ErrorCode <> 0 Then
Debug.Print XMLDoc.parseError.ErrorCode
Debug.Print XMLDoc.parseError.reason
Err.Raise _
Number:=1004, _
Description:=XMLDoc.parseError.ErrorCode & vbCrLf & _
XMLDoc.parseError.reason
End If
Set LoadXML = XMLDoc
End Function
Public Sub PrintXMLParseErrorInfo(XMLErr As MSXML2.IXMLDOMParseError)
With XMLErr
Debug.Print "ErrorCode: " & .ErrorCode
Debug.Print "reason: " & .reason
Debug.Print "URL: " & .URL
Debug.Print "srcText: " & .srcText
Debug.Print "filepos: " & .filepos
Debug.Print "linepos: " & .linepos
Debug.Print "Line: " & .Line
End With
End Sub
Local XML File
To validate and load a local XML file, supply a file path to the load method for the MSXML2.DOMDocument objects associated with the XSD and XML documents.
Option Explicit
Public Sub Example()
Dim XMLPath$
XMLPath = "C:\test.xml"
Dim XSDPath$
XSDPath = "C:\test.xsd"
Dim XSDNamespace$
XSDNamespace = "https://www.example.org/"
If Not ValidateXML(XMLPath, XSDPath, XSDNamespace) Then
MsgBox "Validation Failed.", vbCritical
Exit Sub
End If
Dim XMLDoc As MSXML2.DOMDocument60
Set XMLDoc = LoadXML(XMLPath, XSDPath, XSDNamespace)
Dim Egs As MSXML2.IXMLDOMNodeList
Set Egs = XMLDoc.DocumentElement.SelectNodes(".//example")
Dim Eg As MSXML2.IXMLDOMNode
For Each Eg In Egs
Debug.Print Eg.FirstChild.Text
Next Eg
End Sub
"C:\test.xsd"
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="https://www.example.org/"
xmlns:eg="https://www.example.org/">
<xs:element name="examples">
<xs:complexType>
<xs:sequence>
<xs:element name="example" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="string" type="xs:string"/>
<xs:element name="integer" type="xs:integer"/>
<xs:element name="decimal" type="xs:decimal"/>
</xs:sequence>
<xs:attribute name="exampleindex" type="xs:integer" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
"C:\test.xml"
<?xml version="1.0" encoding="UTF-8"?>
<eg:examples xmlns:eg="https://www.example.org/">
<example exampleindex="1">
<string>"Hello, World!"</string>
<integer>100</integer>
<decimal>3.5</decimal>
</example>
</eg:examples>
XML from Website
To get XML data from a website supply a Web URL to the Load method of a MSXML2.DOMDocument object. The XSD for the XML data may be available on the website at another URL or may be available for download. The example below uses the U.S. Department of the Treasury's website to get the Daily Treasury Yield Curve Rates. The XSD files are available for download on the site. Be sure to download the XSD files for the correct filters given the drop-down settings on the site.
Option Explicit
Public Sub Example()
Dim XMLPath$
XMLPath = "https://data.treasury.gov/feed.svc/DailyTreasuryYieldCurveRateData" & _
"?$filter=year(NEW_DATE)%20eq%202021"
Dim XSDPath$
'Download XSD files from website for correct filter
XSDPath = "C:\Daily Treasury Yield Curve\DailyTreasuryYieldCurveRateData.xsd"
Dim XSDNamespace$
XSDNamespace = "http://www.w3.org/2005/Atom"
If Not ValidateXML(XMLPath, XSDPath, XSDNamespace) Then
MsgBox "Validation Failed.", vbCritical
Exit Sub
End If
Dim XMLDoc As MSXML2.DOMDocument60
Set XMLDoc = LoadXML(XMLPath, XSDPath, XSDNamespace)
XMLDoc.SetProperty "SelectionNamespaces", "xmlns:dummy=""http://www.w3.org/2005/Atom"""
Dim Entries As MSXML2.IXMLDOMNodeList
Set Entries = XMLDoc.DocumentElement.SelectNodes(".//dummy:entry")
Dim Entry As MSXML2.IXMLDOMNode
For Each Entry In Entries
Debug.Print Entry.LastChild.Text
Next Entry
End Sub
v3.0 Versus v6.0
MSXML2.DOMDocument60 may require that the xml namespace be used for the SelectNodes method. When tags are in the default namespace they may not have an identifier and a dummy namespace identifier will need to be provided.
Public Sub GetDailyTreasuryYieldCurveRateData()
Dim XMLDoc As Object 'MSXML2.DOMDocument30
Dim Entries As Object 'MSXML2.IXMLDOMNodeList
Dim Entry As Object 'MSXML2.IXMLDOMNode
Dim DataNode As Object 'MSXML2.IXMLDOMNode
Dim URL As String
Set XMLDoc = CreateObject("MSXML2.DOMDocument.3.0")
XMLDoc.async = False
URL = _
"https://data.treasury.gov/feed.svc/DailyTreasuryYieldCurveRateData" & _
"?$filter=year(NEW_DATE)%20eq%202020"
If Not XMLDoc.Load(URL) Then
MsgBox "XML not loaded.", vbOKOnly + vbCritical
Exit Sub
End If
Set Entries = XMLDoc.DocumentElement.SelectNodes(".//entry")
'Set Entries = XMLDoc.getElementsByTagName("entry") 'Same as above
For Each Entry In Entries
Debug.Print Entry.LastChild.Text
Next Entry
Set Entries = Nothing
Set XMLDoc = Nothing
End Sub
Public Sub GetDailyTreasuryYieldCurveRateData()
Dim XMLDoc As Object 'MSXML2.DOMDocument60
Dim Entries As Object 'MSXML2.IXMLDOMNodeList
Dim Entry As Object 'MSXML2.IXMLDOMNode
Dim DataNode As Object 'MSXML2.IXMLDOMNode
Dim URL As String
Set XMLDoc = CreateObject("MSXML2.DOMDocument.6.0")
XMLDoc.async = False
URL = _
"https://data.treasury.gov/feed.svc/DailyTreasuryYieldCurveRateData" & _
"?$filter=year(NEW_DATE)%20eq%202020"
If Not XMLDoc.Load(URL) Then
MsgBox "XML Not Loaded"
Exit Sub
End If
XMLDoc.SetProperty "SelectionNamespaces", "xmlns:dummy=""http://www.w3.org/2005/Atom"""
Set Entries = XMLDoc.DocumentElement.SelectNodes(".//dummy:entry")
'Set Entries = XMLDoc.DocumentElement.SelectNodes(".//entry") 'DOES NOT WORK
For Each Entry In Entries
Debug.Print Entry.LastChild.Text
Next Entry
Set Entries = Nothing
Set XMLDoc = Nothing
End Sub