开发者

Selecting unique elements using XSLT

开发者 https://www.devze.com 2022-12-28 16:22 出处:网络
I have the following XML: <option> <title>ABC</title> <desc>123</desc> </option>

I have the following 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>

Using XSLT, I want to transform it into:

<choice>
    <title>ABC</title>
    <desc>123</desc>
    <desc>12345</desc>
</choice>
<choice>
    <title开发者_JS百科>EFG</title>
    <desc>123</desc>
    <desc>456</desc>
</choice>


I would suggest looking into "grouping" to solve this problem. Either the built-in grouping functions of XSLT 2.0, like for-each-group, or, if you're using XSLT 1, the technique called "Muenchian Grouping".


Here is a minimal XSLT 2.0 solution:

<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>

Do note the use of the functions current-group() and current-grouping-key()


You've gotten good answers already. In the chase of brevity, I present my 16 line solution, based on Dimitres answer:

<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>

Note that the current context node inside for-each-group is the first item in the current group, while current-group() returns the list of all items in the current group. I exploit the fact that the titleelement is identical for input and output, and copy the first title from each group.

And for completeness, the XSLT 1.0 solution using Muenchian grouping (20 lines):

<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>
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号