3

I'm new with xsl . I am having below xml:

    <record>
<fruit>Apples</fruit>
<fruit>Oranges</fruit>
<fruit>Bananas</fruit>
<fruit>Plums</fruit>
<vegetable>Carrots</vegetable>
<vegetable>Peas</vegetable>
<candy>Snickers</candy>

I want to use key function and have an output file:

     <record> 1
--<fruit> 4
--<vegetable> 2
--<candy> 1

Any solutions?

Yusubov
  • 5,815
  • 9
  • 32
  • 69
Do Thanh Tung
  • 1,223
  • 2
  • 19
  • 30
  • Are those hyphens part of the expected output? Do you really want output with unclosed XML tags? – JLRishe Jan 12 '13 at 06:01

2 Answers2

2

As easy as this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
 <xsl:key name="kElemByName" match="*" use="name()"/>

 <xsl:template match=
  "*[not(generate-id()=generate-id(key('kElemByName',name())[1]))]"/>

 <xsl:template match="*">
  <xsl:value-of select=
  "concat('&lt;',name(),'> ', count(key('kElemByName',name())),'&#xA;')"/>
  <xsl:apply-templates select="*"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<record>
    <fruit>Apples</fruit>
    <fruit>Oranges</fruit>
    <fruit>Bananas</fruit>
    <fruit>Plums</fruit>
    <vegetable>Carrots</vegetable>
    <vegetable>Peas</vegetable>
    <candy>Snickers</candy>
</record>

the wanted, correct result is produced:

<record> 1
<fruit> 4
<vegetable> 2
<candy> 1
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
1

To include the hyphens (I will use Dimitre's answer as a base, so please give him the credit he is due), you can do this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:key name="kElemByName" match="*" use="name()"/>

  <xsl:template match=
  "*[not(generate-id()=generate-id(key('kElemByName',name())[1]))]">
    <xsl:apply-templates select="*" />
  </xsl:template>

  <xsl:template match="*">

    <xsl:apply-templates select="ancestor::*" mode="hyphens" />

    <xsl:value-of select=
       "concat('&lt;',name(),'> ', count(key('kElemByName',name())),'&#xA;')"/>
    <xsl:apply-templates select="*"/>
  </xsl:template>

  <xsl:template match="*" mode="hyphens">
    <xsl:text>--</xsl:text>
  </xsl:template>
</xsl:stylesheet>

With the original input, this produces:

<record> 1
--<fruit> 4
--<vegetable> 2
--<candy> 1

With a more deeply nested input,

<record>
  <fruit>
    Apples
  </fruit>
  <fruit>
    <citrus>Grapefruits</citrus>
    <citrus>Oranges</citrus>
    <citrus>Lemons</citrus>
  </fruit>
  <fruit>Bananas</fruit>
  <fruit>
    <pitted>Plums</pitted>
    <pitted>Apricots</pitted>
    <pitted>Peaches</pitted>
  </fruit>
  <vegetable>Carrots</vegetable>
  <vegetable>Peas</vegetable>
  <candy>
    <chocolate>Snickers</chocolate>
    <chocolate>Milky Way</chocolate>
    <chocolate>Hersheys</chocolate>
  </candy>
  <candy>
    <hard>Lozenges</hard>
    <hard>Lollipops</hard>
  </candy>
</record>

You get:

<record> 1
--<fruit> 4
----<citrus> 3
----<pitted> 3
--<vegetable> 2
--<candy> 2
----<chocolate> 3
----<hard> 2

How's that?

JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • +1 for the `` - very elegant, and an idiom I will have to remember for future reference. – Ian Roberts Jan 12 '13 at 15:06
  • Thanks @IanRoberts! Here's another case where I used a similar technique: http://stackoverflow.com/a/14274683/1945651 In this post here, a `--` could've been just as good, but the other thread actually makes use of the template in more than one place. – JLRishe Jan 12 '13 at 21:31