Search code examples
xslttransformparent

xslt change parent -> child to child -> parent


I have a XML like this and need to change parent->child with their places:

<root>
    <testcase name="Case1">
        <testsuite>Suite1</testsuite>
    </testcase>
    <testcase name="Case2">
        <testsuite>Suite1</testsuite>
    </testcase>

    <testcase name="Case3">
        <testsuite>Suite2</testsuite>
    </testcase>
    <testcase name="Case4">
        <testsuite>Suite2</testsuite>
    </testcase>
</root>

need to transform this to this xml:

<root>
    <testsuite name="Suite1">
        <testcase>Case1</testcase>
        <testcase>Case2</testcase>
    </testsuite>

    <testsuite name="Suite2">
        <testcase>Case3</testcase>
        <testcase>Case4</testcase>
    </testsuite>
</root>

Can anyone help to implement this ?


Solution

  • This is an example of a grouping problem, and as with most grouping problems, the preferred approach (at least in XSLT 1.0) is to use Muenchian grouping:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
      <xsl:key name="kSuite" match="testsuite" use="."/>
    
      <xsl:template match="/*">
        <xsl:copy>
          <xsl:apply-templates 
                          select="testcase/testsuite[generate-id() = 
                                                     generate-id(key('kSuite', .)[1])]"/>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="testsuite">
        <testsuite name="{.}">
          <xsl:apply-templates select="key('kSuite', .)/.." />
        </testsuite>
      </xsl:template>
    
      <xsl:template match="testcase">
        <xsl:copy>
          <xsl:value-of select="@name" />
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>
    

    When this is run on your sample input, the result is:

    <root>
      <testsuite name="Suite1">
        <testcase>Case1</testcase>
        <testcase>Case2</testcase>
      </testsuite>
      <testsuite name="Suite2">
        <testcase>Case3</testcase>
        <testcase>Case4</testcase>
      </testsuite>
    </root>