This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: creating list from structured paragraphs


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]