RE: [xsl] Formatting XSL for XML to CSV translation

Subject: RE: [xsl] Formatting XSL for XML to CSV translation
From: "Passin, Tom" <tpassin@xxxxxxxxxxxx>
Date: Thu, 18 Sep 2003 11:38:54 -0400
[ Stuart Jones]

> For readability I would prefer the XSL file to look nicely 
> nested like this:
> 
>   <xsl:variable name="new_line" select="'&#013;'"/>
>   <xsl:template match="/">
> 
>     <xsl:for-each select="xml/rs:data/z:row">
>       <xsl:value-of select="@field1"/>,
>       <xsl:value-of select="@field2"/>,
>       ...
>       <xsl:value-of select="@fieldn"/>
>       <xsl:value-of select="$new_line"/>
>     </xsl:for-each>
> 
>   </xsl:template>
> 
> Unfortunately, this means that each value ends up on its own 
> line. Placing each element in one long line in the XSL solves 
> the problem but is hard to read (to say the least).
> 

I often do it the following way - it seems odd at first, but is easy and
effective -

      <xsl:value-of select="@field1"
      />,<xsl:value-of select="@field2"
     />, ....

With that out of the way, I would not use your method at all.  There is
no need to enumerate all the fields.  It seems that your row data is
contained in attributes (next time, don't make us guess about that). If
you do not name the fields, you remove the need to change the stylesheet
if your data source design changes, and you make the styesheet simpler
as well..

Suppose first that the only attributes in a row element are the actual
data fields.  Then 

1) Include  a statement like (assuming that the data are contained in a
"row" element) -

    <xsl:apply-templates select='row'/>

2) Process the rows like this.  For clarity I ignore the need to
suppress the last trailing comma, then later I deal with it -

<xsl:template match="row">
	<xsl:apply-templates select='@*' mode='fields'/>
	<xsl:text>&#10;</xsl:text> <!-- or any other characters you
want-->
</xsl:template>

<xsl:template match='@*' mode='fields'>
	<xsl:value-of select='.'/>,
</xsl:template>

The mode attribute acts as a safety feature to prevent other attributes
from bring processed by the second template.

3) Suppress the last comma like this -

	<xsl:value-of select='.'/><xsl:if test='not(position() =
last())'>,</xsl:if>

See how easy it can be?  Just let the processor do its job for you.

There is one possible disadvantage to this method.  The order of
attributes cannot be guaranteed, so if the order is important, you would
have to name the fields in the right order after all.  However, the
order should not be important because with a relational database, the
field order is not significant either., and of course the data was
serialized as attributes in the first place.  You can always handle it
in later processing if needed.

Note that this method can work just as well when the data is contained
in elements instead of attributes. Just remove the "@" characters and
everything else will stay the same.

If you have any other attributes in a row element that are not data
fields (and whose names do not contain the string "field"), you can
modify the select expression like this -

	<xsl:apply-templates select='@*[starts-with(name(),"field")]'
mode='fields'/>

Your approach is a typical procedural programming approach - go through
the fields you want one by one, and put them into the right position.
With xslt it is almost always better to take a different approach.  The
best approach is to choose the collection of nodes you want to operate
on, and say what should happen to them in terms of what the result
should look like.  The processor will then apply your instructions to
all the nodes you selected.  

This is called the "declarative" approach.  Typically, the main problem
becomes one of selecting the right collection of nodes, and sometimes,
of grouping them into larger collections.

Cheers,

Tom P

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread