xsl-list
[Top] [All Lists]

Re: [xsl] Changing a Tree Walking, forward walk example

2009-01-23 21:42:31
At 2009-01-24 01:04 +0000, Graeme Kidd wrote:
The problem is that my list does not have an end list node if a new list starts immediately after an existing list. So for example this:
...
Needs to be changed to this:
...
I am finding it hard because I need to match a list node that does not contain "End List" to start a list as well as end it. So far I have not been able to detect if I am still in a list before a new list starts, in which case I need to close it and start a new one.

That is a typical imperative programming approach that does not work well with XML content. You are thinking about tags instead of thinking about elements. In XSLT one cannot "close one off and start a new one", because that implies one is able to "throw in an end tag and then a start tag". When dealing with markup, this isn't an issue, but XSLT doesn't deal with markup, it deals with nodes. I see this a lot in very poorly written XSLT where novice users try to "end off a table row and start another" by inappropriately using disable-output-escaping. That's fine in programming where I'm serializing markup, but in XSLT one is constructing result trees, not serializing markup (that's the responsibility of the XSLT processor, not the stylesheet).

The approach you need must be more declarative. "How do I detect everything that belongs in a list so that I can properly construct the result tree list nodes?" Nothing there about tags, only about structures.

So, you need to look at your content and determine how to declaratively express the presence of the lists. This is done for your example with <xsl:for-each-group>, which I tell my students is better understood when one reads the element name as if it were "for the first member of each group".

Now, based on the nature of your data, your lists start with "<list>...</list>" and they optionally end with "<list>End List</list>". Another way to say that is a list starts with "<list>....</list>" and non-lists start with "<list>End List</list>".

So this is how I approached it in the solution below ... I hope this helps.

. . . . . . . . . . . ken

t:\ftemp>type kidd.xml
<document>
   <p>Non List text</p>
   <list>List Heading Type 1</list>
   <p>First item</p>
   <p>Second item</p>
   <list>List Heading Type 2</list>
   <p>First item</p>
   <p>Second item</p>
   <p>Third item</p>
   <list>End List</list>
   <p>Unrelated Text</p>
   <p>Not in a list</p>
   <list>List Heading Type 1</list>
   <p>First item</p>
   <list>End List</list>
   <p>More Unrelated Text</p>
   <p>Not in a list</p>
</document>

t:\ftemp>call xslt2 kidd.xml kidd.xsl
<?xml version="1.0" encoding="UTF-8"?>
<document>
   <p>Non List text</p>
   <list>
      <li>First item</li>
      <li>Second item</li>
   </list>
   <list>
      <li>First item</li>
      <li>Second item</li>
      <li>Third item</li>
   </list>
   <p>Unrelated Text</p>
   <p>Not in a list</p>
   <list>
      <li>First item</li>
   </list>
   <p>More Unrelated Text</p>
   <p>Not in a list</p>
</document>
t:\ftemp>type kidd.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="2.0">

<xsl:output indent="yes"/>

<xsl:template match="document">
  <document>
    <xsl:for-each-group select="*" group-starting-with="list">
      <xsl:choose>
        <xsl:when test="not(self::list)">
          <!--there must be a group before the first list-->
          <xsl:apply-templates select="current-group()"/>
        </xsl:when>
        <xsl:when test=".='End List'">
          <!--this indicates that what follows isn't in a list-->
          <xsl:apply-templates select="current-group()[position()>1]"/>
        </xsl:when>
        <xsl:otherwise>
          <!--we are starting a list-->
          <list>
            <xsl:for-each select="current-group()[position()>1]">
              <li><xsl:apply-templates/></li>
            </xsl:for-each>
          </list>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </document>
</xsl:template>

<xsl:template match="@*|node()"><!--identity for all other nodes-->
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>rem Done!



--
Upcoming XSLT/XSL-FO, UBL and code list hands-on training classes:
:  Sydney, AU 2009-01/02; Brussels, BE 2009-03; Prague, CZ 2009-03
Training tools: Comprehensive interactive XSLT/XPath 1.0/2.0 video
Video lesson:    http://www.youtube.com/watch?v=PrNjJCh7Ppg&fmt=18
Video overview:  http://www.youtube.com/watch?v=VTiodiij6gE&fmt=18
G. Ken Holman                 mailto:gkholman(_at_)CraneSoftwrights(_dot_)com
Crane Softwrights Ltd.          http://www.CraneSoftwrights.com/s/
Male Cancer Awareness Nov'07  http://www.CraneSoftwrights.com/s/bc
Legal business disclaimers:  http://www.CraneSoftwrights.com/legal


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--