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

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"

Related

How can I index the right node on a XML file?

So I was trying to read the nodes and write their fields on an Excel workbook, however I'm having troubles to index a specific field that I want. The XML structure is like:
<root>
<data name="Admin" xml:space="preserve">
<value>Administrador</value>
</data>
</root>
Now the problem is that I had no problem to get the text inside the node but I also wanted to get the text inside the "" right after data name. The VB code is as it follows:
Dim XDoc As Object
Dim myNodes As IXMLDOMNodeList, myChildNodes As IXMLDOMNodeList
Dim myElement As IXMLDOMElement
Dim myNode As IXMLDOMNode, myChildNode As IXMLDOMNode
Dim nNode As Integer
Dim nChildNode As Integer
Set XDoc = CreateObject("MSXML2.DOMDocument")
XDoc.async = False: XDoc.validateOnParse = False
XDoc.load (vFileName)
Set myNodes = XDoc.SelectNodes("//data/value")
If myNodes.Length > 0 Then
For nNode = 0 To myNodes.Length - 1
Set myNode = myNodes(nNode)
Set myChildNodes = myNode.ChildNodes ' Get the children of the first node.
For nChildNode = 0 To myChildNodes.Length - 1
vNode = myChildNodes(nChildNode).Text
vRange2 = "A" + Trim(Str(vLineTAG))
Range(vRange2).Value = vNode
Next nChildNode
Next nNode
Else
'Stuff and all
End If
So here I'm referencing "value" and vNode is getting the Administrador string inside the node above, but when I reference only data, it returns an empty string, the range which receives it is blanc, and the next child node returns what is inside the value node as expected. Don't know what am I missing here...
This should do the work
Dim ElementAttribute As IXMLDOMAttribute
Set ElementAttribute = myNode.Attributes.getNamedItem("name")
Debug.Print ElementAttribute.Text

How to handle and get values from dynamic tags from XML file in VBA macro

I have an XML file with 3 levels. Some of the tags are dynamic.(Please check below XML file)
I have to validate whether the tag "price" is present in all nodes or not and also need to get value of "price tag". I also need to fetch a value of every node present in XML file.
I tried validating whether every node in XML file is present or not but getting an error
VBA code snippet
Function fnReadXMLByTags2()
Dim mainWorkBook As Workbook
Set mainWorkBook = ActiveWorkbook
Dim obj As Object
Set obj = CreateObject("MSXML2.DOMDocument")
obj.async = False
XMLFileName = "C:\Prakash\Demo.xml"
obj.Load (XMLFileName)
Set authorNodes = obj.SelectNodes("//Books/book/author/text()")
Set titleNodes = obj.SelectNodes("//Books/book/title/text()")
Set genreNodes = obj.SelectNodes("//Books/book/genre/text()")
Set priceNodes = obj.SelectNodes("//Books/book/price/text()")
Set publish_dateNodes = obj.SelectNodes("//Books/book/publish_date/text()")
Set languageNodes = obj.SelectNodes("//Books/book/language/text()")
For i = 0 To (authorNodes.Length - 1)
If Not publish_dateNodes Is Nothing Then
Set publish_dateNodesValue = obj.SelectNodes("//Books/book/publish_date/text()")
publish_dateObj = publish_dateNodesValue(i).NodeValue
mainWorkBook.Sheets("Sheet3").Range("E" & i + 2).Value = publish_dateObj
Else
mainWorkBook.Sheets("Sheet3").Range("E" & i + 2).Value = "Blank"
End If
Next
End Function
Below error Message for line …
publish_dateObj = publish_dateNodesValue(i).NodeValue
Runtime error '91':
Object variable or With block variable not set
This is what I'm expecting in Excel file:
Excel sheet output
Below is the xml file :
<?xml version="1.0"?>
<Books>
<book id="1">
<author>ABC</author>
<title>Physics</title>
<genre>asd</genre>
<price>Rs.44</price>
<publish_date>20-10-2001</publish_date>
<description>Book1</description>
</book>
<book id="2">
<author>DEF</author>
<title>Chem</title>
<genre>XYZ</genre>
<publish_date>02-12-2016</publish_date>
<description>Book2</description>
</book>
<book id="3">
<author>GHI</author>
<title>Maths</title>
<genre>ABC</genre>
<price>Rs.500</price>
<language>English</language>
<description>Book3</description>
</book>
</Books>
It should be a procedure (Sub) not a function, because it does not return a value (make sure you know the difference).
The way you read the sub nodes made it impossible to keep the data consitent. You need to read all books first and then for each book the sub nodes:
Set CurrentNode = Books(iBook).SelectNodes("author/text()")(0)
The above reads reads the node author from book number iBook into CurrentNode and if it does not exist then CurrentNode will be Nothing. So you just need to check it and output the .NodeValue.
Note that I introduced an array NodesOutputList to be able to loop through the columns easily instead of repeating similar code over and over.
So you end up with something like:
Option Explicit
Public Sub fnReadXMLByTags2()
Dim mainWorkBook As Workbook
Set mainWorkBook = ThisWorkbook '<-- this is the workbook where this code is in ActiveWorkbook is the worbkook that has focus (is on top)
Dim wsOutput As Worksheet 'define output sheet
Set wsOutput = mainWorkBook.Worksheets("Sheet3")
Dim XMLFileName As String
XMLFileName = "D:\code\Demo.xml"
Dim obj As Object
Set obj = CreateObject("MSXML2.DOMDocument")
obj.async = False
obj.Load XMLFileName '<-- no parenthesis here!!!
Dim Books As Object 'get all books
Set Books = obj.SelectNodes("//Books/book")
Dim NodesOutputList() As Variant 'order in which the nodes are checked and output
NodesOutputList = Array("author/text()", "title/text()", "genre/text()", "price/text()", "publish_date/text()", "language/text()", "description/text()")
Dim iBook As Long
For iBook = 0 To Books.Length - 1 'loop through books
Dim iNode As Long
For iNode = LBound(NodesOutputList) To UBound(NodesOutputList) 'loop through nodes in the list
Dim CurrentNode As Object
Set CurrentNode = Books(iBook).SelectNodes(NodesOutputList(iNode))(0)
If Not CurrentNode Is Nothing Then
wsOutput.Cells(iBook + 2, iNode + 1).Value = CurrentNode.NodeValue
Else
wsOutput.Cells(iBook + 2, iNode + 1).Value = "blank"
End If
Next iNode
Next iBook
End Sub

Error-Is Nothing condition in case of object

I am trying to use a conditional code for Object such that if value/text is found in object (in my example it is JSONObject) then do something otherwise nothing. But when I run the code it is working only when found in object and shows "runtime error" when it is not found in object.
The code is:-
Private Sub CommandButton3_Click()
Dim jsonText As String
Dim FSO As New FileSystemObject
Dim JsonTS As TextStream
Dim jsonObject As Object('It is an object created )
myfile = Application.GetOpenFilename(FileFilter:="JSON file (*.json), *.json", Title:="Get File", MultiSelect:=True)
Set JsonTS = FSO.OpenTextFile(myfile, ForReading)
jsonText = JsonTS.ReadAll
JsonTS.Close
Set jsonObject = JsonConverter.ParseJson(jsonText)
' Condition that if in jsonObect, "b2b" is found then
If Not jsonObject("b2b") Is Nothing Then
For Each item In jsonObject("b2b") '("inv")
Sheet3.Cells(a, 2) = jsonObject("fp")
Sheet3.Cells(a, 1) = item("ctin")
End If
End Sub
I'd rather have JSON to test with but you could attempt to set jsonObject("b2b") into a new variable wrapped within an On Error Resume Next and then test that for If Not Is Nothing
Dim b2bObject As Object
Dim item As Variant '<<=== ? missing from your code
On Error Resume Next
Set b2bObject = jsonObject("b2b")
On Error GoTo 0
If Not b2bObject Is Nothing Then
For Each item In b2bObject
Sheet3.Cells(a, 2) = jsonObject("fp")
Sheet3.Cells(a, 1) = item("ctin")
Next
End If
If using in a loop you may wish to Set b2bObject = Nothing before End If as safeguard.

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.

Application-defined or object-defined error in Excel VBA

I'm getting said error in using VBA in Excel on the following code:
Private Sub XMLGen(mapRangeA, mapRangeB, ticketSize, mapping)
Dim fieldOneArr As Variant
Dim fieldTwoArr As Variant
Dim row As Long
Dim column As Long
Dim infoCol As Long
Dim endInfo As Long
Dim objDom As DOMDocument
Dim objNode As IXMLDOMNode
Dim objXMLRootelement As IXMLDOMElement
Dim objXMLelement As IXMLDOMElement
Dim objXMLattr As IXMLDOMAttribute
Set ws = Worksheets("StockData")
Dim wsName As String
Set objDom = New DOMDocument
If ticketSize = 8 Then
wsName = "A7Tickets"
ElseIf ticketSize = 16 Then
wsName = "A8Tickets"
Else
wsName = "A5Tickets"
End If
Set ps = Worksheets(wsName)
'create processing instruction
Set objNode = objDom.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'")
objDom.appendChild objNode
'create root element
Set objXMLRootelement = objDom.createElement("fields")
objDom.appendChild objXMLRootelement
'create Attribute to the Field Element and set value
Set objXMLattr = objDom.createAttribute("xmlns:xfdf")
objXMLattr.NodeValue = "http://ns.adobe.com/xfdf-transition/"
objXMLRootelement.setAttributeNode objXMLattr
infoCol = 1
fieldOneArr = Worksheets(mapping).range(mapRangeA)
fieldTwoArr = Worksheets(mapping).range(mapRangeB)
For row = 1 To UBound(fieldOneArr, 1)
For column = 1 To UBound(fieldOneArr, 2)
'create Heading element
Set objXMLelement = objDom.createElement(fieldOneArr(row, column))
objXMLRootelement.appendChild objXMLelement
'create Attribute to the Heading Element and set value
Set objXMLattr = objDom.createAttribute("xfdf:original")
objXMLattr.NodeValue = (fieldTwoArr(row, column))
objXMLelement.setAttributeNode objXMLattr
objXMLelement.Text = ps.Cells(row, infoCol)
infoCol = infoCol + 1
endInfo = endInfo + 1
If endInfo = 4 Then
infoCol = 1
End If
Next column
Next row
'save XML data to a file
If ticketSize = 2 Then
objDom.Save ("C:\ExportTestA5.xml")
MsgBox "A5 XML created"
ElseIf ticketSize = 8 Then
objDom.Save ("C:\ExportTestA7.xml")
MsgBox "A7 XML created"
Else
objDom.Save ("C:\ExportTestA8.xml")
MsgBox "A8 XML created"
End If
End Sub
When I hit debug it points to this line:
fieldOneArr = Worksheets(mapping).range(mapRangeA)
I know that .Range is supposed to be upper case but it keeps on setting it to lower case automatically whenever I correct it.
This code is meant to create an XML file and then write the details from the chosen worksheet (based on the ticketSize variable) into the correct XML fields. Hence I have a mapping worksheet from which I write the field and attribute names, and then write in the info from the correct ticket size worksheet into the text property of the element.
You should define the types of your function parameters, in this case mapRangeA As String. Office object methods and properties are often not very helpful with their error messages, so it's better to have a type mismatch error if you have a problem with a parameter.

Resources