本地窗口中的XML树与文件中的XML不匹配

时间:2014-06-02 17:09:38

标签: xml vba dom

我有一个XML程序found here,它接受​​一个标记的特定子节点,并将其作为另一个标记的子节点。部分代码涉及告诉VBA如何遍历树,这非常有意义。问题是,当我在Visual Basic编辑器的“Locals”窗口中实际打开树结构时,我发现自己无法访问从实际XML文件中看起来清晰可见的树部分。我正在使用的XML类似于以下块:

<Root> 
<Results xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Reference>{REFERENCE-HERE}</Reference>
      <FillerTags>Filler</FillerTags>
      <entity>
        <entityName>ABC</entityName>
        <entityId>012345</entityId>
      </entity>
      <Items>
        <Item>
          <entityId>012345</entityId>
          <FillerTagsAgain1>Filler2</FillerTagsAgain1>
          <FillerTagsAgain2>Filler2</FillerTagsAgain2>
          <FillerTagsAgain3>Filler2</FillerTagsAgain3> 
         </Item>
         <Item> 
           <entityId>012345</entityId>
           <FillerTagsAgain1>Filler2</FillerTagsAgain1>
           <FillerTagsAgain2>Filler2</FillerTagsAgain2>
           <FillerTagsAgain3>Filler2</FillerTagsAgain3> 
         </AnotherItem>
       </Items>
       <Contents>
       <MoreFiller>asdf</MoreFiller>
       </Contents>
   </Results>
   <Results>
     <entity>
      <entityName>DEF</entityName>
        <entityId>678910</entityId>
      </entity>
      <Items>
        <Item>
          <entityId>678910</entityId>
          <FillerTagsAgain1>Filler2</FillerTagsAgain1>
          <FillerTagsAgain2>Filler2</FillerTagsAgain2>
          <FillerTagsAgain3>Filler2</FillerTagsAgain3> 
         </Item>
         <Item> 
           <entityId>678910</entityId>
           <FillerTagsAgain1>Filler2</FillerTagsAgain1>
           <FillerTagsAgain2>Filler2</FillerTagsAgain2>
           <FillerTagsAgain3>Filler2</FillerTagsAgain3> 
         </Item>
       </Items> 
      <Contents>
      <MoreFiller>asdf</MoreFiller>
     </Contents>  
   </Results>
</Root>

包含项目和内容只是为了澄清块下面有多个块。这里'entity'是Customer,Item是Transaction,Contents是Indicators。

例如,要复制的节点是“客户”的子节点。客户有几个兄弟姐妹,称他们为Sibling1 = Transactions = ItemsSibling2 = Indicators = ContentsSibling3 = Validation(不在示例XML中)。当我打开本地窗口时,我从Customer的子节点开始。我想访问Sibling1的孩子们。为此,我使用路由Customer.ParentNode.NextSibling.ChildNodes,代码运行正常。但是,如果我将其更改为Customer.ParentNode.NextSibling.NextSibling.ChildNodes VBA表明Sibling3实际上是Sibling2。也就是说,路径显示Validation而非Indicators。但是,查看实际的XML文件,似乎很明显指标在Transactions之后。有没有明确的理由可能会出现这种情况?

编辑:下面是我引用的树结构图片以及引用它的代码。

enter image description here

For Each Customer In DOM.DocumentElement.getElementsByTagName("CustomerId")
   'Since Indicators is Customer nextSibling, and Customer is parent of CustomerId,'
   ' we can iterate the collection if its childNodes like this:'
   For Each itm In Customer.ParentNode.NextSibling.ChildNodes
        If itm.HasChildNodes Then
            '# Insert this node before the first child node of Indicators'
            itm.InsertBefore Customer.CloneNode(True), itm.FirstChild
        Else
            '# Append this node to the Indicators'
            itm.appendChild Customer.CloneNode(True)
        End If
    Next
Next

其中customer是XML节点,而Transactions是它的第一个兄弟。参考示例XML Customer是实体,Transactions是Items。

假设我想要到达第二个兄弟姐妹。我希望路径将是(考虑上面的示例代码)`Customer.ParentNode.NextSibling.NextSibling.ChildNodes。但是,如果我尝试这个,我收到一个“找不到对象”错误,树看起来像这样:

enter image description here

注意:在上面的两张图片中,我已从中间列中删除了信息,以便某些节点的值保密。

核心问题是'验证'实际上不是交易的下一个兄弟。指标是交易的下一个兄弟,在示例XML中扮演<Contents>的角色。

再次为了使这个帖子自成一体,我使用的代码如下:

Option Explicit
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Sub ParseResults()
'Most up-to-date copy of XML Parsing Macro
'Requires reference to Microsoft XML, v6.0
'Requires referenc to Microsoft Scripting Runtime
Dim xmlFilePath$, newFilePath$
Dim DOM As MSXML2.DOMDocument
Dim Customer As IXMLDOMNode
Dim fso As Scripting.FileSystemObject
Dim itm As IXMLDOMNode

'# Define the file you are going to load as XML
xmlFilePath = "C:\FAKEPATH\FAKEFILE.xml"

'# Define an output path for where to put the modified XML
newFilePath = "C:\FAKEPATH\FAKEFILE.xml"

'# Create our DOM object
Set DOM = CreateObject("MSXML2.DOMDocument.6.0")

'# Load the XML file
DOM.Load xmlFilePath

'# Wait until the Document has loaded
Do
    Sleep 250
Loop Until DOM.readyState = 4

'##### NO LONGER USED:'
'# Get the CustomerId node'
'Set Customer = DOM.DocumentElement.getElementsByTagName("CustomerId")(0)'

'# Instead of getting the first Transaction like we did before, we can iterate the collection'
' of nodes with the CustomerId tag like so:'
For Each Customer In DOM.DocumentElement.getElementsByTagName("CustomerId")
   'Since Transaction is Customer nextSibling, and Customer is parent of CustomerId,'
   ' we can iterate the collection if its childNodes like this:'
   For Each itm In Customer.ParentNode.NextSibling.ChildNodes
        If itm.HasChildNodes Then
            '# Insert this node before the first child node of Transaction'
            itm.InsertBefore Customer.CloneNode(True), itm.FirstChild
        Else
            itm.appendChild Customer.CloneNode(True)
            '# Append this node to the Transaction'
        End If
    Next
Next


'##### This function call is no longer needed
'AppendCustomer DOM, "Transaction", Customer'

'##### This function call is no longer needed
'AppendCustomer DOM, "Transaction", Customer'

'## Create an FSO to write the new file
Set fso = CreateObject("Scripting.FileSystemObject")

'## Attempt to write the new/modified XML to file
On Error Resume Next
'MsgBox DOM.XML
fso.CreateTextFile(newFilePath, True, False).Write DOM.XML
If Err Then
    '## Print the new XML in the Immediate window
    Debug.Print DOM.XML
    MsgBox "Unable to write to " & newFilePath & " please review XML in the Immediate window in VBE.", vbInformation
    Err.Clear
End If
On Error GoTo 0
'Cleanup
Set DOM = Nothing
Set fso = Nothing
Set Customer = Nothing

End Sub

Sub AppendCustomer(DOM As Object, Transaction As String, copyNode As Object)
'## This subroutine will append child node to ALL XML Nodes matching specific string tag.
Dim TransactionColl As IXMLDOMNodeList
Dim itm As IXMLDOMNode

'# Get a collection of all elements matching the tagName
Set TransactionColl = DOM.DocumentElement.getElementsByTagName(Transaction)

'# Iterate over the collection, appending the copied node
For Each itm In TransactionColl
    If itm.HasChildNodes Then
        '# Insert this node before the first child node of Transaction
        itm.InsertBefore copyNode.CloneNode(True), itm.FirstChild
    Else
        '# Append this node to the Transaction
        itm.appendChild copyNode.CloneNode(True)
    End If
Next

Set itm = Nothing
Set TransactionColl = Nothing

End Sub

1 个答案:

答案 0 :(得分:1)

假设您的xml看起来像这样(为了清楚起见,省略了xml命名空间和内容):

<root>
    <Customer>
        <child1>asdf</child1>
        <child2>sdfg</child2>
        ...
    </Customer>
    <Sibling1>
        ...
    </Sibling1>
    <Sibling2>
        ...
    </Sibling2>
    <Sibling3>
        ...
    </Sibling3>
</root>

然后Customer.NextSibling将是Sibling1,Customer.NextSibling.NextSibling将是Sibling2,依此类推。

使用ParentNode访问Sibling1的唯一原因是当前节点是child1 Customer。然后,您可以使用Sibling1引用child1.ParentNode.NextSibling.ChildNodes的孩子。