开发者

XSLT group by value

I have XML document

<Contracts>   
    <Cont开发者_开发技巧ract>
        <F1>key1</F1>
        <F2>2345</F2>
        <F3>val</F3>
        <F4>dal</F4>
    </Contract>
    <Contract>
        <F1>key1</F1>
        <F2>34562</F2>
        <F3>val</F3>
        <F4>dal</F4>
    </Contract>
    <Contract>
        <F1>key2</F1>
        <F2>416235</F2>
        <F3>val</F3>
        <F4>sal</F4>
    </Contract> 
    <Contract>
        <F1>key2</F1>
        <F2>416235</F2>
        <F3>val</F3>
        <F4>sal</F4>
    </Contract> 
    <Contract>
        <F1>key2</F1>
        <F2>41575</F2>
        <F3>val</F3>
        <F4>bal</F4>
    </Contract>
</Contracts>

I need group these contracts by Contracts/Contract/F1 (key1, key2) to get this result:

<Contracts>
    <Set>
        <Contract>
            <F1>key1</F1>
            <F2>2345</F2>
            <F3>val</F3>
            <F4>dal</F4>
        </Contract>
        <Contract>
            <F1>key1</F1>
            <F2>34562</F2>
            <F3>val</F3>
            <F4>dal</F4>
        </Contract>
    <Set>
    <Set>
        <Contract>
            <F1>key2</F1>
            <F2>416235</F2>
            <F3>val</F3>
            <F4>sal</F4>
        </Contract> 
        <Contract>
            <F1>key2</F1>
            <F2>416235</F2>
            <F3>val</F3>
            <F4>sal</F4>
        </Contract> 
        <Contract>
            <F1>key2</F1>
            <F2>416235</F2>
            <F3>val</F3>
            <F4>sal</F4>
        </Contract>
   </Set>
</Contracts>

How can I do it with XSLT ?


I. XSLT 1.0 solution (Muenchian grouping, no xsl:for-each):

<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="kContrByF1" match="Contract" use="F1"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "Contract[not(generate-id()
               =
                generate-id(key('kContrByF1', F1)[1])
                )
            ]"/>

 <xsl:template match="Contract">
  <Contract>
   <Set>
    <xsl:copy-of select="key('kContrByF1', F1)"/>
   </Set>
  </Contract>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<Contracts>
    <Contract>
        <F1>key1</F1>
        <F2>2345</F2>
        <F3>val</F3>
        <F4>dal</F4>
    </Contract>
    <Contract>
        <F1>key1</F1>
        <F2>34562</F2>
        <F3>val</F3>
        <F4>dal</F4>
    </Contract>
    <Contract>
        <F1>key2</F1>
        <F2>416235</F2>
        <F3>val</F3>
        <F4>sal</F4>
    </Contract>
    <Contract>
        <F1>key2</F1>
        <F2>416235</F2>
        <F3>val</F3>
        <F4>sal</F4>
    </Contract>
    <Contract>
        <F1>key2</F1>
        <F2>41575</F2>
        <F3>val</F3>
        <F4>bal</F4>
    </Contract>
</Contracts>

the wanted, correct result is produced:

<Contracts>
   <Contract>
      <Set>
         <Contract>
            <F1>key1</F1>
            <F2>2345</F2>
            <F3>val</F3>
            <F4>dal</F4>
         </Contract>
         <Contract>
            <F1>key1</F1>
            <F2>34562</F2>
            <F3>val</F3>
            <F4>dal</F4>
         </Contract>
      </Set>
   </Contract>
   <Contract>
      <Set>
         <Contract>
            <F1>key2</F1>
            <F2>416235</F2>
            <F3>val</F3>
            <F4>sal</F4>
         </Contract>
         <Contract>
            <F1>key2</F1>
            <F2>416235</F2>
            <F3>val</F3>
            <F4>sal</F4>
         </Contract>
         <Contract>
            <F1>key2</F1>
            <F2>41575</F2>
            <F3>val</F3>
            <F4>bal</F4>
         </Contract>
      </Set>
   </Contract>
</Contracts>

II. XSLT 2.0 solution:

 <xsl:template match="/*">
     <Contracts>
       <xsl:for-each-group select="Contract" group-by="F1">
         <Contract>
          <Set>
            <xsl:sequence select="current-group()"/>
          </Set>
         </Contract>
       </xsl:for-each-group>
     </Contracts>
 </xsl:template>
</xsl:stylesheet>

Again, when this transformation is applied on the same XML document (above) the same correct result is produced.

Do note:

  1. The use of the xsl:for-each-group instruction.

  2. The use of the current-group() function.


Use an XSL key and Muenchian grouping.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:key name="kContract" match="Contract" use="F1" />

  <xsl:template match="Contracts">
    <xsl:copy>
      <xsl:for-each select="
        Contract[
          generate-id() = generate-id(key('kContract', F1)[1])
        ]
      ">
        <Set>
          <xsl:copy-of select="key('kContract', F1)" />
        </Set>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

This stylesheet produces exactly the output you want.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜