tags:

views:

26

answers:

1

Hello,

this is an example of my input file.

<root>
    <!-- [...] -->
    <bbb>Foo 1</bbb>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
    <bbb>Foo 2</bbb>
    <ccc>Foo 2.1</ccc>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
    <bbb>Foo 3</bbb>
    <ccc>Foo 3.1</ccc>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
    <ccc>Foo 4</ccc>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
    <aaa>xxxxxxxx</aaa>
</root>

I want to group the nodes by the bbb and ccc elements. It is also possible that there are only bbb or only ccc in the input-xml.

There are following conditions:

  • condition A: group by "bbb" or the "ccc" element
  • condition B: if a "ccc" is immediately following a "bbb" take them together

This is the outpout-xml i want to have:

 <root>
        <group>
            <header>Foo 1</header>
                <groupcontent>
                    <aaa>xxxxxxxx</aaa>
                    <aaa>xxxxxxxx</aaa>
                    <aaa>xxxxxxxx</aaa>
                </groupcontent>
        </group>
        <group>
            <header>Foo 2</header>
            <subheader>Foo 2.1</subheader>
            <groupcontent>
                <aaa>xxxxxxxx</aaa>
                <aaa>xxxxxxxx</aaa>
                <aaa>xxxxxxxx</aaa>
            </groupcontent>
        </group>
        <group>
            <header>Foo 3</header>
            <subheader>Foo 3.1</subheader>
            <groupcontent>
                <aaa>xxxxxxxx</aaa>
                <aaa>xxxxxxxx</aaa>
                <aaa>xxxxxxxx</aaa>
            </groupcontent>
        </group>
        <group>
            <subheader>Foo 4</subheader>
            <groupcontent>
                <aaa>xxxxxxxx</aaa>
                <aaa>xxxxxxxx</aaa>
                <aaa>xxxxxxxx</aaa>
            </groupcontent>
        </group>
    </root>

Currently i have following XSL. But the problem is that it generates foreach "Foo" a "group"-Element.

<xsl:stylesheet>

     <xsl:output method="xml" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/">
    <output>
            <xsl:for-each-group select="root/*" group-starting-with="bbb|ccc">
                <group>
                        <xsl:apply-templates select="current-group()[self::bbb or self::ccc]"></xsl:apply-templates>
                        <groupcontent>
                            <xsl:apply-templates select="current-group()[not(self::bbb) and not (self::ccc)]"></xsl:apply-templates>
                        </groupcontent>
                </group>
            </xsl:for-each-group>
        </output>
    </xsl:template>

    <xsl:template match="bbb">
        <header><xsl:value-of select="."></xsl:value-of></header>
    </xsl:template>

    <xsl:template match="ccc">
        <subheader><xsl:value-of select="."></xsl:value-of></subheader>
    </xsl:template>

    <xsl:template match="aaa">
        <p><xsl:value-of select="."></xsl:value-of></p>
    </xsl:template>

</xsl:stylesheet>

How can i add the condition B to my XSL. Is it even possible to solve this with one foreach-group? Can i add the condition B to the "group-starting-with" and give it a higher priority than condition A? I have read something about that you can give patterns a priority...

thx in advance cpt.oneeye

+2  A: 

Preserving your stylesheet style, this stylesheet:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
 <output>
  <xsl:for-each-group select="root/*" group-starting-with=
    "bbb|ccc[preceding-sibling::*[1][not(self::bbb)]]">
    <group>
      <xsl:apply-templates select=
        "current-group()[self::bbb or self::ccc]"/>
      <groupcontent>
        <xsl:apply-templates select=
          "current-group()[not(self::bbb) and not (self::ccc)]"/>
      </groupcontent>
    </group>
  </xsl:for-each-group>
 </output>
</xsl:template>
<xsl:template match="bbb">
 <header>
  <xsl:value-of select="."/>
 </header>
</xsl:template>
<xsl:template match="ccc">
 <subheader>
  <xsl:value-of select="."/>
 </subheader>
</xsl:template>
<xsl:template match="aaa">
 <p>
  <xsl:value-of select="."/>
 </p>
</xsl:template>

Output:

<output>
    <group>
        <header>Foo 1</header>
        <groupcontent>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
        </groupcontent>
    </group>
    <group>
        <header>Foo 2</header>
        <subheader>Foo 2.1</subheader>
        <groupcontent>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
        </groupcontent>
    </group>
    <group>
        <header>Foo 3</header>
        <subheader>Foo 3.1</subheader>
        <groupcontent>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
        </groupcontent>
    </group>
    <group>
        <subheader>Foo 4</subheader>
        <groupcontent>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
            <p>xxxxxxxx</p>
        </groupcontent>
    </group>
</output>
Alejandro
Great solution (+1). However the formatting was so bad that I edited your answer. Please, never write: `<xsl:value-of select="something></xsl:value-of>` or the same wirth `<xsl:apply-templates>`, `<xsl:with-param>`, ..., etc. The only effect is that the code becomes extremely unreadable, is twice longer than it should be and requires horizontal scrolling that no normal person would do...
Dimitre Novatchev
hi alejandro, thank you (again) for the solution. it worked fine.
cpt.oneeye
@Dimitre: Thanks for editing. I've only changed the pattern for grouping. I should paid atention to those empty instructions.
Alejandro