xslt to HTML Transform question

Discussion in 'XML' started by Adam dR., Nov 25, 2006.

  1. Adam dR.

    Adam dR. Guest

    I have an xml file similar to:
    <menu>
    <menuitem name="home" show="false"/>
    <menuitem name="about" show="true"/>
    <menuitem name="links" show="true"/>
    <menuitem name="games" show="true"/>
    <menuitem name="learn" show="true"/>
    <menuitem name="order" show="true"/>
    <menuitem name="contact" show="true"/>
    <menuitem name="support" show="false"/>
    <menuitem name="feedback" show="true"/>
    ....
    </menu>

    What I am trying to achieve is a table that takes every four menuitems
    that are show="true" and put them in a table row and then put each
    menuitem is a <td>..

    Desired output:
    <table>
    <tr>
    <td>about</td>
    <td>links</td>
    <td>games</td>
    <td>learn</td>
    </tr>
    <tr>
    <td>order</td>
    <td>contact</td>
    <td>feedback</td>
    </tr>
    </table>

    I cannot figure out how to loop four and place them in a <tr> tag

    What I have tried:
    <xsl:template match="/">
    <table>
    <xsl:apply-templates select="menu"/>
    </table>
    </xsl:template>

    <xsl:template match="menu">
    <xsl:for-each select="menuitem[@show='true']">

    <xsl:if test="position() mod 4 = 1">
    <xsl:text >&lt;tr&gt;</xsl:text>
    </xsl:if>

    <td><xsl:value-of select="@name"/></td>

    <xsl:if test="position() mod 4 = 0">
    <xsl:text>&lt;tr&gt;</xsl:text>
    </xsl:if>

    </xsl:for-each>
    </xsl:template>

    There are a couple problems with this, 1. the position is still the
    position of all menuitems true or false so I am not guaranteed 4 items
    per row. 2. and most of all if actually right "<tr>" rather then
    placing the <tr> tag in the html doc.

    Any help would be great!

    ~Adam dR.
    Adam dR., Nov 25, 2006
    #1
    1. Advertising

  2. Adam dR.

    Guest

    Michael Kay's XSLT 2nd Edition has a decent explanation of
    this class of problem, which can be found by looking in the index
    for "tail recursion".

    > There are a couple problems with this, 1. the position is still the
    > position of all menuitems true or false


    Couldn't quite grok that statement. Seems to me position()
    would reflect the position of the nodelist formed by the
    for-each select clause. xsltproc seemed to agree with my
    assumption. Maybe I've missed something here.

    > per row. 2. and most of all if actually right "<tr>" rather then
    > placing the <tr> tag in the html doc.


    That gets you to the nut: using recursion instead of iteration.
    Here's one possible solution:

    <?xml version="1.0"?>
    <xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    extension-element-prefixes="exsl"
    version="1.0"
    >

    <xsl:eek:utput method="html" indent="yes"/>

    <xsl:template name="EmitRows">
    <xsl:param name="MenuItemList" />

    <xsl:if test="count($MenuItemList) != 0">
    <tr>
    <xsl:for-each select="$MenuItemList[position() &lt; 5]">
    <td><xsl:value-of select="@name" /></td>
    </xsl:for-each>
    </tr>
    <xsl:call-template name="EmitRows">
    <xsl:with-param name="MenuItemList"
    select="$MenuItemList[position() &gt; 4]" />
    </xsl:call-template>
    </xsl:if>

    </xsl:template>

    <xsl:template match="/">
    <table>
    <xsl:apply-templates select="menu"/>
    </table>
    </xsl:template>

    <xsl:template match="menu">
    <xsl:call-template name="EmitRows">
    <xsl:with-param name="MenuItemList"
    select="./menuitem[@show='true']" />
    </xsl:call-template>
    </xsl:template>
    </xsl:stylesheet>

    xsltproc applies this XSLT file to your example XML data to produce:

    <table>
    <tr>
    <td>about</td>
    <td>links</td>
    <td>games</td>
    <td>learn</td>
    </tr>
    <tr>
    <td>order</td>
    <td>contact</td>
    <td>feedback</td>
    </tr>
    </table>

    Basic technique is to write a callable template that just returns
    if the list is empty. If it's not empty, it does something useful with
    one or more items at the front of the list, then passes all the other
    items to itself recursively.

    Hope this helps,
    Ron Burk
    www.xmlator.com
    , Nov 26, 2006
    #2
    1. Advertising

  3. > Michael Kay's XSLT 2nd Edition has a decent explanation of
    > this class of problem, which can be found by looking in the index
    > for "tail recursion".


    Not all XSLT processors handle tail recursion gracefully. It is safer to use
    another general method that just shortens dramatically the maximum size of
    the call-stack needed -- DVC (Divide and Conquer). For example, to process
    recursively a list of 1000000 (1M) items, the DVC method will use a stack
    with maximum depth ~ 19 (log2(N) ).

    One can read more about implementing DVC in XSLT here:

    http://www.topxml.com/code/default.asp?p=3&id=v20020107050418


    Or I'd recommend that one uses some of the most generic functions for
    recursive processing, such as foldl(), implemented in the FXSL library. It
    comes with most functions already implemented DVC, so one will not have to
    do anything in addition but just use the functions.

    Find more about FXSL here:

    http://fxsl.sf.net (especially read the latest paper for the "Extreme
    Markup Languages 2007" conference).


    Cheers,
    Dimitre Novatchev


    <> wrote in message
    news:...
    >
    > Michael Kay's XSLT 2nd Edition has a decent explanation of
    > this class of problem, which can be found by looking in the index
    > for "tail recursion".
    >
    >> There are a couple problems with this, 1. the position is still the
    >> position of all menuitems true or false

    >
    > Couldn't quite grok that statement. Seems to me position()
    > would reflect the position of the nodelist formed by the
    > for-each select clause. xsltproc seemed to agree with my
    > assumption. Maybe I've missed something here.
    >
    >> per row. 2. and most of all if actually right "<tr>" rather then
    >> placing the <tr> tag in the html doc.

    >
    > That gets you to the nut: using recursion instead of iteration.
    > Here's one possible solution:
    >
    > <?xml version="1.0"?>
    > <xsl:stylesheet
    > xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    > xmlns:exsl="http://exslt.org/common"
    > extension-element-prefixes="exsl"
    > version="1.0"
    >>

    > <xsl:eek:utput method="html" indent="yes"/>
    >
    > <xsl:template name="EmitRows">
    > <xsl:param name="MenuItemList" />
    >
    > <xsl:if test="count($MenuItemList) != 0">
    > <tr>
    > <xsl:for-each select="$MenuItemList[position() &lt; 5]">
    > <td><xsl:value-of select="@name" /></td>
    > </xsl:for-each>
    > </tr>
    > <xsl:call-template name="EmitRows">
    > <xsl:with-param name="MenuItemList"
    > select="$MenuItemList[position() &gt; 4]" />
    > </xsl:call-template>
    > </xsl:if>
    >
    > </xsl:template>
    >
    > <xsl:template match="/">
    > <table>
    > <xsl:apply-templates select="menu"/>
    > </table>
    > </xsl:template>
    >
    > <xsl:template match="menu">
    > <xsl:call-template name="EmitRows">
    > <xsl:with-param name="MenuItemList"
    > select="./menuitem[@show='true']" />
    > </xsl:call-template>
    > </xsl:template>
    > </xsl:stylesheet>
    >
    > xsltproc applies this XSLT file to your example XML data to produce:
    >
    > <table>
    > <tr>
    > <td>about</td>
    > <td>links</td>
    > <td>games</td>
    > <td>learn</td>
    > </tr>
    > <tr>
    > <td>order</td>
    > <td>contact</td>
    > <td>feedback</td>
    > </tr>
    > </table>
    >
    > Basic technique is to write a callable template that just returns
    > if the list is empty. If it's not empty, it does something useful with
    > one or more items at the front of the list, then passes all the other
    > items to itself recursively.
    >
    > Hope this helps,
    > Ron Burk
    > www.xmlator.com
    >
    Dimitre Novatchev, Nov 26, 2006
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Isambella  via DotNetMonster.com

    Transform XML string using XSLT file

    Isambella via DotNetMonster.com, Aug 1, 2005, in forum: ASP .Net
    Replies:
    4
    Views:
    9,717
    Isambella via DotNetMonster.com
    Aug 2, 2005
  2. Igor
    Replies:
    1
    Views:
    4,955
    Dimitre Novatchev
    Jul 20, 2003
  3. David
    Replies:
    1
    Views:
    719
    Martin Honnen
    Apr 3, 2004
  4. Gary Huntress

    XSLT Transform Vector to Array

    Gary Huntress, Aug 13, 2004, in forum: XML
    Replies:
    6
    Views:
    1,065
    Robin Johnson
    Aug 17, 2004
  5. pbd22
    Replies:
    0
    Views:
    584
    pbd22
    Jun 29, 2007
Loading...

Share This Page