Как подсчитать значения в элементе XML, используя XSLT 1 - PullRequest
0 голосов
/ 28 сентября 2018

У меня есть набор документов XML, которые содержат несколько больших списков значений в одном элементе XML.Мне нужно определить, насколько велик каждый список, и выводить счетчик только тогда, когда они слишком велики.Мне необходимо использовать xsltproc, который поддерживает только 1.0 и пытался использовать функцию count (), но, похоже, не выдает никакого значения, кроме 1. Пример таблицы стилей:

<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                >

  <!-- NOTE: US-ASCII encoding is not compatible with Java HTML text -->
  <xsl:output method="html" indent="yes" encoding="ASCII"/>

  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
      <head>
        <title><xsl:value-of select="'Test Case for count()'"/></title>
      </head>
      <body>
        <xsl:element name="table">
          <xsl:attribute name="border">1</xsl:attribute>
          <xsl:attribute name="align">center</xsl:attribute>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel" select="'count'"/>
            <xsl:with-param name="DataValue" select="function-available('count')"/>
          </xsl:call-template>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel" select="'normalize-space'"/>
            <xsl:with-param name="DataValue" select="function-available('normalize-space')"/>
          </xsl:call-template>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel" select="'string-length'"/>
            <xsl:with-param name="DataValue" select="function-available('string-length')"/>
          </xsl:call-template>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel" select="'replace'"/>
            <xsl:with-param name="DataValue" select="function-available('replace')"/>
          </xsl:call-template>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel" select="'tokenize'"/>
            <xsl:with-param name="DataValue" select="function-available('tokenize')"/>
          </xsl:call-template>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel" select="'contains'"/>
            <xsl:with-param name="DataValue" select="function-available('contains')"/>
          </xsl:call-template>

          <xsl:variable name="DataIn" select="' A B C '"/>
          <xsl:variable name="DataList">
            <xsl:call-template name="Tokenize-Str">
              <xsl:with-param name="Data" select="$DataIn"/>
            </xsl:call-template>
          </xsl:variable>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel"
                            select="concat('tokenize(',$DataIn,')')"/>
            <xsl:with-param name="DataValue">
              <xsl:call-template name="Tokenize-Str">
                <xsl:with-param name="Data" select="$DataIn"/>
              </xsl:call-template>
            </xsl:with-param>
          </xsl:call-template>

          <xsl:call-template name="DblColTableDataRow">
            <xsl:with-param name="DataLabel"
                            select="concat('count(',$DataIn,')')"/>
            <xsl:with-param name="DataValue">
              <xsl:copy-of select="$DataList"/>
              <xsl:text>: </xsl:text>
              <xsl:value-of select="count(($DataList))"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:element>
      </body>
    </html>
  </xsl:template>

  <xsl:template name="DblColTableDataRow">
    <xsl:param name="DataLabel" select="'?:'"/>
    <xsl:param name="DataValue" select="'???'"/>
    <xsl:element name="tr">
      <xsl:element name="td">
        <xsl:attribute name="style">text-align:right</xsl:attribute>
        <xsl:copy-of select="$DataLabel"/>
      </xsl:element>
      <xsl:element name="td">
        <xsl:copy-of select="$DataValue"/>
      </xsl:element>
    </xsl:element>
  </xsl:template>

  <!-- template needed because tokenize function not supported -->
  <xsl:template name="Tokenize-Str">
    <xsl:param name="Data"/>
    <xsl:variable name="DataStr">
      <xsl:value-of select="normalize-space($Data)"/>
    </xsl:variable>
    <xsl:if test="0 != string-length($DataStr)">
      <!--xsl:value-of select="concat('Tkn-Str(',$Data,')')"/-->
      <xsl:choose>
        <xsl:when test="contains($DataStr,' ')">
          <xsl:element name="tkn">
            <xsl:value-of select="substring-before($DataStr, ' ')"/>
          </xsl:element>
          <xsl:call-template name="Tokenize-Str">
            <xsl:with-param name="Data"
                            select="substring-after($DataStr, ' ')"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:element name="tkn">
            <xsl:value-of select="$DataStr"/>
          </xsl:element>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

Этонастроить так, чтобы содержимое документа XML не имело значения.Команда:

xsltproc -o tst.html test_case.xsl whatever.xml

Производит:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=ASCII">
<title>Test Case for count()</title></head><body><table border="1" align="center"><tr xmlns="">
<td style="text-align:right">count</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">normalize-space</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">string-length</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">replace</td>
<td>false</td>
</tr>
<tr xmlns="">
<td style="text-align:right">tokenize</td>
<td>false</td>
</tr>
<tr xmlns="">
<td style="text-align:right">contains</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">tokenize( A B C )</td>
<td>
<tkn>A</tkn><tkn>B</tkn><tkn>C</tkn>
</td>
</tr>
<tr xmlns="">
<td style="text-align:right">count( A B C )</td>
<td>
<tkn>A</tkn><tkn>B</tkn><tkn>C</tkn>: 1</td>
</tr></table></body></html>

Я не уверен, почему я получаю счет 1, поскольку мой шаблон явно возвращает 3 узла элемента.

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

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

Вот простой пример:

XML

<input>alpha bravo charlie</input>

XSLT 1.0

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

<xsl:template match="/">
    <count-tokens>
        <xsl:value-of select="string-length(input) - string-length(translate(input, ' ', '')) + 1"/>
    </count-tokens>
</xsl:template>

</xsl:stylesheet>

Результат

<?xml version="1.0" encoding="UTF-8"?>
<count-tokens>3</count-tokens>
0 голосов
/ 28 сентября 2018

При использовании чистого XSLT 1 любая переменная, содержащая результирующие узлы, созданные с помощью xsl:element или буквальных элементов результата, является фрагментом результирующего дерева https://www.w3.org/TR/xslt-10/#section-Result-Tree-Fragments, который представляет собой структуру данных, сильно отличающуюся от набора узлов, который вы получаете из своего вводаdocument (s).

Таким образом, ваша переменная $DataList является таким фрагментом результирующего дерева, который вы можете вывести с помощью xsl:copy-of, но вы не можете использовать XPath для его содержимого, для этого вам нужна функция расширения, такая как exsl:node-set (http://exslt.org/exsl/index.html) например, <xsl:value-of select="count(exsl:node-set($DataList)/*)" xmlns:exsl="http://exslt.org/common"/> даст вам искомое количество (поскольку функция exsl:node-set преобразует фрагмент вашего результирующего дерева в корневой узел, содержащий ваши узлы результирующего элемента).

Обратите внимание, что xsltproc должен поддерживать http://exslt.org/str/functions/tokenize/index.html,, поэтому вы просто можете использовать, например, <xsl:value-of select="count(str:tokenize('A B C', ' '))"/> с соответствующим объявлением пространства имен xmlns:str="http://exslt.org/strings" в вашей таблице стилей.

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