Skip to content

Instantly share code, notes, and snippets.

@nine9ths
Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nine9ths/42283ef0480ad640cfae to your computer and use it in GitHub Desktop.
Save nine9ths/42283ef0480ad640cfae to your computer and use it in GitHub Desktop.
Thoughts on XSLT grouping
<!--
From http://www.w3.org/TR/xslt-30/
A standard grouping exercise to wrap a group of elements
-->
<xsl:template match="body">
<chapter>
<xsl:for-each-group select="*" group-starting-with="h2">
<section title="{self::h2}">
<xsl:for-each select="current-group()[self::p]">
<para><xsl:value-of select="."/></para>
</xsl:for-each>
</section>
</xsl:for-each-group>
</chapter>
</xsl:template>
<!--
This can be rewritten using a subtree to create a node at the group level
to match on.
-->
<xsl:template match="body">
<chapter>
<xsl:for-each-group select="*" group-starting-with="h2">
<xsl:variable name="group" as="element(group)">
<group>
<xsl:sequence select="current-group()"/>
</group>
</xsl:variable>
<xsl:apply-templates select="$group"/>
</xsl:for-each-group>
</chapter>
</xsl:template>
<!--
The problem with this method is that it breaks the context of the nodes in
the group. You can't do something in this template like ancestor::body/foo
-->
<xsl:template match="group">
<section title="{h2}">
<xsl:for-each select="p">
<para><xsl:value-of select="."/></para>
</xsl:for-each>
</section>
</xsl:template>
<!--
What I'd like is to have some kind of psuedo-node e.g. group(), that
would be created by the for-each-group instruction that could be
part of a match pattern. Within the pseudo-node the current-group() elements
would be accessible via the child axis and the current-grouping-key()
function would be defined
-->
<xsl:template match="body">
<chapter>
<xsl:for-each-group select="*" group-starting-with="h2">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</chapter>
</xsl:template>
<xsl:template match="group()">
<section title="{h2}">
<xsl:apply-templates select="p"/>
</section>
</xsl:template>
<xsl:template match="p">
<para><xsl:value-of select="."/></para>
</xsl:template>
@nine9ths
Copy link
Author

nine9ths commented Dec 4, 2014

There are obviously some problems with the proposed child axis for attribute nodes in group(), and you would need to use predicates to distinguish different group() from each other but I think that pattern has been established by XSLT 3.0's use of predicate patterns.

@gimsieke
Copy link

gimsieke commented Dec 4, 2014

If you want something to match on at group level, you could use two passes that process the whole document. The first pass groups, the next pass matches on the groups. We often do multi-pass-whole-document transformations because we can save snapshots of the whole document after each pass for debugging purposes. Of course memory consumption and also transformation time may be an issue.

I don’t fathom which modifications would be necessary on the XDM in order to support this. I believe most people wouldn’t think that the benefits outweigh the additional complexity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment