[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] XSLT3 generic grouping functions
Hi all, While teaching XSLT, someone ask if it was possible to have a kind of generic function to group XML elements. I first answer the only way to do it was to use (eventually nested) <xsl:for-each-group> instruction with specific conditions according to the data. Later, as I was discovering XSLT3, I realize the grouping condition might be sent as an higher-order-function parameter, which lead me to write some generic grouping functions to wrap XML elements (see example below). The original code can be found on github at https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/els-co mmon_xml.xsl There's also some generic xsl to nest titles (https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/nest- titles.xsl) and a specific implementation of it to nest HTML titles (https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/nest- html-titles.xsl) Working examples can be found in the test folder https://github.com/ELSGestion/els-sie-xsl-lib/tree/master/src/test, as xspec unit test or xspec driven integration tests. I actually have a bug with Saxon 9.9 (https://saxonica.plan.io/issues/4636) on the generic starting-with grouping function, but this is an occasion to share theses grouping libraries :) Any comments, feedbacks or similar code implementation are welcome ! Cheers Matthieu Ricaud-Dussarget Example of generic adjacent grouping function used to define a specific "by-name grouping" implementation (there's a similar set of functions for starting-with) : <xd:doc> <xd:desc> <xd:p>Wrap adjacent elements into a new "wrapper" element</xd:p> <xd:p>CAUTION : any text, pi, comments within context will be loose</xd:p> </xd:desc> <xd:param name="context">Parent of the adjacent elements to wrap</xd:param> <xd:param name="adjacent.function">Xpath function to set the adjacency condition</xd:param> <xd:param name="wrapper">Element wrapper</xd:param> <xd:param name="keep-context">Say if the context should be kept or not in the result</xd:param> <xd:return>context (or its content) with wrapped adjacent element</xd:return> </xd:doc> <xsl:function name="els:wrap-elements-adjacent" as="node()*"> <xsl:param name="context" as="element()"/> <xsl:param name="adjacent.function"/> <!--as="xs:string"--> <xsl:param name="wrapper" as="element()"/> <xsl:param name="keep-context" as="xs:boolean"/> <xsl:variable name="content" as="item()*"> <xsl:for-each-group select="$context/*" group-adjacent="$adjacent.function(.)"> <xsl:choose> <xsl:when test="current-grouping-key()"> <xsl:for-each select="$wrapper"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:sequence select="current-group()"/> </xsl:copy> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:copy-of select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:variable> <xsl:choose> <xsl:when test="$keep-context"> <xsl:for-each select="$context"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:sequence select="$content"/> </xsl:copy> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:sequence select="$content"/> </xsl:otherwise> </xsl:choose> </xsl:function> <xd:doc> <xd:desc> <xd:p>Wrap "adjacent by name" elements into a new "wrapper" element</xd:p> <xd:p>CAUTION : any text, pi, comment within context will be loose</xd:p> </xd:desc> <xd:param name="context">Parent of the adjacent elements to wrap</xd:param> <xd:param name="adjacent.names">sequence of qualified names to set adjacent elements</xd:param> <xd:param name="wrapper">element wrapper</xd:param> <xd:param name="keep-context">Say if the context should be kept or not in the result</xd:param> <xd:return>context (or its content) with wrapped adjacent element</xd:return> </xd:doc> <xsl:function name="els:wrap-elements-adjacent-by-names" as="node()*"> <xsl:param name="context" as="element()"/> <xsl:param name="adjacent.names" as="xs:string+"/> <xsl:param name="wrapper" as="element()"/> <xsl:param name="keep-context" as="xs:boolean"/> <xsl:sequence select="els:wrap-elements-adjacent( $context, function($e) as xs:boolean {name($e) = $adjacent.names}, $wrapper, $keep-context)"/> </xsl:function>
|
PURCHASE STYLUS STUDIO ONLINE TODAY!Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced! Download The World's Best XML IDE!Accelerate XML development with our award-winning XML IDE - Download a free trial today! Subscribe in XML format
|