2010-06-10 136 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中看到了该技术。你可以在他的一些示例代码中找到它。