Как я могу использовать XSLT 1.0, чтобы правильно выровнять вывод простого текста? - PullRequest
3 голосов
/ 14 января 2012

Я работаю с файлом 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 ]

4 голосов
/ 14 января 2012

Вот более читаемая и более эффективная версия вашего ответа :

<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 голос
/ 14 января 2012

Я нашел несколько ответов на этой странице .

Простой способ описан так:

<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>

Конечно, вы могли бы немного уменьшить размер их следа, жестко закодировав символ заполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...