(xslt 1.0) Как заменить пробел некоторой строкой из всех текстовых значений в xml? - PullRequest
3 голосов
/ 23 февраля 2010

РЕДАКТИРОВАТЬ: [это началось с замены персонажа, и я закончил с обнаружением замены строки с помощью Dimitre Novatchev и Roland Bouman

Я думаю, что примеры кодов достаточны для объяснения требований ..

Это пример XML:

<root>
  <node1>text node</node1>
  <node2>space between the text</node2>
  <node3> has to be replaced with $</node3>
</root>

Это ожидаемый результат:

<root>
  <node1>text$node</node1>
  <node2>space$between$the$text</node2>
  <node3>$has$to$be$replaced$with$$</node3>
</root>

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

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
  <xsl:template match="text()[.!='']">
    <xsl:call-template name="rep_space">
      <xsl:with-param name="text" select="."/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="rep_space">
    <xsl:param name="text"/>
    <xsl:variable name="temp" select="'&#x36;'"/> 
    <xsl:choose>
      <xsl:when test="contains(text,'&#x32;')">
        <xsl:call-template name="rep_space">
          <xsl:with-param name="text" select="concat((concat(substring-before(text,' '),temp)),substring-after(text,' '))"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

translate (., 'функция ',' $ ') работает .. но не в достаточной степени .. мои вопросы ... что если это строка вместо символа?Я имею в виду, предположим, что я собираюсь заменить '' на "% 20"?И еще один случай: что, если входной XML не «Pretty Print XML», тогда все пространство, появляющееся в XML, заменяется на «$» ..

Pretty print XML - это файл с правильным отступом (обычно мои входные XML никогда не имеют этого), например:

еще один узел, это @ более низкий уровень

Вы можете наблюдать, есть нет "пробелов" перед <new> <test> узлами, но они на самом деле имеют правильные отступы (с помощью altova XMLSPY мы можем дать простую команду в меню редактирования ... чтобы сделать любые файлы XML "красивыми печатными XML") ..

Где, как в примере ниже ..

<new>
  <test>one more node</test>
   <test2>
    <child>this is @ lower level</child>
   </test2>
</new>

Перед всеми начальными тегами стоят пробелы. Тег <child> имеет больше пробелов, чем узел <test2>.

Со вторым примером xml .. все пробелы заменяются на "%20" .. следовательно, результат будет ..

<new>
%20%20<test>one%20more%20node</test>
%20%20<test2>
%20%20%20%20<child>this%20is%20@%20lower%20level</child>
%20%20</test2>
</new>

, конечно, это не ожидается ..

Решения от Dimitre Novatchev и RoЛенд Боуман может также заменить строку другой строкой, изменив параметры, передаваемые вызываемому шаблону.

Это было отличное обучение @Dimitre, @Roland, я очень благодарен и благодаренВам, ребята ..

С уважением,
Children's Pro.

Ответы [ 3 ]

8 голосов
/ 25 февраля 2010

По желанию Роланд , здесь есть хвостово-рекурсивное решение :

 <xsl:template name="replace">
  <xsl:param name="ptext"/>
  <xsl:param name="ppattern"/>
  <xsl:param name="preplacement"/>

  <xsl:choose>
     <xsl:when test="not(contains($ptext, $ppattern))">
      <xsl:value-of select="$ptext"/>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="substring-before($ptext, $ppattern)"/>
       <xsl:value-of select="$preplacement"/>
       <xsl:call-template name="replace">
         <xsl:with-param name="ptext"
           select="substring-after($ptext, $ppattern)"/>
         <xsl:with-param name="ppattern" select="$ppattern"/>
         <xsl:with-param name="preplacement" select="$preplacement"/>
       </xsl:call-template>
     </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

Обратите внимание, что рекурсивный вызов является последней инструкцией в шаблоне - это то, что делает его хвост-рекурсивным. Свойство хвостовой рекурсии позволяет интеллектуальному процессору XSLT (например, Saxon или .NET XslCompiledTransform) оптимизировать код, заменяя рекурсию простой итерацией.

Такой код не закончится исключением переполнения стека, даже если «вложенность» вызовов составляет миллионы, тогда как нерекурсивный (и рекурсивный) код обычно вызывает это переполнение стека на глубине около 1000 вложенных звонки (это действительно зависит от объема доступной памяти).

Что если процессор XSLT недостаточно "умен"? Есть ли другой метод, позволяющий избежать глубокого переполнения стека рекурсивных вызовов, который работает с каждым XSLT-процессором?

Задайте мне отдельный вопрос, и я мог бы сказать вам:)

4 голосов
/ 23 февраля 2010

Проверьте функцию перевода XPath: http://www.w3.org/TR/xpath/#function-translate

<xsl:template match="text()">
    <xsl:value-of select="translate(., ' ', '$')"/>
</xsl:template>

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

<xsl:template match="text()[not(../*)]">
    <xsl:call-template name="replace">
        <xsl:with-param name="text" select="."/>
        <xsl:with-param name="search" select="' '"/>
        <xsl:with-param name="replace" select="'%20'"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="replace">
    <xsl:param name="text"/>
    <xsl:param name="search"/>
    <xsl:param name="replace"/>
    <xsl:choose>
        <xsl:when test="contains($text, $search)">
            <xsl:variable name="replace-next">
                <xsl:call-template name="replace">
                    <xsl:with-param name="text" select="substring-after($text, $search)"/>
                    <xsl:with-param name="search" select="$search"/>
                    <xsl:with-param name="replace" select="$replace"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:value-of 
                select="
                    concat(
                        substring-before($text, $search)
                    ,   $replace
                    ,   $replace-next
                    )
                "
            />
        </xsl:when>
        <xsl:otherwise><xsl:value-of select="$text"/></xsl:otherwise>
    </xsl:choose>
</xsl:template>

Редактировать: изменено соответствие = "text ()" на match = "text () [not (../*)]", так что входной XML не должен быть своего рода "довольно распечатать XML" .. (чтобы удалить нежелательные замены пространства Строка "% 20" в таком xml-файле)

1 голос
/ 23 февраля 2010

Решение "prety-print xml" на самом деле не является решением.

Представьте, что у вас есть такой документ:

<a>
 <b>
  <c>O M G</c>
  <d>D I Y</d>
 </b>
</a>

Выходные данные из принятого в настоящее время решения (после помещения его в <xsl:stylesheet> и добавления правила идентификации:

<a>
%20<b>
%20%20<c>O$M$G</c>
%20%20<d>D$I$Y</d>
%20</b>
</a>

Теперь, почему предложенный обходной путь не спасает ситуацию? Как видно из приведенного выше примера, элемент может иметь более одного дочернего элемента с текстовыми узлами ...

Какое реальное решение ?

Создатели XSLT подумали об этой проблеме. Используя правильную терминологию, мы хотим, чтобы все незначительные текстовые узлы только для пробелов игнорировались процессором XSLT, как если бы они вообще не были частью дерева документа. Это достигается с помощью инструкции <xsl:strip-space>.

Просто добавьте это на глобальном уровне (как потомок <xsl:stylesheet> и, для удобства чтения, перед любыми шаблонами):

 <xsl:strip-space elements="*"/>

и теперь у вас действительно есть рабочее решение.

...