This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: creating list from structured paragraphs
- From: Jeni Tennison <jeni at jenitennison dot com>
- To: "David Santamauro" <david dot santamauro at snet dot net>
- Cc: xsl-list at lists dot mulberrytech dot com
- Date: Mon, 20 May 2002 13:20:56 +0100
- Subject: Re: [xsl] creating list from structured paragraphs
- Organization: Jeni Tennison Consulting Ltd
- References: <NEEDKDINCOEOGOEOLJHAEECCCEAA.david.santamauro@snet.net>
- Reply-to: xsl-list at lists dot mulberrytech dot com
Hi David,
>> >I'm hoping this is simple. Here is the XML:
>> >
>> ><doc>
>> > <para>The following text is a list
>> > <para-1>List item No. 1</para-1>
>> > <para-1>List item No. 2</para-1>
>> > <para-1>List item No. 3</para-1>
>> > </para>
>> ></doc>
>> >
>> >I need:
>> >
>> ><doc>
>> > <para>The following text is a list
>> > <list>
>> > <list-item>List item No. 1</list-item>
>> > <list-item>List item No. 2</list-item>
>> > <list-item>List item No. 3</list-item>
>> > </list>
>> > </para>
>> ></doc>
If all your paras are in that structure, then you could use:
<xsl:template match="para">
<para>
<xsl:value-of select="text()" />
<list>
<xsl:for-each select="para-1">
<list-item><xsl:apply-templates /></list-item>
</xsl:for-each>
</list>
</para>
</xsl:template>
If your para elements hold a mixture of text nodes and para-1 elements
intermingled (i.e. several lists, or text appearing before and after
lists), then you need to use a grouping method. I favour a
step-by-step method in these cases -- apply templates to the first
node of the para element:
<xsl:template match="para">
<para>
<xsl:apply-template select="(text() | *)[1]" mode="para" />
</para>
</xsl:template>
Then, for text nodes, just give the value of the text node and move on
to the next node:
<xsl:template match="text()" mode="para">
<xsl:value-of select="." />
<xsl:apply-templates select="following-sibling::*[1]" mode="para" />
</xsl:template>
For para-1 elements, if you've applied templates in para mode then you
need to create a list, then step through the following para-1 elements
to create the content of the list. After creating the list, apply
templates to the next text node (or element) that isn't a para-1
element:
<xsl:template match="para-1" mode="para">
<list>
<xsl:apply-templates select="." mode="list-item" />
</list>
<xsl:apply-templates mode="para"
select="following-sibling::node()[not(self::para-1)][1]" />
</xsl:template>
<xsl:template match="para-1" mode="list-item">
<list-item>
<xsl:apply-templates />
</list-item>
<xsl:apply-templates mode="list-item"
select="following-sibling::node()[1][self::para-1]" />
</xsl:template>
---
The XSLT 2.0 method is to group adjacent sets of nodes, and have the
grouping value oscillate between true and false -- true when the node
is a para-1 element and false when it isn't:
<xsl:template match="para">
<xsl:for-each-group select="node()"
group-adjacent="self::para-1">
<xsl:choose>
<xsl:when test="self::para-1">
<list>
<xsl:apply-templates select="current-group()" />
</list>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="para-1">
<list-item>
<xsl:apply-templates />
</list-item>
</xsl:template>
I suspect that this is a pattern we'll see quite a lot of in XSLT 2.0.
It's a little non-obvious at first (though no more non-obvious than
the grouping hoops that we have to jump through in XSLT 1.0).
Cheers,
Jeni
---
Jeni Tennison
http://www.jenitennison.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list