Search code examples
excelxmlvbaxpathattributes

Create numbered XML nodes, set attributes when creating XML node


I've got an Excel macro reading XML values from one file and replicating them in a second. I can't just copy files, as the second file has more and different stuff, and user input determines what is mapped.
E.g. to use the example of a movie database, the user picks which genres to copy from source to target. I need to map specific values; the XPaths of the source and target are in a spreadsheet. Not all of the tags exist in the new XML file, so they need to be created by my code before their values can be populated.

I've a great start from Greg-R's work at Create xml file based on xPath from Excel with VBA but his code doesn't handle numbered nodes. I stripped the number from the target tag, but can't figure out the methods for adding the attribute to the node.
E.g. the XPath could be //Movies/Title[@number=1]/Actor[@number=5].Name

Sub makeXPath(xmldoc As Object, xpath As String)
'Original code from: https://stackoverflow.com/questions/12149941/create-xml-file-based-on-xpath-from-excel-with-vba
Dim partsOfPath() As String
Dim oNodeList As IXMLDOMNodeList
Dim strXPathQuery As String
Dim sParent As String
Dim objRootElem As IXMLDOMElement
Dim objMemberElem As IXMLDOMElement
Dim objMemberName As IXMLDOMElement
Dim objParent As Object

Set objParent = xmldoc

partsOfPath = Split(xpath, "/")

For i = LBound(partsOfPath) To UBound(partsOfPath)
    If strXPathQuery > "" Then strXPathQuery = strXPathQuery & "/"
    strXPathQuery = strXPathQuery & partsOfPath(i)

    Set oNodeList = xmldoc.SelectNodes(strXPathQuery)

    If oNodeList.Length = 0 Then
        'if I don't have the node, create it
        Debug.Print "partsOfPath(" & i & ") = " & partsOfPath(i)

        NumberPos = InStr(partsOfPath(i), "[@number=")
        If NumberPos > 0 Then
            'Numbered node, extract the number
            ElementName = Left(partsOfPath(i), NumberPos - 1)
            'Len("[@number=") = 9. Speed the code up by not calculating it each time. Every little bit helps!
            NodeNumber = Mid(partsOfPath(i), NumberPos + 9, Len(partsOfPath(i)) - NumberPos - 9)
        Else
            ElementName = partsOfPath(i)
            NodeNumber = ""
        End If

        Set objMemberElem = xmldoc.createElement(ElementName)
        objParent.appendChild objMemberElem

        If Not NodeNumber = "" Then
            objMemberElem.createAttribute ("number")         '<<<------ This bit is throwing errors :(
            .createAttribute ("number")
            
            objParent.Attributes.setNamedItem(objAttr).Text = NodeNumber
        End If
        'setting the parent for the next element of the path
        Set objParent = objMemberElem
    Else
        'setting parent to first iteration, until I make adjustment otherwise later
        Set objParent = oNodeList.Item(0)
    End If

Next

End Sub

I researched this (How many tabs can Chrome handle?) and tried various methods. What are the methods I should be using?


Solution

  • Just like createElement, createAttribute is a method of the xml document, not of a node like objMemberElem.

    This should work:

    If Not NodeNumber = "" Then
        Set objAttr = xmldoc.createAttribute("number")  
        objAttr.Value = NodeNumber
        objMemberElem.Attributes.setNamedItem objAttr
    End If