How to read a XML self-closing tag in Excel VBA? - excel

I have a XML file that contains a tag that can either have text or can be self-closing when it has no text.
Case 1 (with text):
<Example>
<size>512</size>
</Example>
Case 2 (no text - self-closing):
<Example>
<size />
</Example>
I want to read this tag text in Excel VBA.
In Case 1, no problem, I do the following:
Set oXMLFile = CreateObject("Microsoft.XMLDOM")
oXMLFile.Load ("File.xml")
size = oXMLFile.SelectSingleNode("/Example/size/text()").NodeValue
But in Case 2, the SelectSingleNode function returns this error:
Run-time error '438':
Object doesn't support this property or method
How can I handle Case 2 so that it returns me an empty string? Is there a built-in VBA function to test if a tag is self-closing?

Option Explicit
Sub Test()
Dim oXMLFile As Object
Dim oNode As Object
Dim sSize As String
Set oXMLFile = CreateObject("MSXML2.DOMDocument.6.0")
oXMLFile.LoadXML "<Example><size>512</size></Example>"
Set oNode = oXMLFile.SelectSingleNode("/Example/size/text()")
If Not oNode Is Nothing Then sSize = oNode.NodeValue
End Sub

Related

Attribute Goes Into All Elements

I'm using VBA to create an XML for a software to read. The problem is when creating the elements, the root element needs an attribute but the attribute is asigned to all elements and I dont see the problem.
I have looked through the various properties and methods on MSDN but canĀ“t find what Im doing wrong
Private Xdoc As DOMDocument
Private Root As IXMLDOMElement
Private Parents As IXMLDOMElement
Private Att As IXMLDOMAttribute
Private Sub CreateRoot()
Page = "http://tempuri.org/SpecificationImportData.xsd"
Set Xdoc = CreateObject("MSXML2.DOMDocument")
Set Att = Xdoc.createAttribute("xmlns")
Set Root = Xdoc.createElement("Specification")
Set Parents = Xdoc.createElement("SpecificationRow") value
Xdoc.appendChild Root
Att.Value = Page
Root.setAttributeNode Att
End Sub
Sub AddChild(Ary() As String)
Dim I As Integer, Elem As IXMLDOMElement, Page As String
I = 0
For Each E In fDom()
Set Elem = Xdoc.createElement(E)
Elem.Text = Ary(I)
Parents.appendChild Elem
I = I + 1
Next
Root.appendChild Parents
End Sub
The Above code creates this:
<Specification xmlns="http://tempuri.org/SpecificationImportData.xsd">
<SpecificationRow xmlns="">
<Data>Values</Data>
</SpecificationRow>
</Specification>
But I need this:
<Specification xmlns="http://tempuri.org/SpecificationImportData.xsd">
<SpecificationRow>
<Data>Values</Data>
</SpecificationRow>
</Specification>
The first sub creates the elements and the second sub gets called form a sub that passes values from an array that AddChild reads. It then creates the XML.
I think you're confusing attributes with namespaces. The document createNode method allows you to create an Element (type=1) with a namespace.
Here's a example:
Private Sub CreateRoot()
Dim strNameSpace As String
strNameSpace = "http://tempuri.org/SpecificationImportData.xsd"
Dim xml As Object
Dim ndRoot As Object
Dim ndParent As Object
Dim ndChild As Object
Set xml = CreateObject("MSXML2.DOMDocument")
Set ndRoot = xml.createNode(1, "Specification", strNameSpace)
xml.appendChild ndRoot
Set ndParent = xml.createNode(1, "SpecificationRow", strNameSpace)
ndRoot.appendChild ndParent
Set ndChild = xml.createNode(1, "Data", strNameSpace)
ndParent.appendChild ndChild
ndChild.Text = "Values"
MsgBox xml.xml
End Sub
This outputs
<Specification xmlns="http://tempuri.org/SpecificationImportData.xsd">
<SpecificationRow>
<Data>Values</Data>
</SpecificationRow>
</SpecificationRow>

Using Xpath in Excel to read a child node's attribute

I am having problems getting the value of "Data Import" from the source_lvl2 child node. In Excel, I get a run-time error of 91, "Object variable or With block variable not set"
I can't see what I'm doing wrong - any advice?
VBA
Sub TestXML()
Dim XDoc As Object
Set XDoc = CreateObject("MSXML2.DOMDocument")
XDoc.async = False: XDoc.validateOnParse = False
XDoc.Load ("C:\171215-000438_1513346972.xml")
Set lists = XDoc.SelectNodes("/archive/primary_rnw_contact/source/source_lvl2")
Debug.Print lists(0).Attributes(0).Text
Set XDoc = Nothing
End Sub
XML
EXTRA CODE
<?xml version="1.1" encoding="UTF-8"?>
<archive product="RightNow" version="4.0" build="17.8.0.1.0.248" label="Archived Incident">
You could try the following (not tested):
Dim xmap As XmlMap
Dim k as Long
Dim oMyNewColumn As ListColumn
oMyList As ListObject
' Delete all previous XML maps
k = ThisWorkbook.XmlMaps.Count 'Detect all XML maps
For i = 1 To k
ThisWorkbook.XmlMaps(i).Delete
Next i
...
Set xmap = ThisWorkbook.XmlMaps.Add("some_file.xml")
...
Set oMyNewColumn = oMyList.ListColumns.Add
oMyList.ListColumns(3).XPath.SetValue xmap, "/archive/primary_rnw_contact/source/source_lvl2"

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.

Unable to extract value for xml tags

I am a new to vba and have a task to do at hand. I have written some unction but could not debug it properly. I have following string in xml format:
<!--?xml version=""1.0"" encoding=""UTF-8""?-->
<credentials>
<mid>P</mid>
<mpid>Q</mpid>
<accid>R</accid>
<accesskey>S</accesskey>
<secretkey>T</secretkey>
</credentials>
and am trying to extract value corresponding to tag mid, mpid, accid etc.
Here is the subroutine I wrote:
Private Sub ExtractMWSCredentials(Text As String
Set xmldoc = Nothing
DoEvents
Set xmldoc = Get_XML_DOMDocument_Object()
xmldoc.LoadXML (Trim(Text))
Set mId = xmldoc.getElementsByTagName("mid").Item(0).Text
Set mpId = xmldoc.getElementsByTagName("mpid").Item(0).Text
Set accid = xmldoc.getElementsByTagName("accid").Item(0).Text
Set accesskey = xmldoc.getElementsByTagName("accesskey").Item(0).Text
End Sub
Everything is coming out to be empty. Any help appreciated.
I can't even get that code to compile. You don't use the Set keyword to store a non-object value like Text. You could code, for instance,
Set omId = xmldoc.getElementsByTagName("mid").Item(0)
Debug.Print omId.Text
Also, mId is a reserved word so it can't be a variable name. Here's one way you could do it.
Public Sub Extract()
Dim xDoc As MSXML2.DOMDocument60
Dim sMid As String, sMpid As String
Dim sAccid As String, sAccessKey As String
Set xDoc = New MSXML2.DOMDocument60
xDoc.LoadXML "<!--?xml version=""1.0"" encoding=""UTF-8""?-->" & _
"<credentials><mid>P</mid><mpid>Q</mpid><accid>R</accid>" & _
"<accesskey>S</accesskey><secretkey>T</secretkey></credentials>"
sMid = xDoc.getElementsByTagName("mid").Item(0).Text
sMpid = xDoc.getElementsByTagName("mpid").Item(0).Text
sAccid = xDoc.getElementsByTagName("accid").Item(0).Text
sAccessKey = xDoc.getElementsByTagName("accesskey").Item(0).Text
Debug.Print sMid, sMpid, sAccid, sAccessKey
End Sub
I don't use Set because I'm not storing the Item() in a variable, I'm storing the Item().Text in a variable.

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