VBA DOM ParentNode SelectSingleNode

时间:2015-04-08 22:35:20

标签: xml vba dom xpath xml-parsing

<?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>

我的程序搜索特定作者的所有项目(&#34; Ralls,Kim&#34;),同时收集BookType(&#34;书籍ID&#34;)并将值投入数组。

现在的问题是书的内容并不总是一样的。因为我已经确定了父节点是什么,我想拉出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")

感谢您提供任何帮助和建议。

1 个答案:

答案 0 :(得分:3)

不熟悉VBA,但很可能因为这行

而出现错误
Set Author = XMLFile.SelectNodes("/catalog/book/author/text()")

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

他们有一个父节点,但是

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")

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

如果有帮助,请告诉我。


编辑:我认为我找到了一个有效的解决方案,如下所示。但是,我对VBA完全不熟悉 - 我确信这不是一个非常优雅的解决方案,可以进行很多改进。

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

<强>结果

Title = Author(i).ParentNode.getElementsByTagName("title").Item(0).nodeTypedValue

第二次编辑

  

你可以解释一下Item(0)在这行getElementsByTagName()中做了什么吗?

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

然后,nodeTypedValue返回对象的值,具体取决于其数据类型。顺便说一句,text()属性only works on attributes - 这就是您的代码返回错误的原因。

  

此外,是否需要在XPath中使用/ text()到节点(它似乎不会影响数字或任何东西)?

(最后我很满意!我认为你会从XPath的介绍或教程中获益。)

/catalog/book/author 是一个高度过度使用的构造,但是,它有用处。可以这样想:当XML被读入内存时,会构造一个类似树的表示。在这种表示中,所有类型的对象都是节点;元素是节点,属性是节点 - 文本内容是节点。

现在,像

这样的XPath表达式
/catalog/book/author/text()

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

author

仅选择 {{1}}元素的子文本节点。重要的是要理解元素及其文本内容是不一样的。