I have a XML file that contain a lot of information.
So, I would like to create a macro in VBA Excel that allow me to filter the information based on the accountID (available form an Userform - ComboBox)
It's the first time I work with XML and userform.
I've tried to adjust multiple code that I found on the net, but i understand better this one so I would like to continue with something similar (if possible):
Private Sub ComboBox1_Click()
Dim wks As Worksheet
Set wks = Sheet2
' Load the XML document
Dim XDoc As Object, root As Object
Set XDoc = CreateObject("MSXML2.DOMDocument")
XDoc.async = False: XDoc.validateOnParse = False
XDoc.Load ("C:\Users\isabelle\Google Drive\IB API Integration\Flexqueries\FlexDay.xml")
Dim singleNode As Object
Set singleNode = XDoc.SelectSingleNode("//FlexQueryResponse/FlexStatements/FlexStatement[#accountId='U2396623']")
End Sub
The accountId 'U2396623' is only an example. This will correspond to the entry in the combobox.
My two problems are :
First, I don't know how to link the entry in the combobox to the code (based on the accountID)
Second, code above does not work. I think the path (XDoc.SelectSingleNode) is not the right one... So, I tied several combination and it never worked. Or maybe it's the output that does not work correctly (The output should be in the Sheet2 )
My XML file looks like it :
<FlexQueryResponse queryName="Sample_1" type="AF">
<FlexStatements count="10">
<FlexStatement accountId="" fromDate="2019-04-22" toDate="2019-05-21" period="Last30CalendarDays" whenGenerated="2019-05-22;13:49:30">
<AccountInformation accountId="" acctAlias="" currency="CAD" accountType="Advisor Client" dateOpened="2018-02-08" dateFunded="2018-03-01" dateClosed="" street="" street2="" city="" state="" country="" postalCode="" primaryEmail="" />
<ChangeInNAV accountId="" acctAlias="" startingValue="" endingValue="" realized="0" changeInUnrealized="0" depositsWithdrawals="0" twr="" mtm="" dividends="" changeInDividendAccruals="" interest="" changeInInterestAccruals="" advisorFees="" clientFees="0" otherFees="0" />
<CashReport>
<CashReportCurrency accountId="" acctAlias="" clientFees="0" commissions="" deposits="0" withdrawals="0" accountTransfers="0" dividends="" advisorFees="" otherFees="0" currency="BASE_SUMMARY" startingCash="" endingCash="" endingSettledCash="" />
/>
</CashReport>
<OpenPositions>
<OpenPosition accountId="" acctAlias="" symbol="" position="" costBasisPrice="" strike="" expiry="" putCall="" positionValue="" percentOfNAV="" fifoPnlUnrealized="" currency="CAD" fxRateToBase="1" assetCategory="STK" description="BOMBARDIER INC PFD SER 2" securityID="CA0977515075" cusip="" isin="CA0977515075" />
</OpenPositions>
<FxPositions>
<FxPosition accountId="" fxCurrency="CAD" quantity="" costPrice="" unrealizedPL="0" />
</FxPositions>
<OptionEAE>
<OptionEAE accountId="" acctAlias="" currency="USD" assetCategory="" symbol="" description="" securityID="" cusip="" isin="" listingExchange="" underlyingConid="" underlyingSymbol="FCAU" underlyingSecurityID="NL0010877643" underlyingListingExchange="NYSE" issuer="" strike="16" expiry="2019-05-03" putCall="C" date="2019-04-22" transactionType="Assignment" quantity="2" tradePrice="0.0000" markPrice="0.3500" realizedPnl="0.00" />
</OptionEAE>
<PendingExcercises />
<ClientFees />
<OpenDividendAccruals>
<OpenDividendAccrual accountId="" acctAlias="" currency="" assetCategory="" symbol="" description="" securityID="" cusip="" isin="" exDate="2019-05-03" payDate="2019-06-24" quantity="400" grossRate="0.23" grossAmount="92" netAmount="92" />
</OpenDividendAccruals>
</FlexStatement>
</FlexStatements>
</FlexQueryResponse>
If you have any question don't hesitate !
Thank you for your help
Consider several adjustments:
Use the AfterUpdate trigger event of combobox.
Private Sub ComboBox1_AfterUpdate()
...
End Sub
Simply concatenate the combobox value Me.ComboBox1 to XPath expression.
XDoc.SelectSingleNode("/FlexQueryResponse/FlexStatements/FlexStatement[#accountId='" & Me.ComboBox1 & "']")
Select the needed attribute value or loop through multiple by node for extraction to sheet.
Private Sub ComboBox1_AfterUpdate()
' Load the XML document
Dim XDoc As Object, root As Object, singleNode As Object
Dim i As Integer
Set XDoc = CreateObject("MSXML2.DOMDocument")
XDoc.async = False: XDoc.validateOnParse = False
XDoc.Load ("C:\Users\isabelle\Google Drive\IB API Integration\Flexqueries\FlexDay.xml")
' OUTPUT ONE ATTRIBUTE OF NODE
Set singleNode = XDoc.SelectSingleNode("/FlexQueryResponse/FlexStatements/FlexStatement[#accountId='" & Me.ComboBox1 & "']")
ThisWorkbook.Worksheets("Sheet1").Range("A1") = singleNode.Attributes.getNamedItem("accountId").Text
' OUTPUT ALL ATTRIBUTES OF NODE
For i = 0 To singleNode.Attributes.Length - 1
ThisWorkbook.Worksheets("Sheet1").Range("A" & i + 1) = singleNode.Attributes(i).Name
ThisWorkbook.Worksheets("Sheet1").Range("B" & i + 1) = singleNode.Attributes(i).Text
Next i
Set XDoc = Nothing
End Sub
Related
Given a Column A in Excel with multiple cells containing ISBN (book id) values, I want my VBA macro to loop through each of them and, for each one, parse an online XML file that is unique to that ISBN and put the corresponding book title in Column B.
For example, if A1 contains 1931498717, I should parse this XML and grab the title "Don't think of an elephant! : know your values and frame the debate : the essential guide for progressives" and put that in B1.
Here is a sample of the XML file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<classify xmlns="http://classify.oclc.org">
<response code="4"/>
<!--Classify is a product of OCLC Online Computer Library Center: http://classify.oclc.org-->
<workCount>2</workCount>
<start>0</start>
<maxRecs>25</maxRecs>
<orderBy>thold desc</orderBy>
<input type="isbn">1931498717</input>
<works>
<work author="Lakoff, George" editions="28" format="Book" holdings="1088" hyr="2014" itemtype="itemtype-book" lyr="2004" owi="796415685" schemes="DDC LCC" title="Don't think of an elephant! : know your values and frame the debate : the essential guide for progressives" wi="796415685"/>
<work author="Lakoff, George" editions="1" format="Musical score" holdings="1" hyr="2004" itemtype="itemtype-msscr" lyr="2004" owi="4735145535" schemes="DDC" title="Don't think of an elephant! : know your values and frame the debate : the essential guide for progressives" wi="4735145535"/>
</works>
</classify>
Notice there are two "work" elements. In this case, I am happy to just grab the title attribute from the first one. But even better would be to make sure it's the title of a book (format="Book") and not some other format.
Here is my macro code:
Sub ISBN()
Do
Dim xmlDoc As DOMDocument60
Set xmlDoc = New DOMDocument60
xmlDoc.async = False
xmlDoc.validateOnParse = False
r = CStr(ActiveCell.Value)
xmlDoc.Load ("http://classify.oclc.org/classify2/Classify?isbn=" + r + "&summary=true")
ActiveCell.Offset(0, 2).Value = xmlDoc.SelectSingleNode("/classify/works/work[1]").attributes.getNamedItem("title").text
ActiveCell.Offset(1, 0).Select
Loop Until IsEmpty(ActiveCell.Value)
End Sub
I get this error, "Run-time error 91 Object variable or With block variable not set," on the line that references "xmlDoc.SelectSingleNode("/classify/works/work[1]").attributes.getNamedItem("title").text"
I've tried numerous variations to try to isolate the title text but cannot get anything other than this error.
My Excel file is Microsoft Excel for Microsoft 365, on my laptop.
Help would be greatly appreciated. I am inexperienced in VBA programming and XML parsing and have been googling/reading on this for more time than I care to admit without making any progress. (There was a previous StackOverflow question on parsing ISBN XML files, but it was for a provider that no longer offers the XML files for free. That code inspired my code, but something was lost in the translation.)
Thanks tons for any help you can offer.
Your XML has a "default" namespace which applies to its contents, so when using xpath you need to create a dummy alias for that namespace and use it in your query.
See https://stackoverflow.com/a/72997440/478884
Eg:
Dim xmlDoc As DOMDocument60, col As Object, el As Object
Set xmlDoc = New DOMDocument60
xmlDoc.async = False
xmlDoc.validateOnParse = False
'set default namespace and alias "xx"
xmlDoc.SetProperty "SelectionNamespaces", _
"xmlns:xx='http://classify.oclc.org'"
xmlDoc.Load "http://classify.oclc.org/classify2/Classify?isbn=1931498717&summary=false"
Set col = xmlDoc.SelectNodes("/xx:classify/xx:works/xx:work")
'check each `work` for format and title
For Each el In col
Debug.Print "*****************"
Debug.Print el.Attributes.getNamedItem("format").Text
Debug.Print el.Attributes.getNamedItem("title").Text
Next el
I am creating quite complex XML files using a template, replacing special search strings with values which can be entered in an Excel sheet, and then storing the xml-file.
Dim strInpPath As String
Dim strOutpPath As String
Dim fso
Dim f
Dim oDomRd As Object, oNode As Object, i As Long, oAtt As Object, oGroup As Object, oDomWr As Object
Dim oTest As Object
strInpPath = ActiveWorkbook.ActiveSheet.Cells(3, 4).Value
strOutputPath = ActiveWorkbook.ActiveSheet.Cells(4, 4).Value
Set oDomRd = CreateObject("MSXML2.DOMDocument")
oDomRd.Load strInpPath
Set oDomWr = CreateObject("MSXML2.DOMDocument")
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(strOutputPath, 2, True)
Set oGroup = oDomRd.SelectNodes("/")
Set oNode = oGroup.NextNode
If Not (oNode Is Nothing) Then
strout = oNode.XML
strout = ScanTable("_S_AND_R_TABLE_1", strout)
oDomRd.LoadXML (strout)
Set oGroup = oDomRd.SelectNodes("/")
Set oNode = oGroup.NextNode
If oNode.HasChildNodes() Then
Set oLists = oNode.DocumentElement
Run RemoveOptionalEmptyTags(oLists)
End If
strout = oNode.XML
f.write (strout)
Else
strout = "001 error reading file"
End If
MsgBox strout
End Function
Some of the field values are not mandatory so they can be left empty. In this case, the first procedure (scantable) enters "##REMOVE##" as value. In the second step, I want to step through the entire DOMObject and remove the nodes having the value "##REMOVE##"
for this second step I created a procedure:
Public Function RemoveOptionalEmptyTags(ByRef oLists)
For Each listnode In oLists.ChildNodes
If listnode.HasChildNodes() Then
Run RemoveOptionalEmptyTags(listnode)
Else
lcBasename = listnode.ParentNode.BaseName
lcText = listnode.Text
If lcText = "##REMOVE##" Then
listnode.ParentNode.RemoveChild listnode
Exit For
End If
End If
Next listnode
End Function
This works pretty fine, the only problem is, that the node is not removed, it only is empty ():
<Cdtr>
<Nm>Name Creditor</Nm>
<PstlAdr>
<Ctry>DE</Ctry>
<AdrLine>Street</AdrLine>
<AdrLine/>
</PstlAdr>
</Cdtr>
now the question:
How can I completely REMOVE the node, so it would look like this (the second is gone):
<Cdtr>
<Nm>Name Creditor</Nm>
<PstlAdr>
<Ctry>DE</Ctry>
<AdrLine>Street</AdrLine>
</PstlAdr>
</Cdtr>
Basically the RemoveChild syntax is correct:
{NodeToDelete}.ParentNode.RemoveChild {NodeToDelete}
But let's repeat the xml structure and note that each text node (if existant) is regarded as a ChildNode of its parent (i.e. one hierarchy level deeper).
<Cdtr> <!-- 0 documentElement -->
<Nm>Name Creditor</Nm> <!-- 1 ChildNode of Nm = 'Name Creditor' -->
<PstlAdr> <!-- 1 listNode.ParentNode.ParentNode -->
<Ctry>DE</Ctry> <!-- 2 ChildNode of Ctry = 'DE' -->
<AdrLine>Street</AdrLine> <!-- 2 ChildNode of AdrLine[1] = 'Street' -->
<AdrLine> <!-- 2 listNode.ParentNode to be removed -->
<!-- NODETEXT ##REMOVE## --> <!-- 3 ChildNode of AdrLine[2] -->
</AdrLine>
</PstlAdr>
</Cdtr>
Diving down to bottom in xml hierarchy (assuming text values) via
listnode.ParentNode.RemoveChild listnode
you are deleting the textual ChildNode of AdrLine[2] (level 3) which is the string "##REMOVE##",
but not it container node AdrLine[2] (level 2). Therefore you are deleting only the dummy text.
Following your logic in function RemoveOptionalEmptyTags() as close as possible you'd have to code instead:
listNode.ParentNode.ParentNode.RemoveChild listNode.ParentNode
addressing PstlAdr (=level 1) executing a deletion of its ChildNode AdrLine[2] (i.e. at level 2) which
automatically includes deletion of the dummy string "##REMOVE" at level 3.
Related links:
XML Parse via VBA
Obtain atrribute names from xml using VBA
I am trying to prefill data from Excel to a local intranet website via VBA.
I've been able to prefill most data to the site, but I am struggling with this combodate box:
Please see HTML code:
<div class="form-group row " id="starttime_field">
<label class="col-form-label col-md-3" for="starttime">Start time</label>
<div class="col-md-9">
<input type="text" id="starttime" name="starttime" class="form-control" size="30" style="display: none;"><span class="combodate">
<select class="hour " style="width: auto;"><option value="0">00</option><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option><option value="5">05</option><option value="6">06</option><option value="7">07</option><option value="8">08</option><option value="9">09</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option></select> :
<select class="minute " style="width: auto;"><option value="0">00</option><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option><option value="5">05</option><option value="6">06</option><option value="7">07</option><option value="8">08</option><option value="9">09</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option><option value="24">24</option><option value="25">25</option><option value="26">26</option><option value="27">27</option><option value="28">28</option><option value="29">29</option><option value="30">30</option><option value="31">31</option><option value="32">32</option><option value="33">33</option><option value="34">34</option><option value="35">35</option><option value="36">36</option><option value="37">37</option><option value="38">38</option><option value="39">39</option><option value="40">40</option><option value="41">41</option><option value="42">42</option><option value="43">43</option><option value="44">44</option><option value="45">45</option><option value="46">46</option><option value="47">47</option><option value="48">48</option><option value="49">49</option><option value="50">50</option><option value="51">51</option><option value="52">52</option><option value="53">53</option><option value="54">54</option><option value="55">55</option><option value="56">56</option><option value="57">57</option><option value="58">58</option><option value="59">59</option></select></span><span class="combodate"><select class="hour " style="width: auto;"><option value="0">00</option><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option><option value="5">05</option><option value="6">06</option><option value="7">07</option><option value="8">08</option><option value="9">09</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option></select> : <select class="minute " style="width: auto;"><option value="0">00</option><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option><option value="5">05</option><option value="6">06</option><option value="7">07</option><option value="8">08</option><option value="9">09</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option><option value="24">24</option><option value="25">25</option><option value="26">26</option><option value="27">27</option><option value="28">28</option><option value="29">29</option><option value="30">30</option><option value="31">31</option><option value="32">32</option><option value="33">33</option><option value="34">34</option><option value="35">35</option><option value="36">36</option><option value="37">37</option><option value="38">38</option><option value="39">39</option><option value="40">40</option><option value="41">41</option><option value="42">42</option><option value="43">43</option><option value="44">44</option><option value="45">45</option><option value="46">46</option><option value="47">47</option><option value="48">48</option><option value="49">49</option><option value="50">50</option><option value="51">51</option><option value="52">52</option><option value="53">53</option><option value="54">54</option><option value="55">55</option><option value="56">56</option><option value="57">57</option><option value="58">58</option><option value="59">59</option></select></span>
This is what this section of the form looks like (highlighted in red):
ComboBox Form
Here is a snippet of the code I am using:
Sub Legends()
Dim IE As InternetExplorerMedium
Dim Environment As String
Dim webContent As String
Dim NOME_EMPRESA, CNPJ, CPF, COD_ACESSO As String
Dim Lookup_Range As Range
Dim SH
Dim confirmationBox As Integer
confirmationBox = MsgBox("Confirm Legendary Promotion changes to " & Worksheets("Legends").Range("E2") & "?", vbYesNoCancel, "Woolly Legends")
'Confirmation box data if yes is clicked
Select Case confirmationBox
Case vbYes
Dim eachIE
'Choosing the environment
If ThisWorkbook.Sheets("Legends").Range("E2").Value = ThisWorkbook.Sheets("Parameters").Range("A2") Then
AD = ThisWorkbook.Sheets("Parameters").Range("C2").Value
Environment = AD
End If
If ThisWorkbook.Sheets("Legends").Range("E2").Value = ThisWorkbook.Sheets("Parameters").Range("A3") Then
AC= ThisWorkbook.Sheets("Parameters").Range("C3").Value
Environment = AC
End If
If ThisWorkbook.Sheets("Legends").Range("E2").Value = ThisWorkbook.Sheets("Parameters").Range("A4") Then
AB = ThisWorkbook.Sheets("Parameters").Range("C4").Value
Environment = AB
End If
If ThisWorkbook.Sheets("Legends").Range("E2").Value = ThisWorkbook.Sheets("Parameters").Range("A5") Then
Testing = ThisWorkbook.Sheets("Parameters").Range("C5").Value
Environment = Testing
End If
Set IE = New SHDocVw.InternetExplorer
IE.navigate Environment
ShowWindow IE.hwnd, SW_MAXIMIZE
Do
Set SH = New Shell32.Shell
For Each eachIE In SH.Windows
If InStr(1, eachIE.LocationURL, Environment) Then
Set IE = eachIE
'ie.Visible = False 'This is here because in some environments, the new process defaults to Visible.
Exit Do
End If
Next eachIE
Loop
Set eachIE = Nothing
Set SH = Nothing
'Setting doc to allow for form manipulation via Excel
Set doc = IE.document
'Setting the Promotion Names for the Text Promotion
IE.document.all("promo_name").Value = ThisWorkbook.Sheets("Legends").Range("b3")
IE.document.all("race_name").Value = ThisWorkbook.Sheets("Legends").Range("b19")
'Setting start date and end date
IE.document.all("startdate").Value = Format$(ThisWorkbook.Sheets("Legends").Range("b7").Value, "yyyy-mm-dd")
IE.document.all("enddate").Value = Format$(ThisWorkbook.Sheets("Legends").Range("b8").Value, "yyyy-mm-dd")
'Setting the Runner Names
IE.document.all("runner_names_0").Value = ThisWorkbook.Sheets("Legends").Range("b20")
IE.document.all("runner_names_1").Value = ThisWorkbook.Sheets("Legends").Range("b21")
IE.document.all("runner_names_2").Value = ThisWorkbook.Sheets("Legends").Range("b22")
IE.document.all("runner_names_3").Value = ThisWorkbook.Sheets("Legends").Range("b23")
IE.document.all("runner_names_4").Value = ThisWorkbook.Sheets("Legends").Range("b24")
IE.document.all("runner_names_5").Value = ThisWorkbook.Sheets("Legends").Range("b25")
'Setting the dropdown box for the jurisdictions
If ThisWorkbook.Sheets("Legends").Range("B4").Value = "AAA" Or ThisWorkbook.Sheets("Legends").Range("B5").Value = "AAB" Or ThisWorkbook.Sheets("Legends").Range("B6").Value = "AAC" Then
IE.document.all("jurisdiction[]").Options(0).Selected = True
End If
If ThisWorkbook.Sheets("Legends").Range("B4").Value = "BBB" Or ThisWorkbook.Sheets("Legends").Range("B5").Value = "BBA" Or ThisWorkbook.Sheets("Legends").Range("B6").Value = "BBC" Then
IE.document.all("jurisdiction[]").Options(1).Selected = True
End If
If ThisWorkbook.Sheets("Legends").Range("B4").Value = "CCA" Or ThisWorkbook.Sheets("Legends").Range("B5").Value = "CCB" Or ThisWorkbook.Sheets("Legends").Range("B6").Value = "CCC" Then
IE.document.all("jurisdiction[]").Options(2).Selected = True
End If
End Select
End Sub
I am unsure how to pre-populate data from the spreadsheet into the hour and minute comboboxes, as the layout of this is a bit different from the other fields in the html.
Any help or guidance would be greatly appreciated.
Thanks!
The hour and minute boxes are select elements which means with InternetExplorer automation you have two main methods for selecting individual items (and a couple of alternates for tricky situations):
You can use selectedIndex on the select itself then specify index of option of interest in the list under that select. I think starts at 1 but you would need to check.
Use .Selected = True on the option element itself.
As you have options that have the same values you need to isolate the dropdowns by the parent select if using second method. This can be done by adding parent className in front.
Method 1:
hour
ie.document.querySelector(".hour").SelectedIndex = 1
minute
ie.document.querySelector(".minute").SelectedIndex = 1
Method 2:
hour
ie.document.querySelector(".hour [value='0']").Selected = True 'hour 00
minute
ie.document.querySelector(".minute [value='0']").Selected = True 'minute 00
Alternates are predominately the above/variants thereof written in javascript and executed by method of
ie.document.parentWindow.execScript. This can be helpful when elements are not interactable for some reason but generally frowned
upon by QA automation specialists in particular.
Reading:
CSS Selectors
EDIT:
To differentiate starttime from endtime based on html provided e.g.
ie.document.querySelector(".combodate + .combodate .hour [value='0']").Selected =True 'end hour
ie.document.querySelector(".combodate + .combodate .minute [value='0']").Selected =True 'end minute
Using variable
Dim var As Long
var = 0
ie.document.querySelector(".combodate .hour [value='" & cstr(var) & "']").Selected =True 'start hour
Although your HTML doesn't reflect this I would have expected something more like:
ie.document.querySelector("#starttime_field .combodate .hour [value='" & cstr(var) & "']").Selected =True
ie.document.querySelector("#endtime_field .combodate .hour [value='" & cstr(var) & "']").Selected =True
With below code, I'm trying to extract a line from syslog file having string original value but I'm getting whole file data as output.
Sub test()
Dim InputData
' Open file for input.
Open "D:\temp\DraftTest3_pmi_jt_import_All_Annotations.syslog" For Input As #1
Do While Not EOF(1) ' Check for end of file.
Line Input #1, InputData ' Read line of data.
Debug.Print InputData ' Print to the Immediate window.
If InStr(1, InputData, "original value") Then
Cells(2, 3).Value = InputData ''' Print a line in cell
InputData = ""
End If
Loop
Close #1 ' Close file.
End Sub
Syslog content:
Nodes differ : original value 189.1596739640256
new value 191.1200796864099
Difference:
<object type="NXOpen.ValidationAnnotation3DCollectionValidator" value="None" xml_line_number="14" />
<context line_number="132" variable_name="validationAnnotation3DCollectionValidator1" xml_line_number="15" />
<object type="NXOpen.ValidationWeldValidator" value="None" xml_line_number="2675" />
<context line_number="0" variable_name="" xml_line_number="2676" />
<output name="WeldReferenceLineLength" xml_line_number="2677" />
<object tolerance="1e-06" tolerance_type="absolute" type="double" value="189.1596739640256" />
*** changed to ***
<object tolerance="1e-06" tolerance_type="absolute" type="double" value="191.1200796864099" xml_line_number="2678" />
_________________________________________________________________________________________
Nodes differ : original value 102.5546050485778
new value 102.8898888970786
Difference:
<object type="NXOpen.ValidationAnnotation3DCollectionValidator" value="None" xml_line_number="14" />
<context line_number="132" variable_name="validationAnnotation3DCollectionValidator1" xml_line_number="15" />
<object type="NXOpen.ValidationWeldValidator" value="None" xml_line_number="4422" />
<context line_number="0" variable_name="" xml_line_number="4423" />
<output name="WeldReferenceLineLength" xml_line_number="4424" />
<object tolerance="1e-06" tolerance_type="absolute" type="double" value="102.5546050485778" />
Desired output:
Do you want to extract the value 189.1596739640256? – Siddharth Rout 7 mins ago
Yes. I want that value. – Experimenter 6 mins ago
The fastest way to get that value would be
Open and read the text file in ONE GO. i.e without looping
Split on original value
And then split on new value
Code:
Option Explicit
Sub Sample()
Dim MyData As String, ValueNeeded As String
'~~> Change this to the relevant file
'~~> Read the entire file in a variable in ONE GO
Open "C:\Users\routs\Desktop\Sample.Txt" For Binary As #1
MyData = Space$(LOF(1))
Get #1, , MyData
Close #1
ValueNeeded = Trim(Split(MyData, "original value")(1))
ValueNeeded = Trim(Split(ValueNeeded, "new value")(0))
Debug.Print ValueNeeded
End Sub
Screenshot
EDIT
To incorporate multiple instances as shown in your recent edit, you will have to look for a unique word. In your case it would be Nodes differ :. Why this? becuase this signifies a new section. So the logic would be to split on Nodes differ : and then split on original value, new value and Difference:
See this example
Option Explicit
Sub Sample()
Dim MyData As String, ValueString As String
Dim orgVal As String, newVal As String
Dim strData() As String
Dim i As Long
'~~> Change this to the relevant file
'~~> Read the entire file in a variable in ONE GO
Open "C:\Users\routs\Desktop\test.Txt" For Binary As #1
MyData = Space$(LOF(1))
Get #1, , MyData
Close #1
'~~> Split on "Nodes differ :" to identify separate sections
'~~> Store them in an array
strData() = Split(MyData, "Nodes differ :")
'~~> Loop though each section and split on relevant identifiers
'~~> to get the necessary value
For i = LBound(strData) + 1 To UBound(strData)
ValueString = Trim(Split(strData(i), "original value")(1))
orgVal = Trim(Split(ValueString, "new value")(0))
newVal = Trim(Split(ValueString, "new value")(1))
newVal = Trim(Split(newVal, "Difference:")(0))
Debug.Print "Original Value: " & Trim(orgVal)
Debug.Print "New Value: " & Trim(newVal)
Next i
End Sub
NOTE: I have not done any error handling. I am sure you will take care of that...
InStr returns a Variant, not a Boolean. Try changing your comparison to If InStr(...) > 0. Docs are at https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/instr-function .
I also note that you are overwriting a single cell with each value in turn. If you want to save all the values, you will need to change how you index Cells.
Edit Line Input cannot handle Unix-type text files (LF-only line endings). See, e.g., https://chandoo.org/forum/threads/line-input-reads-entire-text-file-as-a-single-record.23089/ . Try the answers at Loading linux text file into excel using VBA . In short, you have to read the whole file (which you already do :) ) and then split it on vbLf to get individual lines.
I`m working on a VBA macro in Excel, to gather information from a CNC program code.
So far, I have gotten Material type, thickness, x & Y sizes, and qty used.
I`m trying to get the 'cutting length' now - so I can use it in costing calculations.
Here is the XML code segment :
<Info num="6" name="Tools">
<MC machine="psys_ETN_5">
<Tool name="TN901" length="16262.96209" time="53.72817301" cutoutArea="8138.657052"/>
</MC>
</Info>
There are lots of 'Info' lines.
There may be more than one 'Tool' line, but I`m only after anything from line with 'TN901'.
The data I`m trying to capture is the value of 'Length="######.##"'
I`ve captured everything else I need from code like this :
<Material>316</Material>
<SheetX>2000</SheetX>
<SheetY>1000</SheetY>
<Thickness>3</Thickness>
</Material>
using code like this:
For Each nodemat In XMLDataDrg.SelectNodes("//Material")
Matl = nodemat.Text
Worksheets("Sheet4").Range("H" & RowA).Value = Matl
Next
For Each nodesht In XMLDataDrg.SelectNodes("//Thickness")
Thk = nodesht.Text
Worksheets("Sheet4").Range("I" & RowA).Value = Thk
Next
But that type of code does not get the cutting length.
Any help please ? :)
Thanks
Simon
Thickness is saved as XML element in your example.
The length is stored as an XML attribute.
(see https://www.xmlfiles.com/xml/xml-attributes/)
To read an XML attribute please have a look at:
Read XML Attribute VBA
Based on the code presented there you should be able to solve your issue with:
'Include a reference to Microsoft XML v3
Dim XMLDataDrg As DOMDocument30
Set XMLDataDrg = New DOMDocument30
XMLDataDrg.Load ("C:\...\sample.xml")
'...
Dim id As String
id = XMLDataDrg.SelectSingleNode("//Info/MC/Tool").Attributes.getNamedItem("length").Text
You can use an xpath to restrict to Tool elements with attribute name having value TN901 then loop all the attributes and write out. I am reading your XML from a file on desktop.
Option Explicit
Public Sub test()
Dim xmlDoc As Object
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
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 elem As Object, attrib As Object
For Each elem In xmlDoc.SelectNodes("//Tool[#name='TN901']")
For Each attrib In elem.Attributes
Debug.Print attrib.nodeName, attrib.Text
Next
Next
End Sub
Result: