如何使用XSLT 1.0来右对齐纯文本输出?

时间:2012-01-13 22:33:30

标签: xslt

我正在处理一个如下所示的XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="align-test.xsl"?>
<alignTest>
    <item name="Some Name" number="3"></item>
    <item name="Another Name" number="10"></item>
    <item name="A Third One" number="43"></item>
    <item name="A Really Long Name" number="100"></item>
</alignTest>

我的目标是输出一个纯文本文件,其中包含格式良好的表格。我已经想出如何使用这个样式表对齐和填充文本列和分隔符:

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

        <xsl:for-each select="alignTest/item">
            <!-- Scroll right. The next line keeps going, but might not look like it due to formatting -->
            <xsl:value-of select="substring(concat(@name, '                         '), 1, 22)"/>
            <xsl:text> | </xsl:text>
            <xsl:value-of select="@number"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>

    </xsl:template>
</xsl:stylesheet>

哪个输出:

Some Name              | 3
Another Name           | 10
A Third One            | 43
A Really Long Name     | 100

我也希望数值正确对齐。像这样:

Some Name              |   3
Another Name           |  10
A Third One            |  43
A Really Long Name     | 100

如何使用XSLT 1.0以这种方式对纯文本进行右对齐?

2 个答案:

答案 0 :(得分:4)

这是一个更易读,更有效的答案版本

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="item">
        <xsl:call-template name="padRightSide">
            <xsl:with-param name="stringToPad" select="@name"/>
            <xsl:with-param name="totalLength" select="22"/>
        </xsl:call-template>

        <xsl:text>|</xsl:text>

        <xsl:call-template name="padLeftSide">
            <xsl:with-param name="stringToPad" select="@number"/>
            <xsl:with-param name="totalLength" select="5"/>
        </xsl:call-template>

        <xsl:text>&#10;</xsl:text>
 </xsl:template>

 <!-- template to pad the left side of strings (and right justificy) -->
 <xsl:template name="padLeftSide">
    <xsl:param name="stringToPad"/>
    <xsl:param name="totalLength"/>
    <xsl:param name="padChar" select="' '"/>
    <xsl:param name="padBuffer" select=
    "concat($padChar,$padChar,$padChar,$padChar,$padChar,
            $padChar,$padChar,$padChar,$padChar,$padChar
            )"/>
    <xsl:variable name="vNewString" select=
                       "concat($padBuffer, $stringToPad)"/>

    <xsl:choose>
        <xsl:when test="not(string-length($vNewString) >= $totalLength)">
            <xsl:call-template name="padLeftSide">
                <xsl:with-param name="stringToPad" select="$vNewString"/>
                <xsl:with-param name="totalLength" select="$totalLength"/>
                <xsl:with-param name="padChar" select="$padChar"/>
                <xsl:with-param name="padBuffer" select="$padBuffer"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select=
            "substring($vNewString, 
                       string-length($vNewString) - $totalLength + 1)"/>
        </xsl:otherwise>
    </xsl:choose>
 </xsl:template>

 <!-- template to pad the right side of strings -->
 <xsl:template name="padRightSide">
    <xsl:param name="totalLength"/>
    <xsl:param name="padChar" select="' '"/>
    <xsl:param name="stringToPad"/>
    <xsl:param name="padBuffer" select=
    "concat($padChar,$padChar,$padChar,$padChar,$padChar,
            $padChar,$padChar,$padChar,$padChar,$padChar
            )"/>
    <xsl:variable name="vNewString" select=
                       "concat($stringToPad, $padBuffer)"/>
    <xsl:choose>
        <xsl:when test="not(string-length($vNewString) >= $totalLength)">
            <xsl:call-template name="padRightSide">
                <xsl:with-param name="stringToPad" select="$vNewString"/>
                <xsl:with-param name="totalLength" select="$totalLength"/>
                <xsl:with-param name="padChar" select="$padChar"/>
                <xsl:with-param name="padBuffer" select="$padBuffer"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="substring($vNewString,1,$totalLength)"/>
        </xsl:otherwise>
    </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

另一项改进是动态计算最大字符串长度而不必手动计算

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vNamesMaxLen">
  <xsl:call-template name="maxLength">
   <xsl:with-param name="pNodes"
                   select="/*/item/@name"/>
  </xsl:call-template>
 </xsl:variable>


 <xsl:variable name="vNumsMaxLen">
  <xsl:call-template name="maxLength">
   <xsl:with-param name="pNodes"
                   select="/*/item/@number"/>
  </xsl:call-template>
 </xsl:variable>

 <xsl:template match="item">
        <xsl:call-template name="padRightSide">
            <xsl:with-param name="stringToPad"
                            select="@name"/>
            <xsl:with-param name="totalLength"
                            select="$vNamesMaxLen+1"/>
        </xsl:call-template>

        <xsl:text>|</xsl:text>

        <xsl:call-template name="padLeftSide">
            <xsl:with-param name="stringToPad"
                            select="@number"/>
            <xsl:with-param name="totalLength"
                            select="$vNumsMaxLen+1"/>
        </xsl:call-template>

        <xsl:text>&#10;</xsl:text>
 </xsl:template>

 <xsl:template name="maxLength">
  <xsl:param name="pNodes" select="/.."/>

  <xsl:for-each select="$pNodes">
   <xsl:sort select="string-length()" data-type="number"
             order="descending"/>
    <xsl:if test="position() = 1">
     <xsl:value-of select="string-length()"/>
    </xsl:if>
  </xsl:for-each>
 </xsl:template>

 <!-- template to pad the left side of strings (and right justificy) -->
 <xsl:template name="padLeftSide">
    <xsl:param name="stringToPad"/>
    <xsl:param name="totalLength"/>
    <xsl:param name="padChar" select="' '"/>
    <xsl:param name="padBuffer" select=
    "concat($padChar,$padChar,$padChar,$padChar,$padChar,
            $padChar,$padChar,$padChar,$padChar,$padChar
            )"/>
    <xsl:variable name="vNewString" select=
                    "concat($padBuffer, $stringToPad)"/>

    <xsl:choose>
        <xsl:when test="not(string-length($vNewString) >= $totalLength)">
            <xsl:call-template name="padLeftSide">
                <xsl:with-param name="stringToPad" select="$vNewString"/>
                <xsl:with-param name="totalLength" select="$totalLength"/>
                <xsl:with-param name="padChar" select="$padChar"/>
                <xsl:with-param name="padBuffer" select="$padBuffer"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select=
            "substring($vNewString, 
                       string-length($vNewString) - $totalLength + 1)"/>
        </xsl:otherwise>
    </xsl:choose>
 </xsl:template>

 <!-- template to pad the right side of strings -->
 <xsl:template name="padRightSide">
    <xsl:param name="totalLength"/>
    <xsl:param name="padChar" select="' '"/>
    <xsl:param name="stringToPad"/>
    <xsl:param name="padBuffer" select=
    "concat($padChar,$padChar,$padChar,$padChar,$padChar,
            $padChar,$padChar,$padChar,$padChar,$padChar
            )"/>
    <xsl:variable name="vNewString" select=
                    "concat($stringToPad, $padBuffer)"/>
    <xsl:choose>
        <xsl:when test="not(string-length($vNewString) >= $totalLength)">
            <xsl:call-template name="padRightSide">
                <xsl:with-param name="stringToPad" select="$vNewString"/>
                <xsl:with-param name="totalLength" select="$totalLength"/>
                <xsl:with-param name="padChar" select="$padChar"/>
                <xsl:with-param name="padBuffer" select="$padBuffer"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="substring($vNewString,1,$totalLength)"/>
        </xsl:otherwise>
    </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

我在this page找到了一些答案。

描述的简单方法是做类似的事情:

<xsl:value-of select="substring(concat('    ', @number), string-length(@number) + 1, 4)"/>

该页面还列出了几个用于填充左侧和右侧的模板。他们递归地称呼自己并填充适当的金额。 (请注意,如果请求的长度小于正在处理的字符串的长度,它们也将截断。)模板还取消了更改用于填充的字符的功能。

他们需要更多代码来实现,但可能更容易维护。这是使用两个模板更新的原始样式表的一个版本,以显示它们的用法:

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

    <xsl:template match="/">
        <xsl:for-each select="alignTest/item">

            <xsl:call-template name="padRightSide">
                <xsl:with-param name="stringToPad" select="@name"></xsl:with-param>
                <xsl:with-param name="totalLength" select="22"></xsl:with-param>
                <xsl:with-param name="padCharacter" select="' '"></xsl:with-param>
            </xsl:call-template>

            <xsl:text>|</xsl:text>

            <xsl:call-template name="padLeftSide">
                <xsl:with-param name="stringToPad" select="@number"/>
                <xsl:with-param name="totalLength" select="5"/>
                <xsl:with-param name="padCharacteracter" select="' '"/>
            </xsl:call-template>

            <xsl:text>&#10;</xsl:text>

        </xsl:for-each>
    </xsl:template>

    <!-- template to pad the left side of strings (and right justificy) -->
    <xsl:template name="padLeftSide">
        <xsl:param name="stringToPad"/>
        <xsl:param name="totalLength"/>
        <xsl:param name="padCharacteracter"/>

        <xsl:choose>
            <xsl:when test="string-length($stringToPad) &lt; $totalLength">
                <xsl:call-template name="padLeftSide">
                    <xsl:with-param name="stringToPad" select="concat($padCharacteracter,$stringToPad)"/>
                    <xsl:with-param name="totalLength" select="$totalLength"/>
                    <xsl:with-param name="padCharacteracter" select="$padCharacteracter"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="substring($stringToPad,string-length($stringToPad) - $totalLength + 1)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <!-- template to pad the right side of strings -->
    <xsl:template name="padRightSide">
        <xsl:param name="padCharacter"/> 
        <xsl:param name="stringToPad"/>
        <xsl:param name="totalLength"/>
        <xsl:choose>
            <xsl:when test="string-length($stringToPad) &lt; $totalLength">
                <xsl:call-template name="padRightSide">
                    <xsl:with-param name="padCharacter" select="$padCharacter"/>
                    <xsl:with-param name="stringToPad" select="concat($stringToPad,$padCharacter)"/>
                    <xsl:with-param name="totalLength" select="$totalLength"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="substring($stringToPad,1,$totalLength)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

当然,您可以通过对填充字符进行硬编码来缩小其占用空间的大小。