2012-06-26 28 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

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