2010-06-10 138 views
5

我必須從一個XML文檔中選擇唯一記錄,在<xsl:for-each>循環的上下文中。我被Visual Studio限制爲使用XSL 1.0在XSLT/XPath中選擇唯一記錄

<availList> 
     <item> 
      <schDate>2010-06-24</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>13:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-24</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>13:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-25</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>12:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-26</schDate>    
      <schFrmTime>13:00:00</schFrmTime> 
      <schToTime>14:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-26</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>12:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
    </availList> 

的獨特性必須建立在三個子元素的值:schDateschFrmTimeschToTime。如果兩個item元素對於所有三個子元素具有相同的值,則它們是重複的。在上面的XML中,項目1和2是重複的。其餘都是獨一無二的。如上所示,每個項目都包含我們不希望在比較中包含的其他元素。 「唯一性」應該是這三個要素的一個因素,而且僅限於這三個因素。

我試圖通過以下來實現:

availList/item[not(schDate = preceding:: schDate and schFrmTime = preceding:: schFrmTime and schToTime = preceding:: schToTime)] 

這背後的想法是選擇記錄,其中有與同schDateschFrmTimeschToTime沒有前面的元素。但是,它的輸出是錯過了最後一項。這是因爲我的XPath實際上排除了所有子元素值在整個前面的文檔內匹配的項目。沒有一個item匹配所有最後一個項目的子元素 - 但由於每個元素的值分別出現在另一個項目中,最後一個項目被排除。

我能得到通過爲每個先前的項目比較所有子值作爲一個連接字符串相同連接值正確的結果。有人知道我能做到這一點嗎?

+0

好問題(+1)。查看我對XPath和XSLT解決方案的回答。 – 2010-06-10 19:46:38

+1

使用key()的方法通常被稱爲Muenchian方法:http://www.jenitennison.com/xslt/grouping/muenchian.html – 2010-06-11 00:59:17

回答

4

I.作爲一個XPath表達式:

/*/item[normalize-space() and not(. = preceding-sibling::item)] 

II。更爲有效(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:key name="kItemByVal" match="item" use="."/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "*/item[generate-id() = generate-id(key('kItemByVal', .))] 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

兩個I和II中,當所提供的XML文檔施加正確地選擇/複製以下節點

<item><schDate>2010-06-24</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>13:00:00</schToTime></item> 
<item><schDate>2010-06-25</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item> 
<item><schDate>2010-06-26</schDate><schFrmTime>13:00:00</schFrmTime><schToTime>14:00:00</schToTime></item> 
<item><schDate>2010-06-26</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item> 

更新 :如果<item>有其他孩子,那麼這個轉換:

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

    <xsl:key name="kItemBy3Children" match="item" 
    use="concat(schDate, '+', schFrmTime, '+', schToTime)"/> 

<xsl:template match="/"> 
     <xsl:copy-of select= 
     "*/item[generate-id() 
       = generate-id(key('kItemBy3Children', 
           concat(schDate, 
             '+', schFrmTime, 
             '+', schToTime) 
           ) 
          ) 
       ] 
     "/> 
</xsl:template> 
</xsl:stylesheet> 

產生想要的結果

+0

Dimitre, 非常感謝您的回答。儘管如此,我擔心它不適用於我的情況 - 我很抱歉在寫下我的問題時(我後來編輯它)我不是很清楚。問題是,實際上,我的'item'元素還包含各種其他的子元素,這些子元素不應該考慮是否選擇了這些項目。我實際上並不是在尋找「真正的」獨特性,我只在某些子元素值中尋找唯一性。不過,我相信你的答案對其他人來說是有價值的。 丹 – 2010-06-11 00:58:30

+1

@ Daniel-I-S:我已經更新了我的答案,並解決了修改後的問題。 – 2010-06-11 01:28:46

+2

這是一個很好的答案;非常感謝你。 – 2010-06-11 17:04:33

2

我看過的技巧是在兩遍中做到這一點:按三個關鍵字段排序項目,然後將每個項目與其前一項目(而不是所有前面的項目)進行比較。

您是否可以運行兩個獨立的轉換?它使問題變得更容易。

我在舊版本的Michael Kay's XSLT book中看到了該技術。你可以在他的一些示例代碼中找到它。