Re: [xsl] Wrap changing element sequence into container: with 'for-each-group'?

Subject: Re: [xsl] Wrap changing element sequence into container: with 'for-each-group'?
From: Yves Forkl <Y.Forkl@xxxxxx>
Date: Tue, 30 Jan 2007 18:13:30 +0100
What I am having trouble to do is adding a container around a specific
(fixed-order) sequence of those 'p' elements, say, 'p[style="a"]' and 'p[style="b"]', where the first of these may not always occur.

Michael Kay wrote:


If you can identify a comparison between two adjacent siblings which, if
true, means that they go in different containers, then you can use
group-starting-with. For example, in this case the condition might be that
the element is not immediately preceded by an element with a value for
@style that is alphabetically less than the current element's @style:

group-starting-with="p[not(preceding-sibling::p[1][@style lt
current()/@style])]"

Thank you for your useful explanations. Actually, I am faced with grouping in a situation which is a bit more complex. I would like to expose it on behalf of elements with distinct GIs, dropping the above example of "p" elements with "style" attributes to make it easier.


Among the child elements of my "root" element, some adjacent children need to go into some container, and this occurs repeatedly. I can make out two issues when trying to use xsl:for-each-group in this case:

1) The names of those children that should get together are very dissimilar, so functions that analyze the name like in the example above won't help much. Let's say I have "abc" and "xyz" that I want to group: how can I select the first instance of either of them, in order to add the container only once?

2) I fail to see how I can repeatedly group some children, when in between are some children that are not to be grouped. Do I need to apply recursion when working my way through the children?


Abel proposed using modes, along these lines (modified by me, so errors are mine):


>  <xsl:template match="*[starts-with(name(), 'b')][1]">
>     <container>
>       <xsl:apply-templates
>         select="self::* | following-sibling::*"
>         mode="within_container"/>
>     </container>
>   </xsl:template>
>
>   <xsl:template match="*[starts-with(name(), 'b')]"
>     mode="within_container">
>       <xsl:copy>
>           <xsl:apply-templates select="node() | @*"
>             mode="#current"/>
>       </xsl:copy>
>   </xsl:template>
>
>   <xsl:template match="*[not(starts-with(name(), 'b'))]">
>       <xsl:copy>
>           <xsl:apply-templates select="node() | @*" />
>       </xsl:copy>
>   </xsl:template>

This looks even more promising: I suspect taking out several groups of children is easier to do this way than with xsl:for-each-group. Is this true?

But using modes I face the same problem concerning the identification of the nodes to group (which in the above example all had names starting with "b", which isn't real) and of selecting the very first instance of them.

So how could I proceed?

Yves

Current Thread