xsl-list
[Top] [All Lists]

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

2011-12-14 15:04:20
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(_at_)CraneSoftwrights(_dot_)com
Google+ profile: https://plus.google.com/116832879756988317389/about
Legal business disclaimers:    http://www.CraneSoftwrights.com/legal


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>