Как рассчитать максимальную длину строки набора узлов?

Я пытаюсь использовать XSLT, чтобы превратить XML-документ в простые текстовые таблицы для человеческого восприятия. Я использую xsltproc, который реализует только XSLT 1.0 (так что max на самом деле из EXSLT).

Я пробовал ниже, но закомментированное определение не работает, потому что string-length возвращает только одно значение (длину строкового значения первого узла), а не набор узлов, как хочет max.

Трансформация:

<?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>-+&#10;</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> |&#10;</xsl:text>
    </xsl:for-each>
    <xsl:text>+-</xsl:text><xsl:value-of select="str:padding($max_a_width, '-')"/><xsl:text>-+&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

Вход:

<?xml version="1.0" encoding="utf-8"?>
<!-- vim: set sts=2 sw=2: -->
<root>
  <data a="aa"/>
  <data a="aaa"/>
  <data a="a"/>
</root>

Выход:

+----+
| aa |
| aaa |
| a  |
+----+

Чтобы сделать правильную линию границы, мне нужно иметь фактическое максимальное значение в переменной. (В моем реальном примере у меня будут заголовки столбцов и несколько столбцов, но они не нужны для воспроизведения проблемы).

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


person o11c    schedule 22.06.2015    source источник


Ответы (3)


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

Это общий метод выбора из упорядоченного списка производных значений в XSLT 1.0.

Если вы хотите выбрать минимальное/максимальное значение из фактических (изначально сортируемых) значений, вы можете использовать более короткий путь:

<xsl:variable name="max_a" select="//a[not(. &lt; //a)][1]" />
person Tomalak    schedule 22.06.2015

Вот как это сделать с помощью EXSLT math:max():

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:math="http://exslt.org/math" 
xmlns:str="http://exslt.org/strings">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:template match="/root">
    <xsl:variable name="lengths">
        <xsl:for-each select="data">
            <length><xsl:value-of select="string-length(@a)"/></length>
        </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="max_a_width" select="math:max(exsl:node-set($lengths)/length)"/>
    <!-- the rest -->
</xsl:template>

</xsl:stylesheet>
person michael.hor257k    schedule 22.06.2015

Вот простой рекурсивный шаблон. Запустите его с тем же файлом XSLT в качестве входных данных, чтобы проверить его. Это, конечно, проще с XSLT 2 или XQuery...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:template name="maxlen">
    <xsl:param name="input" />
    <xsl:param name="max-so-far" select="0" />

    <xsl:choose>
      <xsl:when test="not($input)">
        <xsl:value-of select="$max-so-far" />
      </xsl:when>
      <xsl:when test="string-length($input[1]) &gt; $max-so-far">
        <xsl:call-template name="maxlen">
          <xsl:with-param name="input" select="$input[position() &gt; 1]" />
          <xsl:with-param name="max-so-far"
            select="string-length($input[1])" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="maxlen">
          <xsl:with-param name="input" select="$input[position() &gt; 1]" />
          <xsl:with-param name="max-so-far" select="$max-so-far" />
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:variable name="input">
    <test>
      <string>123456789012345678901234</string>
      <string>12345678901234</string>
      <string>1234567890</string>
      <string>12345678901234567890123456</string>
    </test>
  </xsl:variable>

  <xsl:template match="/">
    <output>
      <xsl:call-template name="maxlen">
        <xsl:with-param name="input" select="//test/string" />
      </xsl:call-template>
    </output>
  </xsl:template>


</xsl:stylesheet>

person barefootliam    schedule 22.06.2015