XSLT 1.0: Выполните итерации по символам в строке

Я должен выполнить итерации по символам в строке для создания структуры XML.

В настоящее время я делаю это:

<xsl:template name="verticalize">
    <xsl:param name="text">Some text</xsl:param>
    <xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
        <xsl:element name="para">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:for-each>
</xsl:template>

Это производит что-то как:

<para>S</para>
<para>o</para>
<para>m</para>
<para>e</para>
<para> </para>
<para>t</para>
<para>e</para>
<para>x</para>
<para>t</para>

Это хорошо работает с Xpath 2.0. Но я должен применить ту же обработку в среде XPath 1.0, где replace() метод не доступен.

Вы знаете способ достигнуть этого?

16
задан glmxndr 6 May 2010 в 14:36
поделиться

4 ответа

<xsl:template name="letters">
  <xsl:param name="text" select="'Some text'" />
  <xsl:if test="$text != ''">
    <xsl:variable name="letter" select="substring($text, 1, 1)" />
    <para><xsl:value-of select="$letter" /></para>
    <xsl:call-template name="letters">
      <xsl:with-param name="text" select="substring-after($text, $letter)" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>
17
ответ дан 30 November 2019 в 17:04
поделиться

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

Примерно так:

<xsl:template name="verticalize">
    <xsl:param name="text">Some text</xsl:param>
    <xsl:param name="index" select="1" />
    <xsl:if test="string-length($text) &gt;= $index">
        <xsl:element name="para">
            <xsl:value-of select="substring($text, $index, 1)"/>
        </xsl:element>
        <xsl:call-template name="verticalize">
            <xsl:with-param name="text" select="$text" />
            <xsl:with-param name="index" select="$index+1" />
        </xsl:call-template>
    </xsl:if>
</xsl:template>

Если строка длиннее, вы можете использовать аналогичный подход, но с алгоритмом «разделяй и властвуй», чтобы получить максимальную глубину рекурсии log2 (длина строки), например так:

<xsl:template name="verticalize">
    <xsl:param name="text">Some text</xsl:param>
    <xsl:param name="left" select="1" />
    <xsl:param name="right" select="string-length($text)" />
    <xsl:choose>
        <xsl:when test="$left = $right">
            <xsl:element name="para">
                <xsl:value-of select="substring($text, $left, 1)"/>
            </xsl:element>
        </xsl:when>
        <xsl:when test="$left &lt; $right">
            <xsl:variable name="middle" select="floor(($left+$right) div 2)" />
            <xsl:call-template name="verticalize">
                <xsl:with-param name="text" select="$text" />
                <xsl:with-param name="left" select="$left" />
                <xsl:with-param name="right" select="$middle" />
            </xsl:call-template>
            <xsl:call-template name="verticalize">
                <xsl:with-param name="text" select="$text" />
                <xsl:with-param name="left" select="$middle+1" />
                <xsl:with-param name="right" select="$right" />
            </xsl:call-template>
        </xsl:when>
    </xsl:choose>
</xsl:template>
8
ответ дан 30 November 2019 в 17:04
поделиться

Решение XSLT 2.0:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vText" select="'Some Text'"/>

 <xsl:template match="/">
    <xsl:for-each select="string-to-codepoints($vText)">
      <para><xsl:sequence select="codepoints-to-string(.)"/></para>
    </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

Тем из вас, кто изучает XSLT 2.0 / XPath 2.0, обратите внимание на :

  1. Использование стандартных функций XPath 2.0 преобразование строки в код () и codepoints-to-string () .

  2. В XSLT 2.0 значение атрибута select в может быть последовательностью любых элементов, а не только узлов.

7
ответ дан 30 November 2019 в 17:04
поделиться

Решение XSLT 1.0 с использованием FXSL

Библиотека FXSL предлагает ряд общих функций для обработки списков. Почти все они имеют аналог для работы со строками (рассматривая строку как список символов).

Вот пример с использованием функции/шаблона str-foldl:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dvc-foldl-func="dvc-foldl-func"
exclude-result-prefixes="xsl dvc-foldl-func"
>

   <xsl:import href="dvc-str-foldl.xsl"/>

   <dvc-foldl-func:dvc-foldl-func/>
   <xsl:variable name="vFoldlFun" select="document('')/*/dvc-foldl-func:*[1]"/>
    <xsl:output  encoding="UTF-8" omit-xml-declaration="yes"/>

    <xsl:template match="/">

      <xsl:call-template name="dvc-str-foldl">
        <xsl:with-param name="pFunc" select="$vFoldlFun"/>
        <xsl:with-param name="pStr" select="123456789"/>
        <xsl:with-param name="pA0" select="0"/>
      </xsl:call-template>
    </xsl:template>

    <xsl:template match="dvc-foldl-func:*">
         <xsl:param name="arg1" select="0"/>
         <xsl:param name="arg2" select="0"/>

         <xsl:value-of select="$arg1 + $arg2"/>
    </xsl:template>

</xsl:stylesheet>

Это преобразование вычисляет сумму символов в строке, переданной в качестве параметра $pStr, и выдает правильный результат:

45

А используя шаблон/функцию str-map, мы имеем следующее простое и короткое решение:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testmap="testmap"
exclude-result-prefixes="xsl testmap"
>
   <xsl:import href="str-dvc-map.xsl"/>

   <!-- to be applied on any xml source -->

   <testmap:testmap/>

   <xsl:output omit-xml-declaration="yes" indent="yes"/>

   <xsl:template match="/">
     <xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/>
     <xsl:call-template name="str-map">
       <xsl:with-param name="pFun" select="$vTestMap"/>
       <xsl:with-param name="pStr" select="'Some Text'"/>
     </xsl:call-template>
   </xsl:template>

    <xsl:template name="split" match="*[namespace-uri() = 'testmap']">
      <xsl:param name="arg1"/>

      <para><xsl:value-of select="$arg1"/></para>
    </xsl:template>

</xsl:stylesheet>

При применении к любому XML файлу (не использованному), получается желаемый, правильный результат:

<para>S</para>
<para>o</para>
<para>m</para>
<para>e</para>
<para> </para>
<para>T</para>
<para>e</para>
<para>x</para>
<para>t</para>
2
ответ дан 30 November 2019 в 17:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: