在C#.NET 3.5中使用XPath比较不同层次结构级别的两个属性

时间:2011-10-06 14:46:07

标签: .net xpath comparison

我有以下XML:

<A>
  <B>
     <C Since="2011-09-26T11:12:41.1383089Z">
       <E Name="One" AnotherDate="2011-09-26T10:54:05.7025781Z"/>
       <E Name="Two" AnotherDate="2011-09-26T11:54:05.7025781Z"/>
     </C>
  </B>
</A>

我的Xpath表达式如下所示:

//A/B/C/E[@AnotherDate <= ../@Since]

这适用于XMLSpy,也适用于T-SQL 2008查询

where X.exists(xpathexpression)=1 

但不是在带有XmlDocument.SelectNodes()的.NET 3.5中。

据我所知,xpath文档可以在XPATH 1.0中支持该查询,.NET支持该版本。

我想要实现的目标: 我想选择 E 的所有元素 E 早于或等于其父元素 C 属性。

所以:我做错了什么,或者我可以改变什么来实现类似的东西。 请注意,查询也应该在给定的sql where子句中起作用。

2 个答案:

答案 0 :(得分:1)

这非常简单。使用

  /A/B/C/E
     [not(translate(@AnotherDate, '-:TZ', '')
         >
          translate(../@Since, '-:TZ', '')
          )
      ]

基于XSLT的验证

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "/A/B/C/E
         [not(translate(@AnotherDate, '-:TZ', '')
             >
              translate(../@Since, '-:TZ', '')
              )
          ]
     "/>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档

<A>
    <B>
        <C Since="2011-09-26T11:12:41.1383089Z">
            <E Name="One" AnotherDate="2011-09-26T10:54:05.7025781Z"/>
            <E Name="Two" AnotherDate="2011-09-26T11:54:05.7025781Z"/>
        </C>
    </B>
</A>

将所需的正确结果(仅选定的一个节点)复制到输出

<E Name="One" AnotherDate="2011-09-26T10:54:05.7025781Z" />

解释:使用标准XPath 1.0函数 translate() 从日期时间值中删除所有非数字字符,以便剩下的全部然后可以将数字字符串正确地作为数字进行比较。

答案 1 :(得分:0)

在我看来,这符合the specification的行为:

  

当要比较的对象都不是节点集并且运算符是&lt; =,&lt;,&gt; =或&gt;时,则通过将两个对象转换为数字并根据IEEE比较数字来比较对象。 754.

将字符串转换为数字:

  

一个字符串,由可选的空格后跟一个可选的减号,后跟一个数字后跟空格,转换为最接近的IEEE 754数字(根据IEEE 754舍入到最接近的规则)到数学值由字符串表示;任何其他字符串都转换为NaN

因此,在您的情况下,比较两个NaN,返回false。因此,不返回任何节点。在XPath 2.0中,行为有所不同,这可能是它在其他环境中工作的原因。

我认为你不能只使用XPath 1.0选择这样的节点。所以,我认为你不能仅仅通过使用单SelectNodes()来实现。如果你想使用单个XPath 1.0查询实现这一点,你必须克服它的局限性。请参阅Dimitre的答案如何做到这一点。