【问题标题】:XML parse VBA excel (function trip, & MSXML2.DOMDocument)XML解析VBA excel(函数行,&MSXML2.DOMDocument)
【发布时间】:2019-05-02 15:26:09
【问题描述】:

我需要解析数百个结构相同的 XML 文件,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
  <Concepts>
    <ConceptModel name="food">
      <Filters>
        <Filter type="CC"/>
      </Filters>
      <Queries>
        <Query lang="EN">(cheese, bread, wine)</Query>
        <Query lang="DE">(Käse, Brot, Wein)</Query>
        <Query lang="FR">(fromaige, pain, vin)</Query>
      </Queries>
    </ConceptModel>
  </Concepts>

我已经阅读了以下互联网上的几篇文章和帖子,但我无法提出解决方案:

到目前为止,我正在做:

Dim oXml As MSXML2.DOMDocument
Set oXml = New MSXML2.DOMDocument
oXml.LoadXML ("C:\folder\folder\name.xml")

Dim Queries As IXMLDOMNodeList
Dim Query As IXMLDOMNode

ThisWorkbook.Sheets(3).Cells(i, 1) = "before loop"

Set Queries = oXml.SelectNodes("/concepts/Queries")

MsgBox "how many Queries " &  Queries.Length

For Each Query In Queries
    ThisWorkbook.Sheets(3).Cells(i, 1) = "Works"
    ThisWorkbook.Sheets(3).Cells(i, 2) = Query.SelectNodes("Query").iTem(0).Text
    i = i + 1
Next

这段代码似乎可以被 VBA 理解,但它不读取内容。没有读取循环,这意味着(我猜)查询根本没有循环。 Msgbox "how many queries" 给出 0 作为结果这一事实证实了这一点。但实际上有三个查询。有人可以帮帮我吗?

作为第二个问题,我想问一下

 Dim oXml As MSXML2.DOMDocument

将与

相同
 Dim oXml As MSXML2.DOMDocument60

自从我签入工具/参考“Microsof XML, v6.0”

我认为带有标签的查询 可能会导致问题。我添加了以下几行:

Dim childs As IXMLDOMNodeList
Set childs = oXml.SelectNodes("/concepts")

MsgBox "childs " & childs.Length

这也给出了 0 作为结果。我希望是 3,因为概念有三个孩子,即 ConceptModel、FilterQueries。所以,我更加疑惑了。

【问题讨论】:

    标签: excel xml vba parsing domdocument


    【解决方案1】:

    尽可能靠近您的 OP

    我会提请您注意几个错误或误解:

    • [1] .LoadXML 语法无效

    .LoadXML ("C:\folder\folder\name.xml") 和 .Load ("C:\folder\folder\name.xml") 有什么区别?

    Load 需要一个文件路径,然后将文件内容加载到 oXML 对象中。

    LoadXML 不需要文件参数,但它的实际 XML 文本内容必须是格式正确的字符串。

    • [2] XML 区分小写和大写,因此节点需要通过它们的确切文字名称来寻址: &lt;Query&gt; 节点不会被 "query" 识别,"ConceptModel""conceptmodel" 不同。

    作为第二个问题,我想问一下 Dim oXml As MSXML2.DOMDocument 将与相同 Dim oXml As MSXML2.DOMDocument60, 因为我签入了工具/参考“Microsof XML, v6.0”?

    不,不是。 - 请注意,之前的声明会默认加载 3.0 版本。 但是,最好获得 6.0 版本(现在任何其他版本都已过时!)

    当您使用所谓的早期绑定(引用“Microsoft XML,v6.0”)时,我会这样做,但指的是当前版本 6.0:

    Dim oXml As MSXML2.DOMDocument60        ' declare the xml doc object
    Set oXml = New MSXML2.DOMDocument60     ' set an instance of it to memory
    
    • [3] 误解了一些 XPath 表达式

    XPath 表达式 中的斜线“/”总是指 DocumentElement(此处为&lt;Concepts&gt;), 您可以将 .DocumentElement 添加到您的文档对象中。起始双斜杠“//xyz”将找到任何“xyz”节点(如果存在)。

    例如

        oXml.SelectNodes("//Query").Length 
    

    返回与

    相同的 childNodes 编号(此处:3)
        oXml.DocumentElement.SelectNodes("//Query").Length   ' or 
        oXml.SelectSingleNode("//Queries").ChildNodes.Length ' or even       
        oXml.SelectNodes("/*/*/*/Query").Length`.
    

    参考 XML 6.0 版的代码示例

    当然,您必须遍历多个 xml 文件,该示例仅使用一个(从第 2 行开始)。

    仅针对格式不正确的 xml 文件的情况,我添加了一个详细的错误例程,使您能够识别假定的错误位置。 LoadLoadXML 都返回一个布尔值(如果加载正确,则返回 True,否则返回 False)。

    Sub xmlTest()
    
    Dim ws   As Worksheet: Set ws = ThisWorkbook.Sheets(3)
    Dim oXml As MSXML2.DOMDocument60
    Set oXml = New MSXML2.DOMDocument60
    With oXml
        .validateOnParse = True
        .setProperty "SelectionLanguage", "XPath"   ' necessary in version 3.0, possibly redundant here
        .async = False
    
        If Not .Load(ThisWorkbook.Path & "\xml\" & "name.xml") Then
            Dim xPE        As Object    ' Set xPE = CreateObject("MSXML2.IXMLDOMParseError")
            Dim strErrText As String
            Set xPE = .parseError
            With xPE
               strErrText = "Load error " & .ErrorCode & " xml file " & vbCrLf & _
               Replace(.URL, "file:///", "") & vbCrLf & vbCrLf & _
              xPE.reason & _
              "Source Text: " & .srcText & vbCrLf & vbCrLf & _
              "Line No.:    " & .Line & vbCrLf & _
              "Line Pos.: " & .linepos & vbCrLf & _
              "File Pos.:  " & .filepos & vbCrLf & vbCrLf
            End With
            MsgBox strErrText, vbExclamation
            Set xPE = Nothing
            Exit Sub
        End If
    
        ' Debug.Print "|" & oXml.XML & "|"
    
        Dim Queries  As IXMLDOMNodeList, Query As IXMLDOMNode
        Dim Searched As String
        Dim i&, ii&
        i = 2       ' start row
      ' start XPath  
        Searched = "ConceptModel/Queries/Query"                     ' search string
        Set Queries = oXml.DocumentElement.SelectNodes(Searched)    ' XPath
      ' 
        ws.Cells(i, 1) = IIf(Queries.Length = 0, "No items", Queries.Length & " items")
        ii = 1
        For Each Query In Queries
            ii = ii + 1
            ws.Cells(i, ii) = Query.Text
        Next
    
    End With
    
    End Sub
    

    其他提示

    您可能还对如何通过 XMLDOM 列出 所有子节点obtain attribute names from XML using VBA 的示例感兴趣。

    由于稍后的评论,我提供了进一步的提示(感谢@barrowc)

    “使用 MSXML v3.0 的另一个问题是默认选择语言是 XSLPatterns 而不是 XPath。 MSXML 版本之间的一些差异的详细信息是here 并讨论了两种选择语言之间的差异here。”

    在当前的 MSXML2 版本 6.0 中,完全支持 XPath 1.0。所以看来XSL Patterns早些年微软就已经实现了,基本上可以看成是W3C对XPath进行标准化之前的XPath表达式的简化子集。

    MSXML2 版本 3.0 至少通过显式选择语言设置允许集成 XPath 1.0:

    oXML.setProperty "SelectionLanguage", "XPath"   ' oXML being the DOMDocument object as used in original post  
    

    【讨论】:

    • .LoadXML ("C:\folder\folder\name.xml") 和 .Load ("C:\folder\folder\name.xml") 有什么区别???我将我的文件粘贴到“XMLvalidator”中并且没有错误,所以,你到底是什么意思:“
    • 所有这些。所有的 XML 都完全相同,只是改变了文件的名称,因此也改变了 ,以及每个 Query 的内容。始终是 EN、DE、FR
    • 使用 MSXML v3.0 的另一个问题是默认选择语言是 XSLPatterns 而不是 XPath。有关 MSXML 版本之间的一些差异的详细信息是here,两种选择语言之间的差异在 here 中讨论
    • 感谢您提供附加链接并更准确地说明差异;我只在代码中评论说版本 3 中需要明确的 .setProperty "SelectionLanguage", "XPath" - 我可以在我的答案中包含它吗? - @barrowc
    • 非常欢迎您在答案中包含我的评论的任何部分
    【解决方案2】:

    这是特殊字符(德语字母),意味着您需要对 XML 文件进行批量替换,因此开头的行不是这样的:

    <?xml version="1.0" encoding="UTF-8"?>
    

    但是这个:

    <?xml version="1.0" encoding="iso-8859-1" ?>
    

    要测试的代码:

    Option Explicit
    Public Sub test()
        Dim xmlDoc As Object
        Set xmlDoc = CreateObject("MSXML2.DOMDocument") 'New MSXML2.DOMDocument60
        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
        Debug.Print xmlDoc.SelectNodes("//Query").Length
    End Sub
    

    这是我正在使用的 XML:

    <?xml version="1.0" encoding="iso-8859-1" ?>
      <Concepts>
          <ConceptModel name="food">
        <Filters>
          <Filter type="CC"/>
        </Filters>
        <Queries>
          <Query lang="EN">(cheese, bread, wine)</Query>
          <Query lang="DE">(Käse, Brot, Wein)</Query>
          <Query lang="FR">(fromaige, pain, vin)</Query>
       </Queries>
      </ConceptModel>
    </Concepts>
    

    【讨论】:

    • 但这意味着如果没有“ü”、“ä”或其他任何类似的东西,它应该可以工作。它没有。不幸的是我不能影响文件本身的内容,我只能读取数据。
    • 我不确定,但我可以向您保证,它确实适用于上述更改。可能还有其他方法可以实现相同的目的,例如首先使用不同的方法读取并将其解析为 xmldocument。
    • 仅供我理解。我做了以下。我在文本编辑器中打开了 xml 文件,并将头部更改为(如您所说): 保存并再次运行我的代码。同样的结果。是这个意思吗?
    • 另一个尝试:我从文本编辑器保存一个 XML 文件,没有任何头部指定编码,并将它的 XML 扩展名保存为文本文件。 (并且没有德语特殊字符)。也没有用
    • 现在我做到了。你的工作。不仅如此,我把最初的like改成包含UFT的那个,然后报错了。
    猜你喜欢
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 2017-04-20
    • 1970-01-01
    • 2017-04-22
    • 2011-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多