使用XSLT选择唯一元素

4

我有以下XML:

<option>
    <title>ABC</title>
    <desc>123</desc>
</option>
<option>
    <title>ABC</title>
    <desc>12345</desc>
</option>
<option>
    <title>ABC</title>
    <desc>123</desc>
</option>
<option>
    <title>EFG</title>
    <desc>123</desc>
</option>
<option>
    <title>EFG</title>
    <desc>456</desc>
</option>

使用 XSLT,我想将其转换为:
<choice>
    <title>ABC</title>
    <desc>123</desc>
    <desc>12345</desc>
</choice>
<choice>
    <title>EFG</title>
    <desc>123</desc>
    <desc>456</desc>
</choice>

好问题(+1)。请查看我的答案,其中包含一个最小的(19行)XSLT 2.0解决方案。 :) - Dimitre Novatchev
3个回答

2
我建议您研究“分组”来解决这个问题。可以使用 XSLT 2.0 中的内置分组函数,例如 for-each-group,或者如果您使用的是 XSLT 1,则可以使用称为“Muenchian Grouping”的技术。请注意保留 HTML 标签。

这解决了我三个小时的问题,非常感谢,for-each-group 很好用。 - AJ.

1

这里是一个最简XSLT 2.0解决方案:

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

 <xsl:template match="/*">
  <choices>
   <xsl:for-each-group select="*/title" group-by=".">
     <choice>
       <title>
         <xsl:sequence select="current-grouping-key()"/>
       </title>
       <xsl:for-each-group select="current-group()/../desc" group-by=".">
         <xsl:sequence select="."/>
       </xsl:for-each-group>
     </choice>
   </xsl:for-each-group>
  </choices>
 </xsl:template>
</xsl:stylesheet>

请注意使用函数current-group()current-grouping-key()

@Dimitre,你能详细解释一下<xsl:sequence ... />吗?它的目的是什么?我从未使用过它,也从未感到自己缺少了什么,但也许使用它可以帮我省去一些麻烦? - topskip
@Patrick:<xsl:sequence>类似于<xsl:copy-of>,但它不会创建所选节点的副本。相反,它会产生类似于这些节点的“引用”的东西。总之,<xsl:sequence><xsl:copy-of>更经济高效。 - Dimitre Novatchev

0

你已经得到了很好的答案。为了简洁起见,我提供了一个基于Dimitres answer的16行解决方案:

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

  <xsl:template match="/*">
    <choices>
      <xsl:for-each-group select="option" group-by="title">
        <choice>
          <xsl:sequence select="title"/>
          <xsl:for-each-group select="current-group()/desc" group-by=".">
            <xsl:sequence select="."/>
          </xsl:for-each-group>
        </choice>
      </xsl:for-each-group>
    </choices>
  </xsl:template>
</xsl:stylesheet>

请注意,在for-each-group中,当前上下文节点是当前组中的第一项,而current-group()返回当前组中所有项的列表。我利用了输入和输出的title元素相同的事实,并复制每个组中的第一个标题。
为了完整起见,这里提供使用Muenchian分组的XSLT 1.0解决方案(20行):
<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="title" match="option/title" use="."/>
  <xsl:key name="desc" match="option/desc" use="."/>

  <xsl:template match="/*">
    <choices>
      <xsl:for-each select="option/title[count(.|key('title',.)[1]) = 1]">
        <choice>
          <xsl:copy-of select="."/>
          <xsl:for-each select="key('title',.)/../desc
              [count(.|key('desc', .)[../title=current()][1]) = 1]">
            <xsl:copy-of select="."/>
          </xsl:for-each>
        </choice>
      </xsl:for-each>
    </choices>
  </xsl:template>
</xsl:stylesheet>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接