Category Archives: XSL

XSL

XML > 2 > CSV

I was faced with the possibility of writing this from scratch and while I love a challenge, sometimes it’s easier to just post it:)

<?xml version="1.0" encoding="UTF-8"?>
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  version="2.0">

  <xsl:output method="text"/>

  <xsl:variable name="fields" 
                select="distinct-values(//file/*[not(*)]/name(.))"/>

  <xsl:template match="/">
    <!--header row-->
    <xsl:value-of select="$fields" separator=","/>

    <!--body-->
    <xsl:apply-templates select="*"/>

    <!--final line terminator-->
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>

  <!--elements only process elements, not text-->
  <xsl:template match="*">
    <xsl:apply-templates select="*"/>
  </xsl:template>

  <!--these elements are CSV fields-->
  <xsl:template match="file/*[not(*)]">
    <!--replicate ancestors if necessary-->
    <xsl:if test="position()=1 and ../preceding-sibling::file">
      <xsl:for-each select="ancestor::file[position()>1]/*[not(*)]">
        <xsl:call-template name="doThisField"/>
      </xsl:for-each>
    </xsl:if>
    <xsl:call-template name="doThisField"/>
  </xsl:template>

  <!--put out a field ending the previous field and escaping content-->
  <xsl:template name="doThisField">
    <xsl:choose>
      <xsl:when test="name(.)=$fields[1]">
        <!--previous line terminator-->
        <xsl:text>&#xa;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <!--previous field terminator-->
        <xsl:text>,</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <!--field value escaped per RFC4180-->
    <xsl:choose>
      <xsl:when test="contains(.,'&#x22;') or 
                      contains(.,',') or
                      contains(.,'&#xa;')">
        <xsl:text>"</xsl:text>
        <xsl:value-of select="replace(.,'&#x22;','&#x22;&#x22;')"/>
        <xsl:text>"</xsl:text>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  </xsl:stylesheet>

And as a special added bonus. Here is the XSL to eliminate any extraneous information from the pipeline.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
	<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
	<!-- ======================================= -->
	<!-- Identity template -->
	<!-- ======================================= -->
	<xsl:template match="@*|node()">
		<xsl:copy>
			<xsl:apply-templates select="@*|node()"/>
		</xsl:copy>
	</xsl:template>
	<!-- ======================================= -->
	<!-- Exclude the file path attribute. -->
	<!-- ======================================= -->
	<xsl:template match="file">
		<xsl:copy>
			<xsl:apply-templates/>
		</xsl:copy>
	</xsl:template>
	<!-- ======================================= -->
	<!-- Exclude the content element. -->
	<!-- ======================================= -->
	<xsl:template match="file/content"/>
</xsl:stylesheet>

XSL

XSL – Max Date

Was having a devil of a time figuring out how to get the max date out of a node. This post enlightened me to the usage of XSL.

With XSLT 2.0 it is as easy as taking the min and max e.g.

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xsd"
version="2.0">

<xsl:template match="/">
<xsl:variable name="dates" as="xsd:dateTime*"
select="dsQueryResponse/Rows/Row/@StartDate/xsd:dateTime(.)"/>
<xsl:text>Minimum date: </xsl:text>
<xsl:value-of select="min($dates)"/>
<xsl:text>; maximum date: </xsl:text>
<xsl:value-of select="max($dates)"/>
</xsl:template>

</xsl:stylesheet>

You can run XSLT 2.0 in the COM world (e.g. JScript/VBScript, VB(A))
with AltovaXML tools http://www.altova.com/altovaxml.html and in the
.NET world with Saxon 9 (http://saxon.sourceforge.net/).
Sample dataset:

<dsQueryResponse>
<Rows>
<Row ProjectName="Elvis" StartDate="2009-10-01T04:00:00Z"
TargetDate="2009-12-21T04:00:00Z" Overall_x0020_Status="On Track"
_x0025__x0020_Complete=".8" />
<Row ProjectName="Dolly" StartDate="2009-09-11T04:00:00Z"
TargetDate="2009-12-01T04:00:00Z" Overall_x0020_Status="On Track"
_x0025__x0020_Complete=".66" />
<Row ProjectName="CCR" StartDate="2009-09-21T04:00:00Z"
TargetDate="2009-11-21T04:00:00Z" Overall_x0020_Status="Manageable
Issues" _x0025__x0020_Complete=".90" />
<Row ProjectName="Stones" StartDate="2009-09-15T04:00:00Z"
TargetDate="2009-12-28T04:00:00Z" Overall_x0020_Status="Project
Concern" _x0025__x0020_Complete=".50" />
</Rows>
</dsQueryResponse>