Re: [xsl] Re: [xsl 1.0] howto merge branches by name

Subject: Re: [xsl] Re: [xsl 1.0] howto merge branches by name
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Wed, 14 Dec 2011 16:03:59 -0500
At 2011-12-14 22:37 +0200, Adrian Herscu wrote:
I am using XSL 1.0.

In the classroom I cover three ways to approach grouping with XSLT 1. In your case where you have multiple levels of groups, I usually use the variable method (this method is also helpful across multiple files):


t:\ftemp>type adrian.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  version="2.0">

<xsl:output indent="yes"/>

<xsl:template match="test">
  <test>
    <xsl:variable name="suite" select="suite"/>
    <xsl:for-each select="$suite">
      <xsl:if test="generate-id(.)=
                    generate-id($suite[@name=current()/@name])">
        <suite name="{@name}">
          <xsl:variable name="case"
                        select="$suite[@name=current()/@name]/case"/>
          <xsl:for-each select="$case">
            <xsl:if test="generate-id(.)=
                          generate-id($case[@name=current()/@name])">
              <case name="{@name}">
                <xsl:copy-of select="$case[@name=current()/@name]/procedure"/>
              </case>
            </xsl:if>
          </xsl:for-each>
        </suite>
      </xsl:if>
    </xsl:for-each>
  </test>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>xslt adrian.xml adrian.xsl
<?xml version="1.0" encoding="utf-8"?>
<test>
   <suite name="A">
      <case name="A">
         <procedure name="A"/>
         <procedure name="B"/>
      </case>
      <case name="B">
         <procedure name="A"/>
      </case>
   </suite>
</test>
t:\ftemp>type adrian.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  version="2.0">

<xsl:output indent="yes"/>

<xsl:template match="test">
  <test>
    <xsl:variable name="suite" select="suite"/>
    <xsl:for-each select="$suite">
      <xsl:if test="generate-id(.)=
                    generate-id($suite[@name=current()/@name])">
        <suite name="{@name}">
          <xsl:variable name="case"
                        select="$suite[@name=current()/@name]/case"/>
          <xsl:for-each select="$case">
            <xsl:if test="generate-id(.)=
                          generate-id($case[@name=current()/@name])">
              <case name="{@name}">
                <xsl:copy-of select="$case[@name=current()/@name]/procedure"/>
              </case>
            </xsl:if>
          </xsl:for-each>
        </suite>
      </xsl:if>
    </xsl:for-each>
  </test>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>

I see you've tried to use the Muenchian method, which can be done, and you are approaching it correctly, but I have some comments:

My last attempt to solve it this evening was:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:key name="suites" match="//suite" use="@name" />
<xsl:key name="cases" match="//case" use="concat(../@name, '.', @name)" />

The "//" is not needed in either of the above.


Be careful of the "." delimiter, which may work fine for your data ... I typically use a character unlikely to be found in the XML content

        <xsl:template match="/">
                <xsl:apply-templates />
        </xsl:template>

<xsl:template match="test">
<test>
<xsl:for-each select="//suite">
<xsl:if test="generate-id() = generate-id(key('suites',@name)[1])">
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>

The above can be more compact by using a predicate and simply pushing rather than doing both a pull and a push.


                </test>
        </xsl:template>

<xsl:template match="suite">
<suite name="{@name}">
<xsl:for-each select="//case">
<xsl:if test="generate-id() = generate-id(key('cases',concat(../@name, '.', @name))[1])">
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
</suite>
</xsl:template>


        <xsl:template match="case">
                <case name="{@name}" />
        </xsl:template>
</xsl:stylesheet>

which rendered duplicate cases :(

<?xml version="1.0" encoding="UTF-8"?>
<test>
        <suite name="A">
                <case name="A" />
                <case name="B" />
                <case name="B" />
        </suite>
        <suite name="C">
                <case name="A" />
                <case name="B" />
                <case name="B" />
        </suite>
</test>

Wait ... that output is for data that isn't the same as the data you had in your original post.


What is your input data so that I can mimic your incorrect results and then repair what you have?

. . . . . . . . . . . Ken

--
Contact us for world-wide XML consulting and instructor-led training
Free 5-hour video lecture: XSLT/XPath 1.0 & 2.0 http://ude.my/uoui9h
Crane Softwrights Ltd.            http://www.CraneSoftwrights.com/s/
G. Ken Holman                   mailto:gkholman@xxxxxxxxxxxxxxxxxxxx
Google+ profile: https://plus.google.com/116832879756988317389/about
Legal business disclaimers:    http://www.CraneSoftwrights.com/legal

Current Thread