2012-06-26 25 views
2

我想使用XPath 1.0从以下xml中识别重复的序列号,然后使用XPathNavigator在.Net中对其进行评估。如何使用XPathNavigator评估XPath 1.0中的重复节点?

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

我试图在一个定制的XSLT上下文功能评估此

//Items/Item/SerialNumber 

表达(实施IXsltContextFunctionlike this MSDN example)在.net中,但调用函数来做到这一点在同一时间被调用一个结果,所以我没有其他结果的可见性来查找重复项。

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

OR

2)是否有传递元件的阵列到定制的XSLT上下文功能类的单个调用呼叫的方法吗?我在VB.Net工作,但很高兴任何人都可以分享的C#示例。

感谢,

加文

编辑

多亏至O R映射和Dimitre对他们的答复。我最初接受O R Mapper的回应,因为它确实按照我的要求做了。因为我喜欢它,所以我接受了Dimitre的答案,因为它提供了一个明确的值列表。虽然这两个回应非常有帮助

+0

加文·萨瑟兰:你知道,目前公认的答案是不正确的?对于提供的XML文档,它选择一个节点。但是,如果有超过两个具有相同字符串值的元素(假设有三个或更多' 1112'),那么XPath表达式将选择每个副本,但第一个除外。因此,如果有10个元素< 1112,则表达式选择9个文本节点“1112”。在我看来,你只需要选择一个“1112”文本节点。 –

+0

@DimitreNovatchev:该问题询问如何找到重复的序列号。所以,如果有10个元素' 1112',那么其中的9个元素是重复的。因此,最初接受的答案正是所要求的。这个问题并没有说明各个节点会发生什么,所以没有理由自动假设OP不希望在文档中出现重复节点的完整列表。 –

+0

@ORMapper:是的,这就是为什么在我的评论中,我问我的猜测是否正确 - 事实证明我是......根据我的经验,“现实世界”的问题通常意味着与他们所说的不同的东西 - 我们需要接受这一事实并适应。毕竟,发展自己的猜测能力并不是什么坏事。 –

回答

3

使用

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

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

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() 
+0

优秀的答案@Dimitre。初始/ */* /代表库存和物料节点吗? –

+0

@GavinSutherland:是的,因为我们知道XML文档的结构,我们知道只有/这些元素可以通过'/ */*'来选择 - 这是一个方便的捷径,它的效率稍高一点,测试。 –

4

我要回答1),所以2)不应该的问题更多:

您可以使用preceding-sibling轴您<Item>元素找到任何前述<Item>元素与相同的序列号。

试试这个(编写,以便它仅返回序列号本身,而不是元素 - 如果这不是你想要很什么,你不知道如何改变的结果,让我知道):

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

为您的样品文件,它返回

1112 
+0

太棒了。做我需要的。谢谢! –