使用 Excel VBA 解析 XML

Parsing XML using Excel VBA

我正在尝试使用 Excel VBA 解析来自 Sharepoint REST API 的 XML 响应。这是 XML 代码的摘录。出于保密原因,我更改了一些值

<?xml version="1.0" encoding="utf-8"?>
<feed xml:base="https://company/sites/subsite/_api/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
    <id>4db71c92-9576-4c59-bb85-89d27459139e
    </id>
    <title />
    <updated>2022-04-13T20:17:19Z
    </updated>
    <entry>
        <id>https://company.sharepoint.com/sites/subsite/_api/Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)
        </id>
        <category term="SP.RoleAssignment" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
        <link rel="edit" href="Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)" />
        <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Member" type="application/atom+xml;type=entry" title="Member" href="Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)/Member">
            <m:inline>
                <entry>
                    <id>https://lionbridge.sharepoint.com/sites/LIOXSalesOpsFinancialsDEV/_api/Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)/Member
                    </id>
                    <category term="SP.Group" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
                    <link rel="edit" href="Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)/Member" />
                    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Owner" type="application/atom+xml;type=entry" title="Owner" href="Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)/Member/Owner" />
                    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Users" type="application/atom+xml;type=feed" title="Users" href="Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)/Member/Users" />
                    <title />
                    <updated>2022-04-13T20:17:19Z
                    </updated>
                    <author>
                        <name />
                    </author>
                    <content type="application/xml">
                        <m:properties>
                            <d:Id m:type="Edm.Int32">3
                            </d:Id>
                            <d:IsHiddenInUI m:type="Edm.Boolean">false
                            </d:IsHiddenInUI>
                            <d:LoginName>John Smith
                            </d:LoginName>
                            <d:Title>John Smith
                            </d:Title>
                            <d:PrincipalType m:type="Edm.Int32">8
                            </d:PrincipalType>
                            <d:AllowMembersEditMembership m:type="Edm.Boolean">false
                            </d:AllowMembersEditMembership>
                            <d:AllowRequestToJoinLeave m:type="Edm.Boolean">false
                            </d:AllowRequestToJoinLeave>
                            <d:AutoAcceptRequestToJoinLeave m:type="Edm.Boolean">false
                            </d:AutoAcceptRequestToJoinLeave>
                            <d:Description m:null="true" />
                            <d:OnlyAllowMembersViewMembership m:type="Edm.Boolean">false
                            </d:OnlyAllowMembersViewMembership>
                            <d:OwnerTitle>John Smith
                            </d:OwnerTitle>
                            <d:RequestToJoinLeaveEmailSetting>
                            </d:RequestToJoinLeaveEmailSetting>
                        </m:properties>
                    </content>
                </entry>
            </m:inline>
        </link>
        <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RoleDefinitionBindings" type="application/atom+xml;type=feed" title="RoleDefinitionBindings" href="Web/Lists(guid'e32e86fd-3161-4e44-a654-2424b357d566')/Items(1590)/RoleAssignments/GetByPrincipalId(3)/RoleDefinitionBindings">
            <m:inline>
                <feed>
                    <id>db3f92c8-3b3e-40eb-99de-00be4a313e5c
                    </id>
                    <title />
                    <updated>2022-04-13T20:17:19Z
                    </updated>
                    <entry>
                        <id>https://company.sharepoint.com/sites/subsite/_api/Web/RoleDefinitions(1073741829)
                        </id>
                        <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
                        <link rel="edit" href="Web/RoleDefinitions(1073741829)" />
                        <title />
                        <updated>2022-04-13T20:17:19Z
                        </updated>
                        <author>
                            <name />
                        </author>
                        <content type="application/xml">
                            <m:properties>
                                <d:BasePermissions m:type="SP.BasePermissions">
                                    <d:High m:type="Edm.Int64">2147483647
                                    </d:High>
                                    <d:Low m:type="Edm.Int64">4294967295
                                    </d:Low>
                                </d:BasePermissions>
                                <d:Description>Has full control.
                                </d:Description>
                                <d:Hidden m:type="Edm.Boolean">false
                                </d:Hidden>
                                <d:Id m:type="Edm.Int32">1073741829
                                </d:Id>
                                <d:Name>Full Control
                                </d:Name>
                                <d:Order m:type="Edm.Int32">1
                                </d:Order>
                                <d:RoleTypeKind m:type="Edm.Int32">5
                                </d:RoleTypeKind>
                            </m:properties>
                        </content>
                    </entry>
                </feed>
            </m:inline>
        </link>
        <title />
        <updated>2022-04-13T20:17:19Z
        </updated>
        <author>
            <name />
        </author>
        <content type="application/xml">
            <m:properties>
                <d:PrincipalId m:type="Edm.Int32">3
                </d:PrincipalId>
            </m:properties>
        </content>
    </entry>
    <entry>
...

我想获取 ID 和登录名(在那个例子中,John Smith)

我使用的VBA代码如下:

    'Declare variables
    Dim xml_obj As MSXML2.XMLHTTP60
    
    'Create a reference to the Microsoft XML library
    Set xml_obj = New MSXML2.XMLHTTP60

    'Define URL Components
    base_url = "https://lionbridge.sharepoint.com/sites/lioxsalesopsfinancialsDEV/_api/web"
    endpoint = "/GetFolderByServerRelativeUrl('/sites/lioxsalesopsfinancialsDEV/Shared%20Documents/LGS/Forecast')/ListItemAllFields/RoleAssignments?"
    
    param_1 = "$expand="
    param_1_val = "Member,RoleDefinitionBindings"
    
    'Combine all the different components into a single URL
    api_url = base_url + endpoint + _
              param_1 + param_1_val
    
    Debug.Print api_url
        
    'Open a new request, specify the method and the URL
    xml_obj.Open bstrMethod:="GET", bstrURL:=api_url
    
    'Send the request
    xml_obj.send
    
    'Print the status code, it should be "OK"
    Debug.Print "The Request was " + xml_obj.statusText

    'To parse the info that is sent back, we will store it in a "document" which will leverage a document object model.
    'This model has
    Dim xDoc As MSXML2.DOMDocument60
    Dim xNodes As MSXML2.IXMLDOMNodeList
    Dim xNode As MSXML2.IXMLDOMNode
    
    'First create a new document.
    Set xDoc = New MSXML2.DOMDocument60
    
    'Laod the response text into our document.
    xDoc.LoadXML (xml_obj.responseText)
    
    xDoc.SetProperty "SelectionNamespaces", "xmlns:d='http://schemas.microsoft.com/ado/2007/08/dataservices' xmlns:m='http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'"
    
    Set xNodes = xDoc.getElementsByTagName("m:properties")
    
    For Each xNode In xNodes
        If xNode.ChildNodes.Length <> 1 Then
            Debug.Print xNode.SelectSingleNode("d:Id").Text, xNode.SelectSingleNode("d:Title").Text
        End If
    Next

如果我使用 getElementsByTagName 方法,我几乎得到了我需要的东西,但我也得到了我不需要的节点

我想改用以下方法:

Set xNodes = xDoc.SelectNodes("/feed/entry/link[2]/m:inline/entry/content/m:properties")

然而,xNodes.Length returns 0

我错过了什么?

在根 feed 元素中声明了一些名称空间:

<feed xml:base="https://company/sites/subsite/_api/" 
         xmlns="http://www.w3.org/2005/Atom" 
         xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" 
         xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
         xmlns:georss="http://www.georss.org/georss" 
         xmlns:gml="http://www.opengis.net/gml">

注意其中一个没有别名——这意味着它是 default 命名空间。要使用默认查询节点,您仍然需要使用 SetProperty "SelectionNamespaces" 添加它,并给它一个别名(我在下面使用“xxx”)以便您可以在 xpath 查询中使用它:

Sub tester()

    Dim xDoc As MSXML2.DOMDocument60
    Dim xNodes As MSXML2.IXMLDOMNodeList
    Dim xNode As MSXML2.IXMLDOMNode
    
    Set xDoc = New MSXML2.DOMDocument60
    
    xDoc.validateOnParse = True
    
    xDoc.Load "C:\Temp\tmp.xml" 'loading from a file for testing
    
    xDoc.SetProperty "SelectionNamespaces", _
       "xmlns:xxx='http://www.w3.org/2005/Atom' " & _
       "xmlns:d='http://schemas.microsoft.com/ado/2007/08/dataservices' " & _
       "xmlns:m='http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'"
    
    Set xNodes = xDoc.SelectNodes("/xxx:feed/xxx:entry/xxx:link[2]/m:inline/xxx:entry/xxx:content/m:properties")
    
    Debug.Print xNodes.Length '1 for my sample XML
    
End Sub