Re: [xsl] Grouping with siblings

Subject: Re: [xsl] Grouping with siblings
From: "Terry Badger terry_badger@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sat, 22 Feb 2025 18:03:27 -0000
I wanted to try solving this problem and then using Oxygen's new AI to help
with documentation. It seems to explain the templates reasonably well. I added
one comment to explain the key logic I used which is the first comment. The
following XSL creates the required output.

<?xml version="1.0" encoding="UTF-8"?>
<!-- TB-COMMENT WHEN USING FOR-EACH-GROUP STARTS WITH chapter AND p ELEMENTS
THOSE ELEMENTS THAT IMPLY AN END
B B B  TO THEM IN SERIAL INPUT BECOME ELEMENTS WITH NO NODES EXCEPT THEMSELVES
AND ARE EASILY ELIMINATED BY THE NODE COUNT IN THE XSL:CHOOSE. -->
<!-- Transforms an XML document with a root element into a structured "book"
format, organizing
B B B  "div" elements into "chapter" and "p" elements. Handles nested text and
formatting elements,
B B B  ensuring proper grouping and formatting. -->
<xsl:stylesheet exclude-result-prefixes="xs" expand-text="yes" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema";
B B B  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
B B B  <!-- Transforms an XML document with a root element into a structured
"book" format. Groups and
B B B B B B B  processes "div" elements into "chapter" and "p" elements,
handling nested text and formatting
B B B B B B B  elements. -->
B B B  <xsl:output indent="yes"/>
B B B  <xsl:strip-space elements="*"/>
B B B  <!-- Transforms the root element into a "book" element. Applies
templates to process child nodes
B B B B B B B  within the "book" element. -->
B B B  <xsl:template match="/root">
B B B B B B B  <xsl:element name="book">
B B B B B B B B B B B  <xsl:apply-templates/>
B B B B B B B  </xsl:element>
B B B  </xsl:template>
B B B  <!-- Transforms "div" elements by grouping nodes into chapters and
paragraphs. Constructs
B B B B B B B  structured XML output with chapter numbers, titles, and
cleansed paragraph content. -->
B B B  <xsl:template match="div">
B B B B B B B  <!-- Groups nodes starting with "chapter" and processes each
group to create structured "chapter"
B B B B B B B B B B B  elements. It further groups nodes starting with "p"
within each chapter, handling text and
B B B B B B B B B B B  formatting elements. -->
B B B B B B B  <xsl:for-each-group group-starting-with="chapter"
select="node()">
B B B B B B B B B B B  <!-- Processes and structures chapter elements based on
their presence and attributes. Groups and
B B B B B B B B B B B B B B B  formats paragraph elements within chapters,
handling text and specific formatting tags. -->
B B B B B B B B B B B  <xsl:choose>
B B B B B B B B B B B B B B B  <xsl:when test="count(current-group()/node()) =
0"/>
B B B B B B B B B B B B B B B  <xsl:otherwise>
B B B B B B B B B B B B B B B B B B B  <!-- Creates a "chapter" element with a
"number" attribute and a "title" element, processing
B B B B B B B B B B B B B B B B B B B B B B B  grouped "p" elements. It
removes extra spaces and line breaks, and copies "emphasis" and "bold"
B B B B B B B B B B B B B B B B B B B B B B B  elements within paragraphs.
-->
B B B B B B B B B B B B B B B B B B B  <xsl:element name="chapter">
B B B B B B B B B B B B B B B B B B B B B B B  <xsl:attribute name="number"
select="current-group()/self::chapter/@n"/>
B B B B B B B B B B B B B B B B B B B B B B B  <xsl:element name="title">
B B B B B B B B B B B B B B B B B B B B B B B B B B B  <xsl:value-of
select="current-group()/self::chapter/@sID"/>
B B B B B B B B B B B B B B B B B B B B B B B  </xsl:element>
B B B B B B B B B B B B B B B B B B B B B B B  <!-- Groups elements starting
with "p" and processes them based on their count. If more than one,
B B B B B B B B B B B B B B B B B B B B B B B B B B B  it creates a new "p"
element, concatenating text and copying "emphasis" or "bold" elements. -->
B B B B B B B B B B B B B B B B B B B B B B B  <xsl:for-each-group
group-starting-with="p" select="current-group()">
B B B B B B B B B B B B B B B B B B B B B B B B B B B  <xsl:choose>
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B  <xsl:when
test="count(current-group()/normalize-space(self::node())) = 1"/>
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B 
<xsl:otherwise>
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B 
<xsl:element name="p">
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B 
<xsl:for-each select="current-group()/(self::node())">
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B
 B B B B  <xsl:value-of select="replace(self::text(), '(B  )|&#x0A;|&#x0D;',
'')"/>
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B
 B B B B  <xsl:copy-of select="self::emphasis | self::bold"/>
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B 
</xsl:for-each>
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B 
</xsl:element>
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B 
</xsl:otherwise>
B B B B B B B B B B B B B B B B B B B B B B B B B B B  </xsl:choose>
B B B B B B B B B B B B B B B B B B B B B B B  </xsl:for-each-group>
B B B B B B B B B B B B B B B B B B B  </xsl:element>
B B B B B B B B B B B B B B B  </xsl:otherwise>
B B B B B B B B B B B  </xsl:choose>
B B B B B B B  </xsl:for-each-group>
B B B  </xsl:template>
</xsl:stylesheet>
Terry Badger


    On Saturday, February 15, 2025 at 11:07:11 PM EST, Liam R. E. Quin
liam@xxxxxxxxxxxxxxxx <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:

 On Sun, 2025-02-16 at 00:12 +0000, Martin Honnen martin.honnen@xxxxxx wrote:



 On 16/02/2025 01:00, Liam R. E. Quin liam@xxxxxxxxxxxxxxxx wrote:


 On Sat, 2025-02-15 at 20:49 +0000, rick@xxxxxxxxxxxxxx wrote:

 Thank you very much Martin. That got me going in the right direction.
 A quick note - in some cases group-by won't work well. An example can




It was group-starting-with in the posted code, both the original and the
suggestion I made (actually the code I spelled out used only
group-starting-with but I also said that the existence of start and end marker
elements kind of asked for the group-starting-with to wrap a
group-ending-with.



Yes, your approach was fine for the posterbs case, as i said.
Itbs for when they try to take that approach to other applications.
Sorry if i wasnbt clear.
Best,
liam
--
Liam Quin,B https://www.delightfulcomputing.com/Available for
XML/Document/Information Architecture/XSLT/XSL/XQuery/Web/Text Processing/A11Y
training, work & consulting.Barefoot Web-slave, antique illustrations:
B http://www.fromoldbooks.orgXSL-List info and archiveEasyUnsubscribe(by
email)

Current Thread