[XSL-LIST Mailing List Archive Home]
[By Thread]
[By Date]
[xsl] reordening sequence, transposing rows, columns: generalisation and optimisation
Subject: [xsl] reordening sequence, transposing rows, columns: generalisation and optimisation From: Oscar Schoof <oscar@xxxxxxxxx> Date: Thu, 14 Mar 2002 22:53:33 +0100 |
Hi everybody, let me appologise immediately for the long post this is. This is about reordening the 'natural' sequence an xml/xsl can imply in applying templates. When transferring data to table-like structures you can also see this as transposing (colums with rows) or as some product (such as Crystal Reports) would call it cross-tabbing. I give full sources of an example. I have a few questions about 1. generalisation and 2. optimisation. See the latter parts of this posting. Hope you stay with me. The xml-example = = = = = = = = <!- persons.xml -> <persons> <person id="paul"> <favorites> <favorite><item>color</item><value>blue</value></favorite> <favorite><item>book</item><value>lordofrings</value></favorite> </favorites> </person> <person id="rosa"> <favorites> <favorite><item>color</item><value>green</value></favorite> <favorite><item>book</item><value>dune</value></favorite> </favorites> </person> <person id="stef"> <favorites> <favorite><item>color</item><value>red</value></favorite> <favorite><item>book</item><value>endersgame</value></favorite> </favorites> </person> </persons> In this example every person has some favorite items. The item-types are the same for every person and are ordered the same way (everyone has a color and a book favorite and book follows color). This ordening is not essential for xsl, but makes my examples as simple as possible. Simple stylesheet = = = = = = = = = To quickly see what my topic is about, i give an example in (conversion to) html of this data: (Never mind the specifics of html. It is of no issue to me. My purposes are into FO and then PDF.) <?xml version="1.0" encoding="UTF-8"?> <!- simple_table.xml -> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="persons"> <html> <body> <table width="120" border="1"> <xsl:apply-templates/> </table> </body> </html> </xsl:template> <xsl:template match="person"> <xsl:if test="position() = 1"> <tr> <td align="right" valign="top" width="40" > </td> <xsl:apply-templates mode="heading"/> </tr> </xsl:if> <tr> <td align="right" valign="top" width="40" bgcolor="yellow"><xsl:value-of select="@id"/></td> <xsl:apply-templates/> </tr> </xsl:template> <xsl:template match="favorite" mode="heading"> <td align="right" valign="top" width="40" bgcolor="red"><xsl:value-of select="item"/></td> </xsl:template> <xsl:template match="favorite"> <td align="right" valign="top" width="40"><xsl:value-of select="value"/></td> </xsl:template> <!-- ignore any other node/element --> <xsl:template match="text()|@*" mode="heading"> </xsl:template> <xsl:template match="text()|@*"> </xsl:template> </xsl:stylesheet> When assigning this xsl to my xml data and running the transformation the following table can be seen (use a fixed fontwidth): color book paul blue lordofrings rosa green dune stef red endersgame The xsl is straightforward. The structure of the xml and the way basic apply-template operates (top to bottom, root to end-node) makes that the persons become rows and their favorite items become columns. Transposing nodes = = = = = = = = = For my purposes it is necessary to have the data transposed: the persons as columns and their items as rows, like: paul rosa stef color blue green red book lordofrings dune endersgame This can be obtained with the following xsl: <?xml version="1.0" encoding="UTF-8"?> <!- transposing.xml -> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="persons"> <html> <body> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match="person"> <xsl:if test="position() mod 3 = 1"> <table width="160" border="1"> <xsl:variable name="pers" select="position()"/> <! - create column-headings - > <tr> <td align="right" valign="top" width="40"> </td> <td align="right" valign="top" width="40" bgcolor="yellow"> <xsl:value-of select="@id"/> </td> <td align="right" valign="top" width="40" bgcolor="yellow"> <xsl:value-of select="../person[$pers+1]/@id"/> </td> <td align="right" valign="top" width="40" bgcolor="yellow"> <xsl:value-of select="../person[$pers+2]/@id"/> </td> </tr> <xsl:apply-templates/> </table> </xsl:if> </xsl:template> <xsl:template match="favorite"> <xsl:variable name="fav" select="position()"/> <xsl:variable name="pers" select="1 + count(ancestor::person[1]/preceding-sibling::*)"/> <tr> <td align="right" valign="top" width="40" bgcolor="red"> <xsl:value-of select="item"/> </td> <td align="right" valign="top" width="40"> <xsl:value-of select="value"/> </td> <td align="right" valign="top" width="40"> <xsl:value-of select="ancestor::persons/person[$pers+1]/favorites/favorite[$fav]/value"/> </td> <td align="right" valign="top" width="40"> <xsl:value-of select="ancestor::persons/person[$pers+2]/favorites/favorite[$fav]/value"/> </td> </tr> </xsl:template> <!-- ignore any other node/element --> <xsl:template match="text()|@*"> </xsl:template> </xsl:stylesheet> In both stylesheets only the processing in the templates for match="favorite" are of interest. The processing of the templates for the match=" person" is only there for making a heading for the html-tables (except of course the "apply-templates" at the ends, which serve only as make-up for this example. Note the addressing-style in the transposing template: <xsl:value-of select="ancestor::persons/person[$pers+x]/favorites/favorite[$fav]/value"/> It's as if you address an element in a two-dimensional array or grid. Optimisation = = = = = = The variable 'pers' as defined as follows: <xsl:variable name="pers" select="1 + count(ancestor::person[1]/preceding-sibling::*)"/> Now, this variable is set for every third favorite item. I suspect (but this is guessing) that this operation (counting siblings) is kind of expensive. So, 1. Can anybody shine a light on this? Are the some general things to say about the processing costs? Should counting or other aggregate functions be avoided if not used sparsely? Anybody with 'experience'? I know some of you say 'why worry about processing costs?', but i'm not worried, i'm curious. 2. Is there an alternative way? (In experimenting i had also found the following way of selecting of the second and third column-values: 2nd col: <xsl:value-of select="../following::favorite[$fav]/value"/> 3rd col: <xsl:value-of select="../following::person[1]/following::favorite[$fav]/value"/> But i left this track, because this would never lead to any generic solution (i think).) Generalisation = = = = = = = Note also that the transposing stylesheet puts persons in tables of three persons max. It creates a new table for every group of three persons. That is all driven by the structure of the "favorite" match template. I had to choose how many persons are put into columns (per table) and here it is three (and that's why there is a "position() mod 3" construct is in the "person" match template. The column values here are obtained by: 1st col: <xsl:value-of select="value"/> 2nd col: <xsl:value-of select="ancestor::persons/person[$pers+1]/favorites/favorite[$fav]/value"/> 3rd col: <xsl:value-of select="ancestor::persons/person[$pers+2]/favorites/favorite[$fav]/value"/> So, 3. Has anybody any idea on how to generalise this? How can i avoid to hard-code in the templates how many columns there will be? How can this number of columns be related to how many persons exist in the xml, without knowing this before hand? Any response appreciated, Oscar XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] RE: need to show spaces, Joerg Pietschmann | Thread | [xsl] Shorter, better, nicer XSLT, Antonio Bueno |
Re: [xsl] RE: need to show spaces, Joerg Heinicke | Date | Re: [xsl] RE: need to show spaces, Anurag Batra |
Month |