Specific node in XML file use VBA - excel

I want to replace data in a XML file using VBA. The XML data looks like this:
<MarkGrades>
<Grade MarkGrade="0" Label="3OP" TestBins="1,-1">0</Grade>
<Grade MarkGrade="2" Label="GOOD" TestBins="2">2</Grade <Grade>
<Grade MarkGrade="4" Label="FU" TestBins="31,3135,3136,3312,">4</Grade>
<Grade MarkGrade="3" Label="PA" TestBins="4016,4022,4029">3</Grade>
<Grade MarkGrade="5" Label="OS" TestBins="12,13,20012,20013">5</Grade>
</MarkGrades>
I want to replace data in node TestBins. If MarkGrade="4" or Label="FU" then replace data in TestBins from "2" to "something". But I can't seem to figure out how to specify that:
Dim doc As Object
Set doc = CreateObject("MSXML2.DOMDocument")
Dim isLoaded As Boolean
Dim i As Integer
Dim filePath As Variant
filePath = "C:\Users\xxxx\Desktop\Splitter\test.xml"
isLoaded = doc.Load(filePath)
i = 0
If isLoaded Then
Dim Grade As msxml2.IXMLDOMNodeList
Set Grade = doc.getElementsByTagName("Grade")
Dim attr As msxml2.IXMLDOMAttribute
Dim node As msxml2.IXMLDOMElement
For Each node In Grade
For Each attr In node.Attributes
If attr.Name = "TestBins" Then
i = i + 1
If i = 3 Then
attr.Value = SplitterMark.TextBox3.Value
ElseIf i = 4 Then
attr.Value = SplitterMark.TextBox4.Value
ElseIf i = 5 Then
attr.Value = SplitterMark.TextBox5.Value
End If
End If
Next attr
Next node
I tried to count if found TestBins count +1
That is working good. But can use with only this file because other file it is not common. like this:
<MarkGrades>
<Grade MarkGrade="5" Label="OS" TestBins="12,13,20012,20013">5</Grade>
<Grade MarkGrade="0" Label="3OP" TestBins="1,-1">0</Grade>
<Grade MarkGrade="4" Label="FU" TestBins="31,3135,3136,3312,">4</Grade>
<Grade MarkGrade="2" Label="GOOD" TestBins="2">2</Grade <Grade>
<Grade MarkGrade="3" Label="PA" TestBins="4016,4022,4029">3</Grade>
</MarkGrades>
Any ideas?

You could use xpath to identify the appropriate nodes and setAttribute for the value setting. You need to correct your xml though.
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
For Each elem In xmlDoc.SelectNodes("//Grade[#MarkGrade='4' or #Label='FU' and #TestBins]")
elem.setAttribute "TestBins", "Banana"
Next
Debug.Print xmlDoc.XML
End Sub
Corrected xml line:
<Grade MarkGrade="2" Label="GOOD" TestBins="2">2</Grade>

Related

VBA and OneNote. Moving a page in another section: Error on OneNote.UpdateContentPage method

I wrote a Subroutine to move a page in a different section compared to the original one but it does't work! May someone help me?
Private Sub CreateInNewSection(onote As OneNote.Application, pageXML As String, newSecId As String, title As String)
Dim pDoc As MSXML2.DOMDocument60
Set pDoc = New MSXML2.DOMDocument60
If pDoc.LoadXML(pageXML) Then
Dim cNodes As MSXML2.IXMLDOMNodeList
Dim fNodes As MSXML2.IXMLDOMNodeList
Dim iNodes As MSXML2.IXMLDOMNodeList
Dim cNode As MSXML2.IXMLDOMNode
Dim fNode As MSXML2.IXMLDOMNode
Dim iNode As MSXML2.IXMLDOMNode
soapNS = "xmlns:one='http://schemas.microsoft.com/office/onenote/2013/onenote'"
pDoc.setProperty "SelectionNamespaces", soapNS
Set cNodes = pDoc.DocumentElement.SelectNodes("//one:T")
Set fNodes = pDoc.DocumentElement.SelectNodes("//one:InsertedFile")
Set iNodes = pDoc.DocumentElement.SelectNodes("//one:Image")
Dim nPageID As String
onote.CreateNewPage newSecId, nPageID, npsDefault
Dim oXML As String
onote.GetPageContent nPageID, oXML, piAll, xs2013
'oXML = pageXML
Dim nDoc As MSXML2.DOMDocument60
Set nDoc = New MSXML2.DOMDocument60
If nDoc.LoadXML(oXML) Then
Dim npNode As MSXML2.IXMLDOMNode
soapNS = "xmlns:one='http://schemas.microsoft.com/office/onenote/2013/onenote'"
nDoc.setProperty "SelectionNamespaces", soapNS
Set npNode = nDoc.SelectSingleNode("//one:Page")
' Find the Title element.
Dim tNode As MSXML2.IXMLDOMNode
Set tNode = nDoc.SelectSingleNode("//one:Page/one:Title/one:OE/one:T")
' Get the CDataSection where OneNote store's the Title's text.
Dim cdataChild As MSXML2.IXMLDOMNode
Set cdataChild = tNode.SelectSingleNode("text()")
' Change the title in the local XML copy.
cdataChild.Text = title
' Write the update to OneNote.
'oneNote.UpdatePageContent doc.XML
'---------- For Text Nodes -----------
For Each cNode In cNodes
If cNode.Text <> "" Then
Dim newTextNodeElement As MSXML2.IXMLDOMElement
Dim newTextNode As MSXML2.IXMLDOMNode
' Create Outline node.
Set newTextNodeElement = nDoc.createElement("one:Outline")
Set newTextNode = npNode.appendChild(newTextNodeElement)
' Create OEChildren.
Set newTextNodeElement = nDoc.createElement("one:OEChildren")
Set newTextNode = newTextNode.appendChild(newTextNodeElement)
' Create OE.
Set newTextNodeElement = nDoc.createElement("one:OE")
Set newTextNode = newTextNode.appendChild(newTextNodeElement)
' Create TE.
Set newTextNodeElement = nDoc.createElement("one:T")
Set newTextNode = newTextNode.appendChild(newTextNodeElement)
' Add the text for the Page's content.
Dim newcd As MSXML2.IXMLDOMCDATASection
Set newcd = nDoc.createCDATASection(cNode.Text)
newTextNode.appendChild newcd
End If
Next
---------- For File Nodes -----------
For Each fNode In fNodes
'Set newFileNode = fNode
Set npNode = npNode.appendChild(fNode)
Next
onote.UpdatePageContent nDoc.XML, DateTime.Now, xs2013
End If
End If
End Sub
The onenote.UpdatePageContent continues to fail with a runtime error -2147213296 (80042010). The code works if i consider only text nodes, while if i add the code for File Nodes it doesn't work anymore.
I tried to change the code for File Nodes with this code:
For Each fNode In fNodes
Dim newFileNodeElement As MSXML2.IXMLDOMElement
Dim newFileNode As MSXML2.IXMLDOMNode
'Set newFileNode = fNode
'Set npNode = npNode.appendChild(fNode)
Set newFileNodeElement = nDoc.createElement("one:InsertedFile")
Set newFileNode = npNode.appendChild(newFileNodeElement)
For i = 0 To fNode.Attributes.Length - 1
Dim attrName As String
Dim attrValue As String
Dim attr As MSXML2.IXMLDOMAttribute
Dim namedNodeMap As MSXML2.IXMLDOMNamedNodeMap
attrName = fNode.Attributes(i).nodeName
attrValue = fNode.Attributes(i).NodeValue
Set attr = nDoc.createNode(2, attrName, "")
attr.Value = attrValue
Set namedNodeMap = nDoc.DocumentElement.LastChild.Attributes
Set newFileNode = namedNodeMap.setNamedItem(attr)
Next i
Next
but the results are the same.
OneNote error codes are listed here, so that error is "The last modified date does not match. ".
The second argument to UpdatePageContent is:
dateExpectedLastModified – (Optional) The date and time that you think
the page you want to update was last modified. If you pass a non-zero
value for this parameter, OneNote proceeds with the update only if the
value you pass matches the actual date and time the page was last
modified. Passing a value for this parameter helps prevent
accidentally overwriting edits users made since the last time the page
was modified.
Remove the DateTime.Now argument (omit it), if you don't need it, or correct the value. It is an additional check parameter, not a date-stamp.
I solved this issue using a different approach: i used Publish method in order to create a new section with the name of the page i needed to copy, and the MergeSections method to copy the page in the section i wished. Here is the code:
Private Sub CreateInNewSection(onote As onenote.Application, pageXML As String,
newSecId As String, title As String, nbID As String, path As String)
Dim pDoc As MSXML2.DOMDocument60
Set pDoc = New MSXML2.DOMDocument60
If pDoc.LoadXML(pageXML) Then
Dim pNode As MSXML2.IXMLDOMNode
soapNS = "xmlns:one='http://schemas.microsoft.com/office/onenote/2013/onenote'"
pDoc.setProperty "SelectionNamespaces", soapNS
Set pNode = pDoc.SelectSingleNode("//one:Page")
Dim oldPageId As String
Dim oldPageName As String
oldPageId = pNode.Attributes.getNamedItem("ID").Text
oldPageName = pNode.Attributes.getNamedItem("name").Text
path = path + "\" + oldPageName + ".one"
Debug.Print path
onote.Publish oldPageId, path, pfOneNote, ""
Dim sXml As String
onote.GetHierarchy nbID, hsSections, sXml, xs2013
Dim sDoc As MSXML2.DOMDocument60
Set sDoc = New MSXML2.DOMDocument60
Dim nowSecId As String
If sDoc.LoadXML(sXml) Then
' select the Section nodes
Dim sNodes As MSXML2.IXMLDOMNodeList
soapNS = "xmlns:one='http://schemas.microsoft.com/office/onenote/2013/onenote'"
sDoc.setProperty "SelectionNamespaces", soapNS
Set sNodes = sDoc.DocumentElement.SelectNodes("//one:Section")
Dim j As Integer
If Not sNodes Is Nothing Then
' Get the first section.
Dim sNode As MSXML2.IXMLDOMNode
For j = 0 To (sNodes.Length - 1)
If sNodes(j).Attributes.getNamedItem("name").Text = oldPageName Then
nowSecId = sNodes(j).Attributes.getNamedItem("ID").Text
Exit For
End If
Next j
End If
onote.MergeSections nowSecId, newSecId
onote.DeleteHierarchy nowSecId
onote.DeleteHierarchy oldPageId
End If
End If
End Sub

VBA Macro, get URL from given range loop and pull XML node

I have this code that is set up to get data ZIP code (single node) from an XML URL. However, I actually have a list of URLs in Sheet1, Column B that I need to loop through until all the data is extracted.
I dont want to have to update the code each time individually per URL. There are thousands... How would i be able to do that?
Here's an example of a working code for a single URL:
Sub test1()
Dim xmlDocument As MSXML2.DOMDocument60
Dim URL As String
Dim node As Object
Set xmlDocument = New DOMDocument60
URL = Sheets("Sheet1").Range("b2").Value
'Open XML page
Set xmlDocument = New MSXML2.DOMDocument60
xmlDocument.async = False
xmlDocument.validateOnParse = False
xmlDocument.Load URL
Dim nodeId As IXMLDOMNode
Dim nodeId2 As IXMLDOMNode
Set nodeId = xmlDocument.SelectSingleNode("/ZipCodeLookupResponse/Address/Zip5")
Set nodeId2 = xmlDocument.SelectSingleNode("/ZipCodeLookupResponse/Address/Zip4")
If Not nodeId Is Nothing Then
Sheets("fy2016").Range("e2").Value = nodeId.Text & " " & nodeId2.Text
Else
Sheets("fy2016").Range("e2").Value = "'ZIP code' was not found."
End If
End Sub
Assuming your code works you want something like a For Loop over all the urls. Move your document outside of the loop and load it inside the loop. I use an array to store the urls read in from the sheet for faster handling. Your construct wasn't handling any errors on parse so I have commented out related lines.
Not tested.
Option Explicit
Public Sub test1()
Dim xmlDocument As MSXML2.DOMDocument60, URLs(), i As Long
Dim node As Object, nodeId As IXMLDOMNode, nodeId2 As IXMLDOMNode
Set xmlDocument = New DOMDocument60
URLs = ThisWorkbook.Worksheets("Sheet1").Range("B2:B1000").Value
Set xmlDocument = New MSXML2.DOMDocument60
xmlDocument.async = False
' xmlDocument.validateOnParse = False
For i = LBound(URLs, 1) To UBound(URLs, 1)
xmlDocument.Load URLs(i, 1)
Set nodeId = xmlDocument.SelectSingleNode("/ZipCodeLookupResponse/Address/Zip5")
Set nodeId2 = xmlDocument.SelectSingleNode("/ZipCodeLookupResponse/Address/Zip4")
If Not nodeId Is Nothing Then
ThisWorkbook.Worksheets("fy2016").Cells(i + 1, "E").Value = nodeId.Text & " " & nodeId2.Text
Else
ThisWorkbook.Worksheets("fy2016").Cells(i + 1, "E").Value = "'ZIP code' was not found."
End If
Set nodeId = Nothing: Set nodeId2 = Nothing
Next
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 finda text and get the page no. for acrobat using vba

I want to find the text and get the page number of text found in acrobat using VBA, I am able to find the text but not able to get the page number. for that
Sub Main()
Dim acrApp, acrAVDoc
Set acrApp = CreateObject("AcroExch.app")
Set acrAVDoc = CreateObject("AcroExch.AVDoc")
acrApp.Show
If acrAVDoc.Open("FileName", "") Then
Ok = acrAVDoc.FindText("Text to search", 0, 1, 1)
MsgBox (Ok)
End If
Set acrAVDoc = Nothing
Set acrApp = Nothing
End Sub
I am not able to set the object for
Set acrPDDoc = CreateObject("Acrobat.AV_PAGE_VIEW")
I know this is an old question, but it was one of the top search results when I was looking for the same info. I never found anything that truly met my needs so I made something up by combining several different resources.
The function below is acceptably fast, even on very large documents. It searches page by page, not word by word, so it will find multi-word matches and words with dashes (case insensitive). It returns the matches for all pages separated by commas.
Hope this is helpful to someone in the future.
Sub Demo()
Dim SearchResult As String
SearchResult = AdobePdfSearch("my search string", "C:\Demo\Demo.pdf")
MsgBox SearchResult
End Sub
Function AdobePdfSearch(SearchString As String, strFileName As String) As String
'Note: A Reference to the Adobe Library must be set in Tools|References!
'Note! This only works with Acrobat Pro installed on your PC, will not work with Reader
Dim AcroApp As CAcroApp, AcroAVDoc As CAcroAVDoc, AcroPDDoc As CAcroPDDoc
Dim AcroHiliteList As CAcroHiliteList, AcroTextSelect As CAcroPDTextSelect
Dim PageNumber, PageContent, Content, i, j, iNumPages
Dim strResult As String
Set AcroApp = CreateObject("AcroExch.App")
Set AcroAVDoc = CreateObject("AcroExch.AVDoc")
If AcroAVDoc.Open(strFileName, vbNull) <> True Then Exit Function
Set AcroPDDoc = AcroAVDoc.GetPDDoc
iNumPages = AcroPDDoc.GetNumPages
For i = 0 To iNumPages - 1
Set PageNumber = AcroPDDoc.AcquirePage(i)
Set PageContent = CreateObject("AcroExch.HiliteList")
If PageContent.Add(0, 9000) <> True Then Exit Function
Set AcroTextSelect = PageNumber.CreatePageHilite(PageContent)
' The next line is needed to avoid errors with protected PDFs that can't be read
On Error Resume Next
For j = 0 To AcroTextSelect.GetNumText - 1
Content = Content & AcroTextSelect.GetText(j)
Next j
If InStr(1, LCase(Content), LCase(SearchString)) > 0 Then
strResult = IIf(strResult = "", i + 1, strResult & "," & i + 1)
End If
Content = ""
Next i
AdobePdfSearch = strResult
'Uncomment the lines below if you want to close the PDF when done.
'AcroAVDoc.Close True
'AcroApp.Exit
'Set AcroAVDoc = Nothing: Set AcroApp = Nothing
End Function
sub checks each page of pdf, word by word
Sub FindtextandPageNumber()
Dim FindWord 'Word you want to search
Dim acroAppObj As Object
Dim PDFDocObj As Object
Dim myPDFPageHiliteObj As Object
Dim iword As Integer, iTotalWords As Integer
Dim numOfPage As Integer, Nthpage As Integer
Dim word As String, sPath As String
Set acroAppObj = CreateObject("AcroExch.App")
Set PDFDocObj = CreateObject("AcroExch.PDDoc")
Set myPDFPageHiliteObj = CreateObject("AcroExch.HiliteList")
Check3 = myPDFPageHiliteObj.Add(0, 32767)
FindWord = "Hello"
acroAppObj.Show
sPath = "Test.pdf" 'Path of pdf where you want to search
PDFDocObj.Open (sPath)
numOfPage = PDFDocObj.GetNumPages
word = vbNullString
Set PDFJScriptObj = Nothing
For Nthpage = 0 To numOfPage - 1
Set pAcroPDPage = PDFDocObj.AcquirePage(Nthpage)
Set wordHilite = pAcroPDPage.CreateWordHilite(myPDFPageHiliteObj)
Set PDFJScriptObj = PDFDocObj.GetJSObject
iTotalWords = wordHilite.GetNumText
iTotalWords = PDFJScriptObj.getPageNumWords(Nthpage)
''check the each word
For iword = 0 To iTotalWords - 1
word = Trim(CStr(PDFJScriptObj.getPageNthWord(Nthpage, iword)))
If word <> "" Then
If word = FindWord Then
PageNumber = Nthpage
msgbox PageNumber
End If
word = ""
End If
Next iword
Next Nthpage
End Sub

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