MSXML2:如何使用 XPATH 更新(+插入和删除)节点

MSXML2: How to update (+insert and delete) node using XPATH

我需要帮助更新 XML 文件的特定节点。我使用的是 MSXML2.DOMDocument60。此代码演示了这个想法:

Option Explicit

Sub UpdateXML()
    'Load the XML file into oDoc
    Dim oDoc As MSXML2.DOMDocument60
    Set oDoc = New MSXML2.DOMDocument60
    If Not oDoc.Load("C:\test.xml") Then
        Debug.Print oDoc.parseError
        Exit Sub
    End If
    
    Dim xPath As String
    'Lets say I want to update this node:
    xPath = "/root/devices/device[@name='DB']/package[@name='DIL8']/technologies"
    
    Dim sNode As IXMLDOMNode
    'I know how to select it
    Set sNode = oDoc.selectSingleNode(xPath)
    
    If Not sNode Is Nothing Then
        Debug.Print sNode.XML
        
        'This function returns the node with new data (function follows)
        Debug.Print getTechnologies.XML
        
        'Here I need to insert the data returned by getTechnologies()
        'into the correct place of the oDoc (specified by xPath)
        
        'this does not work
        oDoc.selectSingleNode(xPath) = getTechnologies
        '??? odoc.replaceChild ??? I'm lost here
         
        'It would be great to see the example how to insert and delete
        'the node <technologies> of the oDoc using xPath... if it is possible of course.
        
    End If
    
    'Save modified data into new file
    oDoc.Save "C:\final.xml"
    
    'Final file should now contain "newValue" within the
    '/root/devices/device[@name='DB']/package[@name='DIL8']/technologies
End Sub

Function getTechnologies() As IXMLDOMNode
    'This is just a simplified example to demonstrate the function
    'that returns the IXMLDOMNode object
    'In real, this function pulls data from a database
    
    Dim oNode As MSXML2.DOMDocument60
    Set oNode = New MSXML2.DOMDocument60
    Dim sXml As String
    sXml = "<technologies>" & vbCrLf & _
           "    <property name='prop1' value='newValue'/>" & vbCrLf & _
           "    <property name='prop2' value='newValue'/>" & vbCrLf & _
           "    <property name='prop3' value='newValue'/>" & vbCrLf & _
           "    <property name='prop4' value='newValue'/>" & vbCrLf & _
           "</technologies>"
    If Not oNode.loadXML(sXml) Then
        Debug.Print oNode.parseError
    Else
        Set getTechnologies = oNode.selectSingleNode("/technologies")
    End If
End Function

这是我在示例中使用的文件 test.xml。它是真实文件的简化版本:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <library>
        <items>
            <item name="foo"/>
            <item name="bar"/>
            <item name="foo2"/>
            <item name="bar2"/>
        </items>
    </library>
    <devices>
        <device name="DB">
            <package name="DIL4">
                <something>Another tree could be here</something>
                <technologies>
                    <property name="prop1" value="oldValue"/>
                    <property name="prop2" value="oldValue"/>
                    <property name="prop3" value="oldValue"/>
                    <property name="prop4" value="oldValue"/>
                </technologies>
            </package>
            <package name="DIL8">
                <technologies>
                    <property name="prop1" value="oldValue"/>
                    <property name="prop2" value="oldValue"/>
                    <property name="prop3" value="oldValue"/>
                    <property name="prop4" value="oldValue"/>
                </technologies>
                <something>The order is not guaranteed</something>
            </package>
            <package name="DIL16">
                <technologies>
                    <property name="prop1" value="oldValue"/>
                    <property name="prop2" value="oldValue"/>
                    <property name="prop3" value="oldValue"/>
                    <property name="prop4" value="oldValue"/>
                </technologies>
            </package>
        </device>
        <device name="NPN">
            <package name="SOT23">
                <technologies>
                    <property name="prop1" value="oldValue"/>
                    <property name="prop2" value="oldValue"/>
                    <property name="prop3" value="oldValue"/>
                    <property name="prop4" value="oldValue"/>
                </technologies>
            </package>
        </device>
    </devices>
</root>

编辑:下面是 this answer 的代码,但我不明白修改 xmlRoot 会如何影响 xmlDoc -是参考文献吗? (见代码中的注释)

Sub XMLTest()
Dim myVar As String, pathToXML As String
Dim xmlDoc As Object, xmlRoot As Object
    Set xmlDoc = CreateObject("MSXML2.DOMDocument")
    pathToXML = "N:\example.xml" '<--- change the path
    Call xmlDoc.Load(pathToXML)
    Set xmlRoot = xmlDoc.getElementsByTagName("RefTest").Item(0)
    myVar = "foobar" '<--- your value

    'Here the xmlRoot object is updated
    xmlRoot.selectSingleNode("iRef5").Text = myVar
    
    'Here the xmlDoc is saved
    Call xmlDoc.Save(pathToXML)
End Sub

感觉答案就在眼前,却看不到

简单xml文件:

<?xml version="1.0"?>
<!-- This file represents a fragment of a bookstore inventory database -->
<bookstore specialty="novel">
  <book>
    <Title>Beginning XML</Title>
    <Publisher>Wrox</Publisher>
  </book>
  <book>
    <Title>Professional XML</Title>
    <Publisher>Wrox</Publisher>
  </book>
  <book>
    <Title>Programming ADO</Title>
    <author>
      <first-name>Mary</first-name>
      <last-name>Jones</last-name>      
    </author>
    <datePublished>1/1/2000</datePublished>
    <Publisher>Microsoft Press</Publisher>
  </book>
</bookstore>

以下为我编辑单个节点工作。

Dim oDoc As MSXML2.DOMDocument60, sNode As MSXML2.IXMLDOMNode
Set oDoc = New MSXML2.DOMDocument60
oDoc.Load "path\filename"
Set sNode = oDoc.SelectSingleNode("//book[3]/Title") 'selection criteria using XPath syntax
sNode.Text = "something"
oDoc.Save("path\filename") 'If same path\filename is used, it will overwrite.

整个问题是我认为 oDocsNode 对象是相互独立的。我没有意识到 sNode 是对 oDoc 节点的主动引用。感谢 June7 的回答,我了解了它是如何工作的,然后只花了一点时间就找到了我最初想到的所有 3 个问题的答案:

Sub UpdateXML()
    'Load the XML file into oDoc
    Dim oDoc As MSXML2.DOMDocument60
    Set oDoc = New MSXML2.DOMDocument60
    If Not oDoc.Load("C:\test.xml") Then
        Debug.Print oDoc.parseError
        Exit Sub
    End If

    Dim xPath As String
    'Lets say I want to update this node:
    xPath = "/root/devices/device[@name='DB']/package[@name='DIL8']/technologies"

    Dim sNode As IXMLDOMNode         'Reference to oDoc node
    Set sNode = oDoc.selectSingleNode(xPath)
    If Not sNode Is Nothing Then     'If node is found then
    
        Dim nTech As IXMLDOMNode
        Set nTech = getTechnologies  'Get node from function
    
        'To update selected node
        sNode.parentNode.replaceChild nTech, sNode
        
        'To remove node (Reference to sNode must be set again after replaceChild method)
        Set sNode = oDoc.selectSingleNode(xPath)
        sNode.parentNode.removeChild sNode
        
        'To insert new node into a particular node
        xPath = "/root/devices/device[@name='DB']/package[@name='DIL8']"
        Set sNode = oDoc.selectSingleNode(xPath)
        sNode.appendChild nTech

    End If
    'Save modified data into new file
    oDoc.Save "C:\final.xml"
End Sub