XML解析VBA excel(功能旅行和MSXML2.DOMDocument)

时间:2018-11-30 13:00:24

标签: excel xml vba parsing domdocument

我需要解析数百个具有以下相同结构的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,因为概念有3个子级,即 ConceptModel,Filter Queries 。所以,我更加困惑。

谢谢。

2 个答案:

答案 0 :(得分:2)

尽可能靠近您的OP

我会提醒您注意一些错误或误解:

  • [1] 无效的.LoadXML语法
  

.LoadXML(“ C:\ folder \ folder \ name.xml”)和.Load(“ C:\ folder \ folder \ name.xml”)之间有什么区别?

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

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

  • [2] XML区分大小写,因此节点需要使用其确切的文字名称来寻址:  <Query>节点不会被“ 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(此处为<Concepts>), 您可以改为将.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模式是Microsoft在较早的时候实现的,基本上可以将其视为XPath W3C标准化之前的XPath表达式的简化子集。

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

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

答案 1 :(得分:1)

这是特殊字符(德语字母),这意味着您需要对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>