如何使用XPathNavigator来评估XPath 1.0中的重复节点?

时间:2012-06-26 08:05:05

标签: c# .net vb.net xpath

我正在尝试使用XPath 1.0从以下xml中识别重复的序列号,然后使用XPathNavigator在.Net中对其进行评估。

<?xml version="1.0" encoding="utf-16"?>
<Inventory xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Items>
        <Item>
            <SerialNumber>1111</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>1112</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>1112</SerialNumber>
        </Item>
    </Items>
</Inventory>

我尝试通过评估此

来做到这一点
//Items/Item/SerialNumber
在.Net中的自定义XSLT上下文函数(实现IXsltContextFunction like this MSDN example)中的

表达式,但Invoke函数一次被调用一个结果,因此我无法查看其他结果以查找重复项。

1)有没有办法使用单个XPath 1.0表达式?

OR

2)有没有办法将一个元素数组传递给自定义XSLT上下文函数类的单个Invoke调用?我在VB.Net中工作,但对任何C#都很满意人们可以分享的例子。

谢谢,

加文

修改

感谢O R Mapper和Dimitre的回复。我最初接受了O R Mapper的回复,因为它确实做了我的要求。我已经接受了Dimitre的回答,因为我喜欢它提供了一个独特的值列表。这两个回复都非常有用!

2 个答案:

答案 0 :(得分:4)

我要回答 1),所以 2)应该不再重要:

您可以使用preceding-sibling元素上的<Item>轴来查找具有相同序列号的任何前面的<Item>元素。

试试这个(编写它只返回序列号而不是元素 - 如果这不是你想要的,你不知道如何更改结果,请告诉我):

/Inventory/Items/Item/SerialNumber/node()[.=../../preceding-sibling::Item/SerialNumber/node()]

对于您的示例文档,它将返回

1112

答案 1 :(得分:3)

使用

/*/*/Item
      [SerialNumber = following-sibling::Item/SerialNumber
     and
       not(SerialNumber = preceding-sibling::Item/SerialNumber)
      ]

这为只有Item子元素且具有相同字符串值的Item个元素组选择一个SerialNumber元素。

基于XSLT的验证

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

 <xsl:template match="/">
     <xsl:copy-of select=
      "/*/*/Item
          [SerialNumber = following-sibling::Item/SerialNumber
         and
           not(SerialNumber = preceding-sibling::Item/SerialNumber)
          ]"/>
 </xsl:template>
</xsl:stylesheet>

对此XML文档应用此转换时(基于提供的转换,但更有趣):

<Inventory>
    <Items>
        <Item>
            <SerialNumber>1111</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>2222</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>2222</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>2222</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>1111</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>1111</SerialNumber>
        </Item>
        <Item>
            <SerialNumber>3333</SerialNumber>
        </Item>
    </Items>
</Inventory>

转换会评估XPath表达式并将所选节点复制到输出中:

<Item>
   <SerialNumber>1111</SerialNumber>
</Item>
<Item>
   <SerialNumber>2222</SerialNumber>
</Item>

最后,如果您只想获得SerialNumber重复值,请使用

   /*/*/Item
          [SerialNumber = following-sibling::Item/SerialNumber
         and
           not(SerialNumber = preceding-sibling::Item/SerialNumber)
          ]
           /SerialNumber/text()