I am trying to use XSLT to turn an XML document into plain text tables for human consumption. I am using xsltproc
, which only implements XSLT 1.0 (so max
is from EXSLT actually).
I tried the below, but the commented-out definition fails because string-length
returns only a single value (the length of the first node's string value), not a node-set like max
wants.
Transformation:
<?xml version="1.0" encoding="utf-8"?>
<!-- vim: set sts=2 sw=2: -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math" xmlns:str="http://exslt.org/strings">
<xsl:output method="text"/>
<xsl:template match="/root">
<!-- <xsl:variable name="max_a_width" select="math:max(string-length(data/@a))"/> -->
<xsl:variable name="max_a_width" select="string-length(data/@a)"/>
<xsl:text>+-</xsl:text><xsl:value-of select="str:padding($max_a_width, '-')"/><xsl:text>-+ </xsl:text>
<xsl:for-each select="data">
<xsl:text>| </xsl:text><xsl:value-of select="@a"/><xsl:value-of select="str:padding($max_a_width - string-length(@a), ' ')"/><xsl:text> | </xsl:text>
</xsl:for-each>
<xsl:text>+-</xsl:text><xsl:value-of select="str:padding($max_a_width, '-')"/><xsl:text>-+ </xsl:text>
</xsl:template>
</xsl:stylesheet>
Input:
<?xml version="1.0" encoding="utf-8"?>
<!-- vim: set sts=2 sw=2: -->
<root>
<data a="aa"/>
<data a="aaa"/>
<data a="a"/>
</root>
Output:
+----+
| aa |
| aaa |
| a |
+----+
In order to make the right border line up, I need to have the actual maximum value in the variable. (In my real example I will have column headers and multiple columns, but they are not necessary to reproduce the problem).
If it makes it easier to find a solution, I can guarantee that the data values will never contain spaces.
<xsl:variable name="max_a_width">
<xsl:for-each select="data">
<xsl:sort select="string-length(@a)" data-type="number" />
<xsl:if test="position() = last()">
<xsl:value-of select="string-length(@a)" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
This is the general method of picking from an ordered list of derived values in XSLT 1.0.
If you want to pick the minimum/maximum from actual (natively sortable) values, you can take a short-cut:
<xsl:variable name="max_a" select="//a[not(. < //a)][1]" />