HeisenBerg
HeisenBerg

Reputation: 127

XSLT to print values of a node based on its position repetitively

I have to transform this sample xml in xslt 1.0 or xslt 2.0 Source XML

<root>
    <group>
        <subgroup>
            <name>AAA1</name>
        </subgroup>
        <subgroup>
            <name>AAA2</name>
        </subgroup>
        <subgroup>
            <name>AAA3</name>
       </subgroup>
    </group>
 <group>
        <subgroup>
            <name>BB1</name>
        </subgroup>
        <subgroup>
            <name>BB2</name>
        </subgroup>
        <subgroup>
            <name>BB3</name>
       </subgroup>
    </group>
</root>

Required XML OUTPUT

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <group>AAA1|BB1|AAA2|BB2|AAA3|BB3</group>
</root>

I tried the following xslt:

XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" />
    <xsl:template match="root">
    <root>
      <group>
       <xsl:for-each select="group">
          <xsl:for-each select="subgroup">
            <xsl:choose>
            <xsl:when test="position() != last()">
            <xsl:value-of select="name" /><xsl:text>|</xsl:text>
            </xsl:when>
            <xsl:otherwise>
               <xsl:value-of select="name" />
            </xsl:otherwise>
             </xsl:choose> 
          </xsl:for-each>
       </xsl:for-each>
       </group>
    </root>
</xsl:template>
</xsl:stylesheet>

Current-output

<?xml version="1.0" encoding="UTF-8"?>
<root>
<group>AAA1|AAA2|AAA3BB1|BB2|BB3</group>
</root>

How can I do this? Should I create a template a call it recursively?

Upvotes: 0

Views: 1686

Answers (2)

Ian Roberts
Ian Roberts

Reputation: 122374

You need to process just one of the sets of spec elements, and pull out the corresponding spec from the other set by position:

<root>
  <group>
    <xsl:for-each select="group[1]/spec">
      <xsl:variable name="pos" select="position()"/>
      <xsl:for-each select="../../group">
        <!-- put a | in front of everything but the first spec in the first group -->
        <xsl:if test="$pos &gt; 1 or position() &gt; 1">|</xsl:if>
        <xsl:value-of select="spec[$pos]/name" />
      </xsl:for-each>
    </xsl:for-each>
  </group>
</root>

XSLT 2.0 version: in 2.0 you can do it with a single value-of:

<group>
  <xsl:value-of select="for $n in 1 to count(group[1]/spec)
                        return (group/spec[$n]/name)" separator="|" />
</group>

Upvotes: 1

Saurav
Saurav

Reputation: 640

Came up with this:

Picked up the all other groups except the first and then iterated over all specs for first group while picking up the corresponding specs from all other groups

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="root">
    <root>
      <group>
        <xsl:variable name="firstExcludeGroups" select="//group[position()!=1]"/>
        <xsl:variable name="firstGroup" select="//group[position()=1]"/>
        <xsl:for-each select="$firstGroup/spec">
          <xsl:value-of select="name"/>
          <xsl:text>|</xsl:text>
          <xsl:variable name="curPosition" select="position()"/>
          <xsl:for-each select="$firstExcludeGroups/spec">
            <xsl:if test="$curPosition=position()">
              <xsl:value-of select="name"/>
            </xsl:if>
          </xsl:for-each>
        </xsl:for-each>
      </group>
    </root>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 0

Related Questions