(xslt 1.0) Как заменить пробел некоторой строкой из всех текстовых значений в xml?

StackOverflow https://stackoverflow.com/questions/2318118

  •  22-09-2019
  •  | 
  •  

Вопрос

Редактировать: [это началось с замены символов и я закончил обнаружением строки замены с помощью Дмитрий Новатчев и Роланд Боуман

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

Это пример 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>

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

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

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

Вы можете наблюдать, есть никаких "пробелов" до того , как <new> <test> узлы, но на самом деле они имеют правильный отступ (с помощью altova XMLSPY мы можем дать простую команду в меню редактирования ..чтобы преобразовать любые XML-файлы в "pretty print 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, @Roland, я действительно благодарен и благодарен вам, ребята..

с уважением,
младенец-профессионал.

Это было полезно?

Решение

В соответствии с пожеланием Роланд, вот хвостово-рекурсивное решение:

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

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

Другие советы

Ознакомьтесь с функцией перевода 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>

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

Решение проблемы "предварительно напечатанного 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="*"/>

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top