VBA DOM ParentNode SelectSingleNode

VBA DOM ParentNode SelectSingleNode

<?xml version="1.0"?>
<catalog>
<book id="Adventure">
   <author>Gambardella, Matthew</author>
   <title>XML Developer's Guide</title>
   <price>44.95</price>
</book>
<book id="Adventure">
   <author>Ralls, Kim</author>
   <title>Midnight Rain</title>
   <price>5.95</price>
</book>
<book id="Adventure">
   <author>Boal, John</author>
   <title>Mist</title>
   <price>15.95</price>
</book>
<book id="Mystery">
   <author>Ralls, Kim</author>
   <title>Some Mystery Book</title>
   <price>9.95</price>
</book>
</catalog>

我的程序搜索特定作者 ("Ralls, Kim") 的所有项目,同时收集 BookType ("book id") 并将值放入数组中。

但现在的问题是图书 ID 并不总是相同。因此,由于我已经确定了父节点是什么,所以我想取出 BookTitle - 这样我就不必在 XPATH 中放置一个已经存储在数组中的变量(这对我来说听起来像是一场噩梦).

Sub mySub()

Dim XMLFile As Variant
Dim Author As Variant
Dim athr As String, BookType As String, Title As String
Dim AuthorArray() As String, BookTypeArray() As String, TitleArray() As  String
Dim i As Long, x As Long, j As Long

Dim mainWorkBook As Workbook
Dim n As IXMLDOMNode
Set mainWorkBook = ActiveWorkbook
Set XMLFile = CreateObject("Microsoft.XMLDOM")
XMLFile.Load ("C:\Books.xml")


x = 1
j = 0

Set Author = XMLFile.SelectNodes("/catalog/book/author/text()")
For i = 0 To (Author.Length - 1)
    ReDim Preserve AuthorArray(0 To i)
    ReDim Preserve BookTypeArray(0 To i)
    ReDim Preserve TitleArray(0 To i)
    athr = Author(i).NodeValue
    BookType = Author(i).ParentNode.ParentNode.getAttribute("id")
    Title = Author(i).ParentNode.SelectSingleNode("title")

    If athr = "Ralls, Kim" Then

        AuthorArray(j) = athr
        BookTypeArray(j) = BookType
        TitleArray(j) = Title

        j = j + 1
        x = x + 1
    End If
Next



Range("A3:A" & UBound(AuthorArray) + 1) = WorksheetFunction.Transpose(AuthorArray)
Range("B3:B" & UBound(BookTypeArray) + 1) = WorksheetFunction.Transpose(BookTypeArray)
Range("C3:C" & UBound(BookTypeArray) + 1) = WorksheetFunction.Transpose(TitleArray)


End Sub

我在这行代码中不断收到对象错误,我不明白为什么:

Title = Author(i).ParentNode.SelectSingleNode("title")

感谢您的帮助和建议。

不熟悉 VBA,但您很可能收到错误,因为这一行

Set Author = XMLFile.SelectNodes("/catalog/book/author/text()")

Author 变量 (?) 设置为 字符串 值,text() 表示所有作为上下文直接子节点的文本节点的串联元素。字符串与元素有很大不同,它们没有属性,也没有子元素。

他们确实有一个父节点,但是

BookType = Author(i).ParentNode.getAttribute("id")

将尝试检索此类文本节点的父节点的 id 属性,这是一个 author 元素 - 但 author 元素没有 id属性。

改变

Set Author = XMLFile.SelectNodes("/catalog/book/author/text()")

Set Author = XMLFile.SelectNodes("/catalog/book/author")

如果有帮助请告诉我。


编辑:我想我找到了一个可行的解决方案,下面的怎么样。但是,我完全不熟悉 VBA - 我确信这不是一个非常优雅的解决方案,可以改进很多。

Sub mySub()

Dim XMLFile As MSXML2.DOMDocument
Dim Author As Variant
Dim athr As String, BookType As String, Title As String
Dim AuthorArray() As String, BookTypeArray() As String, TitleArray() As String
Dim i As Long, x As Long, j As Long

Dim mainWorkBook As Workbook
Dim n As IXMLDOMNode
Set mainWorkBook = ActiveWorkbook
Set XMLFile = CreateObject("Microsoft.XMLDOM")
XMLFile.Load ("vba.xml")


x = 1
j = 0

Set Author = XMLFile.SelectNodes("/catalog/book/author")
For i = 0 To (Author.Length - 1)
    ReDim Preserve AuthorArray(0 To i)
    ReDim Preserve BookTypeArray(0 To i)
    ReDim Preserve TitleArray(0 To i)
    athr = Author(i).Text
    BookType = Author(i).ParentNode.getAttribute("id")
    Title = Author(i).ParentNode.getElementsByTagName("title").Item(0).nodeTypedValue

    If athr = "Ralls, Kim" Then

        AuthorArray(j) = athr
        BookTypeArray(j) = BookType
        TitleArray(j) = Title

        j = j + 1
        x = x + 1
    End If
Next



Range("A3:A" & UBound(AuthorArray) + 1) = WorksheetFunction.Transpose(AuthorArray)
Range("B3:B" & UBound(BookTypeArray) + 1) = WorksheetFunction.Transpose(BookTypeArray)
Range("C3:C" & UBound(BookTypeArray) + 1) = WorksheetFunction.Transpose(TitleArray)


End Sub

结果

Ralls, Kim  Adventure   Midnight Rain
Ralls, Kim  Mystery     Some Mystery Book

第二次编辑

can you please explain what Item(0) is doing in this line Title = Author(i).ParentNode.getElementsByTagName("title").Item(0).nodeTypedValue?

getElementsByTagName() returns 元素的 集合 ,即使只有一个结果 - 它最终会出现在只有一个项目的集合中。 Item(0) 选择此集合中的第一个元素。

然后,nodeTypedValue returns the value of an object, depending on its data type. By the way, the NodeValue property only works on attributes - 这就是您的代码返回错误的原因。

Also, is there ever a need for using /text() within the XPath to the node (it doesn't seem to affect numbers or anything either)?

(终于有了让我满意的东西!我想您会从 XPath 的介绍或教程中获益。)

text() 一个被过度使用的构造,但是,是的,它有很多用途。可以这样想:当 XML 被读入内存时,就构建了一个树状表示。在这种表示中,各种对象都是节点;元素是节点,属性是节点 - 文本内容是节点。

现在,像

这样的 XPath 表达式
/catalog/book/author

选择一个元素节点(或者可能是一组元素节点)。在您的例子中,它是一个元素节点,它有一个文本节点作为其子节点。将路径表达式扩展为

/catalog/book/author/text()

仅选择 author 个元素的子文本节点。了解元素与其文本内容不同这一点很重要。