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]

reordening sequence, transposing rows, columns: generalisation and optimisation




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


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