为什么使用 MSXML v3.0 解析 XML 文档有效,而 MSXML v6.0 不起作用



所以,我正在从事一个项目,该项目根据每个来源的特征,用许多不同的方法从互联网上的许多不同来源抓取和收集数据。

最近添加的是 WebAPI调用,它返回以下XML作为响应:

<?xml version="1.0"?>
<Publication_MarketDocument xmlns="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0">
<mRID>29b526a69b9445a7bb507ba446e3e8f9</mRID>
<revisionNumber>1</revisionNumber>
<type>A44</type>
<sender_MarketParticipant.mRID codingScheme="A01">10X1001A1001A450</sender_MarketParticipant.mRID>
<sender_MarketParticipant.marketRole.type>A32</sender_MarketParticipant.marketRole.type>
<receiver_MarketParticipant.mRID codingScheme="A01">10X1001A1001A450</receiver_MarketParticipant.mRID>
<receiver_MarketParticipant.marketRole.type>A33</receiver_MarketParticipant.marketRole.type>
<createdDateTime>2019-09-19T11:28:51Z</createdDateTime>
<period.timeInterval>
<start>2019-09-18T22:00Z</start>
<end>2019-09-19T22:00Z</end>
</period.timeInterval>
<TimeSeries>
<mRID>1</mRID>
<businessType>A62</businessType>
<in_Domain.mRID codingScheme="A01">10YCS-SERBIATSOV</in_Domain.mRID>
<out_Domain.mRID codingScheme="A01">10YCS-SERBIATSOV</out_Domain.mRID>
<currency_Unit.name>EUR</currency_Unit.name>
<price_Measure_Unit.name>MWH</price_Measure_Unit.name>
<curveType>A01</curveType>
<Period>
<timeInterval>
<start>2019-09-18T22:00Z</start>
<end>2019-09-19T22:00Z</end>
</timeInterval>
<resolution>PT60M</resolution>
<Point>
<position>1</position>
<price.amount>44.08</price.amount>
</Point>
<Point>
<position>2</position>
<price.amount>37.14</price.amount>
</Point>
<Point>
<position>3</position>
<price.amount>32.21</price.amount>
</Point>
<Point>
<position>4</position>
<price.amount>31.44</price.amount>
</Point>
<Point>
<position>5</position>
<price.amount>32.48</price.amount>
</Point>
<Point>
<position>6</position>
<price.amount>45.52</price.amount>
</Point>
<Point>
<position>7</position>
<price.amount>56.05</price.amount>
</Point>
<Point>
<position>8</position>
<price.amount>74.96</price.amount>
</Point>
<Point>
<position>9</position>
<price.amount>74.08</price.amount>
</Point>
<Point>
<position>10</position>
<price.amount>69.03</price.amount>
</Point>
<Point>
<position>11</position>
<price.amount>72.89</price.amount>
</Point>
<Point>
<position>12</position>
<price.amount>68.91</price.amount>
</Point>
<Point>
<position>13</position>
<price.amount>74.95</price.amount>
</Point>
<Point>
<position>14</position>
<price.amount>72.91</price.amount>
</Point>
<Point>
<position>15</position>
<price.amount>75.97</price.amount>
</Point>
<Point>
<position>16</position>
<price.amount>76.49</price.amount>
</Point>
<Point>
<position>17</position>
<price.amount>59.08</price.amount>
</Point>
<Point>
<position>18</position>
<price.amount>60.19</price.amount>
</Point>
<Point>
<position>19</position>
<price.amount>64.69</price.amount>
</Point>
<Point>
<position>20</position>
<price.amount>69.18</price.amount>
</Point>
<Point>
<position>21</position>
<price.amount>64.97</price.amount>
</Point>
<Point>
<position>22</position>
<price.amount>63.38</price.amount>
</Point>
<Point>
<position>23</position>
<price.amount>52.92</price.amount>
</Point>
<Point>
<position>24</position>
<price.amount>48.08</price.amount>
</Point>
</Period>
</TimeSeries>
</Publication_MarketDocument> 

使用Microsoft XML, v6.0成功处理此类情况后,我尝试了以下方法:

Dim respXML As New MSXML2.DOMDocument60
respXML.LoadXML (ThisWorkbook.Worksheets("Sheet2").Range("A1")) 'for the sake of the post's simplicity I'm loading the xml from excel
Debug.Print respXML.getElementsByTagName("price.amount").Length

这应该返回24,但它返回0。 确实有以下几点:

Debug.Print respXML.getElementsByTagName("price.amount")(1) Is Nothing

返回True,这意味着找不到<price.amount></price.amount>元素。但是,Debug.Print respXML.XML会产生预期的结果。

我在某处读到早期绑定可能会导致问题,所以我也尝试了以下内容:

Dim respXML As Object
Set respXML = CreateObject("MSXML2.DOMDocument.6.0")
respXML.LoadXML (ThisWorkbook.Worksheets("Sheet2").Range("A1"))
Debug.Print respXML.getElementsByTagName("price.amount").Length
Debug.Print respXML.getElementsByTagName("price.amount")(1) Is Nothing

结果仍然是一样的。

切换到Microsoft XML, v3.0可以完全解决问题。

但是,我更愿意坚持使用 v6.0,因为它是更积极地维护和支持的 v6.0。

为什么会这样?它与 XML 本身有关吗?它与我的代码有关吗?我错过了什么吗?有没有办法让它与Microsoft XML, v6.0一起工作?

任何意见将不胜感激。

为了扩展@CindyMeister的答案,问题似乎是使用getElementsByTagName()的 MSXML 版本之间的命名空间处理。具体来说,您的 XML 维护一个没有冒号标识前缀的xmlns属性,这需要 DOM 库在解析内容时分配前缀:

<Publication_MarketDocument xmlns="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0" ...

但是,使用SelectionNamespaces+SelectNodes将临时别名(如doc(定义为默认命名空间前缀,两个库都会打印出预期的结果。MS 文档甚至建议使用后一种方法(强调(:

getElementsByTagName方法模拟 提供了反对tagName属性结果的论据IXMLDOMElement.执行时,它不识别或支持 命名空间。相反,您应该使用selectNodes方法,即 在某些情况下速度更快,并且可以支持更复杂的搜索。

MXSML v3.0(打印意外getElementsByTagName结果(

Sub ParseXMLv3()
Dim respXML As New MSXML2.DOMDocument30
respXML.Load "C:PathToInput.xml"
respXML.setProperty "SelectionLanguage", "XPath"
respXML.setProperty "SelectionNamespaces", "xmlns:doc='urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0'"
Debug.Print respXML.SelectNodes("//doc:price.amount").Length       ' PRINTS 24
Debug.Print respXML.SelectNodes("//price.amount").Length           ' PRINTS 0
Debug.Print respXML.getElementsByTagName("price.amount").Length    ' PRINTS 24
Set respXML = Nothing
End Sub

MSXML v6.0

Sub ParseXMLv6()
Dim respXML As New MSXML2.DOMDocument60
respXML.Load "C:PathToInput.xml"
respXML.setProperty "SelectionLanguage", "XPath"
respXML.setProperty "SelectionNamespaces", "xmlns:doc='urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0'"
Debug.Print respXML.SelectNodes("//doc:price.amount").Length       ' PRINTS 24
Debug.Print respXML.SelectNodes("//price.amount").Length           ' PRINTS 0
Debug.Print respXML.getElementsByTagName("price.amount").Length    ' PRINTS 0
Set respXML = Nothing
End Sub

这里的快速测试表明,没有一个节点/元素是使用DOMDocument60拾取的。

我成功地使用了DOMDocument30,仍然使用 MSXML6 解析器。因此,这可能是您的解决方法:

'Using the MSXML6 parser, it's still possible to use what worked in older versions
Dim respXML As Msxml2.DOMDocument30
Set respXML = CreateObject("MSXML2.DOMDocument.3.0")

互联网上的研究发现了两个有用的链接,一个在MSDN上,另一个在VB论坛上。

第一个基本上是说安全属性是在MSXML6中添加的,这意味着在MSXML2中工作的一些东西在较新的版本中不再有效。这些记录在 Microsoft^s 网站上。

我不知道它是哪一个(如果有的话,但最接近的似乎是 SelectionNamespace 属性(,但另一个变化似乎是解析器如何处理"匿名"命名空间(VB 论坛链接(。如果在顶级元素中声明命名空间,没有前缀,则它不会应用于任何子元素 - 因此不会"看到"它们。

由于问题中的 XML 代码包含没有前缀的命名空间,这似乎是问题所在。如果声明DOMDocument30对您不起作用,并且 SelectionNamespace 无济于事,那么我认为唯一的办法是更改/转换 XML 以添加命名空间所有元素的前缀。

最新更新