在XSLT/XPath中选择唯一记录

Dan*_*ake 5 xml xslt xpath transform

我必须在循环的上下文中仅从XML文档中选择唯一记录<xsl:for-each>.我被Visual Studio限制为使用XSL 1.0.

    <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>
Run Code Online (Sandbox Code Playgroud)

唯一性必须建立在三个子元素的值schDate,schFrmTimeschToTime.如果两个item元素对于所有三个子元素具有相同的值,则它们是重复的.在上面的XML中,第一项和第二项是重复的.其余的都是独特的.如上所述,每个项目包含我们不希望包括在比较中的其他元素."独特性"应该是这三个要素的一个因素,而仅仅是那些要素.

我试图通过以下方式实现这一目标:

availList/item[not(schDate = preceding:: schDate and schFrmTime = preceding:: schFrmTime and schToTime = preceding:: schToTime)]
Run Code Online (Sandbox Code Playgroud)

这背后的想法是选择没有前面元素的记录schDate,schFrmTime并且schToTime.但是,它的输出缺少最后一项.这是因为我的XPath实际上排除了所有子元素值在整个前一个文档中匹配的项目.没有单个item匹配所有最后一个项目的子元素 - 但由于每个元素的值单独存在于另一个项目中,因此最后一个项目将被排除.

通过将所有子值作为连接字符串每个前面项的相同连接值进行比较,我可以得到正确的结果.有人知道我能做到这一点吗?

Dim*_*hev 4

I. 作为单个 XPath 表达式:

/*/item[normalize-space() and not(. = preceding-sibling::item)]
Run Code Online (Sandbox Code Playgroud)

二. 使用键更高效(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>
Run Code Online (Sandbox Code Playgroud)

当应用于提供的 XML 文档时,I 和 II 都正确选择/复制以下节点

<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>
Run Code Online (Sandbox Code Playgroud)

更新:如果<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>
Run Code Online (Sandbox Code Playgroud)

产生想要的结果