How to do xslt Muenchian grouping with some null a

2019-07-12 21:18发布

Slightly different to the standard Meunchain grouping as I'm dealing with a null(?) attribute for some of the tags that I'm transforming. I'd like the null groupings to be treated as their own individual group with the transformed output adding the grouped strings. Also if there is a grouping to do a count of how many there were.

<root>
<section>
    <subsection>
        <module>
            <comp>111</comp>
        </module>
        <module group='group01'>
            <comp>222</comp>
        </module>
        <module group='group01'>
            <comp>333</comp>
        </module>
        <module>
            <comp>444</comp>
        </module>
        <module>
            <comp>555</comp>
        </module>
    </subsection>
</section>
<section>
    <subsection>
        <module group ="group02">
            <comp>666</comp>
        </module>
        <module group ="group02">
            <comp>777</comp>
        </module>
        <module>
            <comp>888</comp>
        </module>
        <module group ="group03">
            <comp>999</comp>
        </module>
        <module group ="group03">
            <comp>101010</comp>
        </module>
    </subsection>
    <subsection>
        <module group ="group04">
            <comp>11111</comp>
        </module>
        <module group ="group04">
            <comp>121212</comp>
        </module>
        <module group ="group05">
            <comp>131313</comp>
        </module>
        <module group ="group05">
            <comp>141414</comp>
        </module>
        <module group ="group06">
            <comp>151515</comp>
        </module>
        <module group ="group06">
            <comp>161616</comp>
        </module>
        <module>
            <comp>171717</comp>
        </module>
    </subsection>
</section>

Wanted output:

<AllSections>
<section>
    <subsection>
        <page>
            <content>111</content>
        </page>
        <page>
            <content>222333</content>
            <count>2</count>
        </page>
        <page>
            <content>444</content>
        </page>
        <page>
            <content>555</content>
        </page>
    </subsection>
</section>
<section>
    <subsection>
        <page>
            <content>666777</content>
            <count>2</count>
        </page>
        <page>
            <content>888</content>
        </page>
        <page>
            <content>999101010</content>
            <count>2</count>
        </page>
    </subsection>
    <subsection>
        <page>
            <content>111111121212</content>
            <count>2</count>
        </page>
        <page>
            <content>131313141414161616</content>
            <count>3</count>
        </page>
        <page>
            <content>151515</content>
        </page>
        <page>
            <content>171717</content>
        </page>
    </subsection>
</section>

Thanks!

标签: xslt xslt-1.0
1条回答
戒情不戒烟
2楼-- · 2019-07-12 21:28

For the elements with a group attribute, you are grouping by that attribute but also within the parent subsection element. Therefore you could start off by defining a key to group them this was

<xsl:key name="modules" match="module[@group]" use="concat(generate-id(..), '|', @group)" />

Next, you would need templates to match the various cases for the module elements. Firstly, you could have a template to match module elements with no group attribute, where you could format the output as required.

<xsl:template match="module[not(@group)]">
    <page>
        <content>
            <xsl:value-of select="comp"/>
        </content>
    </page>
 </xsl:template>

For modules, with group attributes you would need a match that checked that this particular module element occurred first in the group for the key defined above.

<xsl:template 
  match="module
    [@group]
    [generate-id() = generate-id(key('modules', concat(generate-id(..), '|', @group))[1])]">

Within this template, you could then easily define a variable to hold the elements in the group, using the key, and then either output the child comp elements, or count them

<xsl:variable name="modules" select="key('modules', concat(generate-id(..), '|', @group))"/>
<page>
    <content>
        <xsl:apply-templates select="$modules/comp/text()"/>
    </content>
    <count>
        <xsl:value-of select="count($modules)" />
     </count>
</page>

Finally, you would need a third template to match all other module elements (i.e. elements with a group attribute, but not first in the group) to ignore them, to ensure they don't get output twice. (The XSLT processor should always match more specific templates before this more generic one)

<xsl:template match="module"/>

Here is the full XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="modules" match="module[@group]" use="concat(generate-id(..), '|', @group)"/>

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

    <xsl:template match="module[not(@group)]">
        <page>
            <content>
                <xsl:value-of select="comp"/>
            </content>
        </page>
    </xsl:template>

    <xsl:template match="module[@group][generate-id() = generate-id(key('modules', concat(generate-id(..), '|', @group))[1])]">
        <xsl:variable name="modules" select="key('modules', concat(generate-id(..), '|', @group))"/>
        <page>
            <content>
                <xsl:apply-templates select="$modules/comp/text()"/>
            </content>
            <count>
                <xsl:value-of select="count($modules)" />
            </count>
        </page>
    </xsl:template>

    <xsl:template match="module"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

When applied to your sample XML, the following is output

<AllSections>
    <section>
        <subsection>
            <page>
                <content>111</content>
            </page>
            <page>
                <content>222333</content>
                <count>2</count>
            </page>
            <page>
                <content>444</content>
            </page>
            <page>
                <content>555</content>
            </page>
        </subsection>
    </section>
    <section>
        <subsection>
            <page>
                <content>666777</content>
                <count>2</count>
            </page>
            <page>
                <content>888</content>
            </page>
            <page>
                <content>999101010</content>
                <count>2</count>
            </page>
        </subsection>
        <subsection>
            <page>
                <content>11111121212</content>
                <count>2</count>
            </page>
            <page>
                <content>131313141414</content>
                <count>2</count>
            </page>
            <page>
                <content>151515161616</content>
                <count>2</count>
            </page>
            <page>
                <content>171717</content>
            </page>
        </subsection>
    </section>
</AllSections>
查看更多
登录 后发表回答