Home > DeveloperSection > Forums > Parse repeating child elements in non-repeating parent node
Ashish Pandey

Total Post:128

Points:898
Posted on    November-06-2014 12:54 AM

 XML Xslt  Xslt-2.0 
Ratings:


 1 Reply(s)
 765  View(s)
Rate this:

I have the following input xml and was wondering if XSLT can handle such transformation. If it can, how can it be achieve?

input xml:

<foo>
  <bar>
    <A>xxx</A>
    <B>yyy</B>
    <C>zzz</C>
    <A>aaa</A>
    <B>bbb</B>
    <C>ccc</C>
     ...
     ..
  </bar>
</foo>

output xml:

<data>
   <A>xxx</A>
   <B>yyy</B>
   <C>zzz</C>
</data>
<data>
   <A>aaa</A>
   <B>bbb</B>
   <C>ccc</C>
</data>
....

There could be more repeating A, B, C nodes in the example above. Since the repetition isn't in a repeating parent node, it's not possible to use for-each. I was exploring the option of for-each-group but not sure if that is applicable. Would appreciate any advise.



Jayden Bell
Jayden Bell

Total Post:105

Points:739
Posted on    November-06-2014 3:12 AM

Using XSLT 1.0, following solution is working - tested with your example XML with 6 elements:

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

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

<xsl:template match="/*">

 <xsl:apply-templates select="/foo/bar"/>

</xsl:template>

 

<xsl:template match="bar">

 <xsl:for-each select="*/node()">

   <xsl:if test="(position()-1) mod(3) = 0">

    <data>

     <xsl:call-template  name="grouping">

      <xsl:with-param name="position" select="position()"/>

      <xsl:with-param name="amount" select="3"/>

     </xsl:call-template>

    </data>

   </xsl:if>

  </xsl:for-each>

</xsl:template>

 

<xsl:template  name="grouping">

<xsl:param name="position" select="0"/>

<xsl:param name="amount" select="0"/>

<xsl:copy-of select="//bar/*[$position]"/>

   <xsl:if test="$amount > $position or   ($position mod(3) > 0)">

    <xsl:call-template  name="grouping">

      <xsl:with-param name="position" select="$position + 1"/>

      <xsl:with-param name="amount" select="$amount"/>

    </xsl:call-template>

   </xsl:if>

 </xsl:template>

</xsl:stylesheet>

Result:

<?xml version="1.0" encoding="UTF-8"?>

<data>

   <A>xxx</A>

   <B>yyy</B>

   <C>zzz</C>

</data>

<data>

   <A>aaa</A>

   <B>bbb</B>

   <C>ccc</C>

</data>

As short explanation - in the template matching "bar" all element-nodes are processed in

<xsl:for-each select="*/node()">

By using <xsl:if test="(position()-1) mod(3) = 0"> (which is 0 for the 1st node and for every 3rd node) the template named "grouping" is called to provide generating groups of 3 nodes. This template is called with the parameters position - the position of the current node - and amount - the amount of nodes to be copied in each group.


Modified On Mar-22-2016 01:35:05 AM

Don't want to miss updates? Please click the below button!

Follow MindStick