Eric V
Eric V

Reputation: 1

XSLT-1.0 | Get first and last node and call specific template

I am relatively new to XLST and this might be a typical beginners problem, but I can't figure it out. Hope you can help and understand me. Thank you in advance. :-)

I try to transform a big XML file to another one, but can't figure out how I can get the first and last node and surround them with other nodes.

The problem is all fruit-nodes are created iteratively.

In my opinion, I should add the surrounding fruits-nodes after all fruit - elements are created.

I already tried something like:

<xsl:template name="tplFruit" match="PARENT_FRUIT">
   <xsl:apply-template select="(//element[@name='fruit'])[1]" />
</xsl:template>

<xsl:template name="addFruits">
   <fruits>
</xsl:template>
<xsl:template name="tplFruit" match="PARENT_FRUIT">
   <xsl:apply-template select="(//element[@name='fruit'])[last()]" />
</xsl:template>

<xsl:template name="addFruits">
   </fruits>
</xsl:template>

But it doesn't work and I am pretty sure another error will occur because I open and close the elements fruits in different templates.

The following xml is given (Input - XML):

<root>
... other elements ...

<PARENT_FRUIT>
   <different_node nr="1" />
   <different_node nr="2" />
   <different_node nr="3" />

   <fruit>
      <name>Apple</name>
      <calorien>999</calorien>
      <color>red</color>
   </fruit>
   <fruit>      
      <name>cherry</name>
      <calorien>999</calorien>
      <color>red</color></fruit>
   <fruit>
      <name>banana</name>
      <calorien>999</calorien>
      <color>red</color>
   </fruit>

   <different_node nr="4" />
   <different_node nr="5" />
   <different_node nr="6" />

   <fruit>
      <name>Apple2</name>
      <calorien>999</calorien>
      <color>red</color>
   </fruit>
   <fruit>
      <name>cherry2</name>
      <calorien>999</calorien>
      <color>red</color>
   </fruit>
   <fruit>
      <name>banana2</name>
      <calorien>999</calorien>
      <color>red</color>
   </fruit>

...and so on...
</PARENT_FRUIT>

...other elements ...
</root>

The following XML should be my end result:

<root>
... other elements ...

<PARENT_FRUIT>
... other elements ...

<fruits>
   <fruit>
      <name>apple</name>
      <calorien>999</carlorien>
      <color>red</color>
   </fruit>
   <fruit>
      <name>cherry</name>
      <calorien>999</carlorien>
      <color>red</color>
   </fruit>
   <fruit>
      <name>banana</name>
      <calorien>999</carlorien>
      <color>red</color>
   </fruit>
   <fruit>
      <name>apple2</name>
      <calorien>999</carlorien>
      <color>red</color>
   </fruit>
   <fruit>
      <name>cherry2</name>
      <calorien>999</carlorien>
      <color>red</color>
   </fruit>
   <fruit>
      <name>banana2</name>
      <calorien>999</carlorien>
      <color>red</color>
   </fruit>

... and so on ...
<fruits>

... other elements ...
<PARENT_FRUIT>

... other elements ...
</root>

EDIT 04.06.2019:

I use XLST Version 1.0

Upvotes: 0

Views: 300

Answers (2)

Martin Honnen
Martin Honnen

Reputation: 167516

You can match on fruit[1] and wrap all fruit elements into a fruits container and match on the other fruit elements to not process them:

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

  <xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="fruit[1]">
      <fruits>
          <xsl:copy-of select="../fruit"/>
      </fruits>
  </xsl:template>

  <xsl:template match="fruit[position() > 1]"/>

</xsl:stylesheet>

Upvotes: 0

zx485
zx485

Reputation: 29022

If you can live with all of the other elements grouped at the beginning, the following, simple, XSLT-1.0 stylesheet will do the job. Otherwise it would be unclear where you want to place the <fruits> element.

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

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

    <xsl:template match="PARENT_FRUIT">
        <xsl:copy>
            <xsl:apply-templates select="*[not(self::fruit)]|@*"/>
            <fruits>
                <xsl:apply-templates select="fruit"/>
            </fruits>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Its output is:

<?xml version="1.0"?>
<root>
    ... other elements ...

    <PARENT_FRUIT>
        <different_node nr="1"/>
        <different_node nr="2"/>
        <different_node nr="3"/>
        <different_node nr="4"/>
        <different_node nr="5"/>
        <different_node nr="6"/>
        <fruits><fruit>
                <name>Apple</name>
                <calorien>999</calorien>
                <color>red</color>
            </fruit>
            <fruit>      
                <name>cherry</name>
                <calorien>999</calorien>
                <color>red</color>
            </fruit>
            <fruit>
                <name>banana</name>
                <calorien>999</calorien>
                <color>red</color>
            </fruit>
            <fruit>
                <name>Apple2</name>
                <calorien>999</calorien>
                <color>red</color>
            </fruit>
            <fruit>
                <name>cherry2</name>
                <calorien>999</calorien>
                <color>red</color>
            </fruit>
            <fruit>
                <name>banana2</name>
                <calorien>999</calorien>
                <color>red</color>
            </fruit>
        </fruits>
    </PARENT_FRUIT>

    ...other elements ...
</root>

Upvotes: 0

Related Questions