Заменить строку XSLT

86

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

Недопустимая функция XSLT / XPath

на этой линии

<xsl:variable name="text" select="replace($text,'a','b')"/>

Это XSL

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:inm="http://www.inmagic.com/webpublisher/query" version="1.0">
    <xsl:output method="text" encoding="UTF-8" />

    <xsl:preserve-space elements="*" />
    <xsl:template match="text()" />

    <xsl:template match="mos">
        <xsl:apply-templates />

        <xsl:for-each select="mosObj">
          'Notes or subject' 
           <xsl:call-template
                name="rem-html">
                <xsl:with-param name="text" select="SBS_ABSTRACT" />
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="rem-html">
        <xsl:param name="text" />
        <xsl:variable name="text" select="replace($text, 'a', 'b')" />
    </xsl:template>
</xsl:stylesheet>

Кто-нибудь может сказать мне, что с этим не так?

Аксимили
источник
Обратите внимание, что replace()функция доступна начиная с XPath 2.0 (и, следовательно, XSLT 2.0) и далее и поддерживает замену регулярных выражений.
Abel

Ответы:

150

replace недоступно для XSLT 1.0.

У Codesling есть шаблон для замены строки, который вы можете использовать как замену функции:

<xsl:template name="string-replace-all">
    <xsl:param name="text" />
    <xsl:param name="replace" />
    <xsl:param name="by" />
    <xsl:choose>
        <xsl:when test="$text = '' or $replace = ''or not($replace)" >
            <!-- Prevent this routine from hanging -->
            <xsl:value-of select="$text" />
        </xsl:when>
        <xsl:when test="contains($text, $replace)">
            <xsl:value-of select="substring-before($text,$replace)" />
            <xsl:value-of select="$by" />
            <xsl:call-template name="string-replace-all">
                <xsl:with-param name="text" select="substring-after($text,$replace)" />
                <xsl:with-param name="replace" select="$replace" />
                <xsl:with-param name="by" select="$by" />
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

вызывается как:

<xsl:variable name="newtext">
    <xsl:call-template name="string-replace-all">
        <xsl:with-param name="text" select="$text" />
        <xsl:with-param name="replace" select="a" />
        <xsl:with-param name="by" select="b" />
    </xsl:call-template>
</xsl:variable>

С другой стороны, если вам буквально нужно заменить только один символ другим, вы можете вызвать, translateкоторый имеет аналогичную подпись. Что-то вроде этого должно работать нормально:

<xsl:variable name="newtext" select="translate($text,'a','b')"/>

Также обратите внимание, что в этом примере я изменил имя переменной на «newtext», в XSLT переменные являются неизменяемыми, поэтому вы не можете сделать то же самое, $foo = $fooчто и в исходном коде.

Марк Эллиот
источник
Спасибо, Марк, но теперь я получаю эту ошибку: была вызвана неизвестная функция расширения XPath
Аксимили
@aximili, извините, перепутали, отредактировали XSLT 1.0 и 2.0 ... теперь должно быть хорошо.
Марк Эллиот
19
Это неверный ответ! Функция replace в XSLT заменяет соответствующие ОДИН СИМВОЛ, а не целые строки! См., Например, здесь: w3schools.com/xpath/xpath_functions.asp
Якуб,
12
@Jakub Ты думаешь translate, не так replace. replaceФункция в XPath 2.0 обрабатывает ее второй аргумент как регулярное выражение и заменяет все матчи этого выражения с указанной строкой замены (которая может включать в себя $nссылку на группы захвата в регулярном выражении). Эта translateфункция (в версиях 1.0 и 2.0) выполняет замену одного символа на один.
Ян Робертс
6
не должна ли 4-я строка в примере использования заключать в <xsl:with-param name="replace" select="'a'" />кавычки a?
DJL
37

Вот функция XSLT, которая будет работать аналогично функции String.Replace () в C #.

Этот шаблон имеет 3 параметра, как показано ниже.

text : - ваша основная строка

replace : - строка, которую вы хотите заменить

by : - строка, которая ответит новой строкой

Ниже представлен шаблон

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

Пример ниже показывает, как это называть

<xsl:variable name="myVariable ">
  <xsl:call-template name="string-replace-all">
    <xsl:with-param name="text" select="'This is a {old} text'" />
    <xsl:with-param name="replace" select="'{old}'" />
    <xsl:with-param name="by" select="'New'" />
  </xsl:call-template>
</xsl:variable>

Вы также можете обратиться к указанному ниже URL-адресу для получения подробной информации.

Оптимус
источник
1
Использование xslt 1.0 Этот пост / шаблон работал у меня, а Марк Эллиот - нет.
HostMyBus
12

Примечание: если вы хотите использовать уже упомянутый алгоритм для случаев, когда вам нужно заменить огромное количество экземпляров в исходной строке (например, новые строки в длинном тексте), высока вероятность того, что вы столкнетесь с StackOverflowExceptionрекурсивным вызов.

Я решил эту проблему благодаря встроенному в Xalan (не смотрел, как это сделать в Saxon ) Java-типу:

<xsl:stylesheet version="1.0" exclude-result-prefixes="xalan str"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xalan="http://xml.apache.org/xalan"
                xmlns:str="xalan://java.lang.String"
        >
...
<xsl:value-of select="str:replaceAll(
    str:new(text()),
    $search_string,
    $replace_string)"/>
...
</xsl:stylesheet>
Милан Алексич
источник
Извините, если я веду себя глупо, но я получаю:Cannot find a script or an extension object associated with namespace 'xalan://java.lang.String'.
Ян Грейнджер
Какой у вас XSLT-движок?
Милан Алексич
3
Мой комментарий касался самого популярного механизма Java XSLT 1.0 Xalan ( xml.apache.org/xalan-j ), который поддерживает прямое сопоставление с доступными типами внутри доступного пути к классам Java; вы не можете применить мое решение для стека .Net
Милан Алексич,
@IanGrainger, вы можете использовать его с .NET, добавив <msxsl:script>блок, который может вызывать любой метод .NET, библиотеку и т.д. Хотя .NET также поддерживает функции расширения EXSLT, поэтому вам не нужно.
Abel
exslt также поддерживается в libxslt и, следовательно, во всех потомках xsltproc и т.д ...
Ален Паннетье,
7

Вы можете использовать следующий код, когда ваш процессор работает на .NET или использует MSXML (в отличие от Java-процессоров или других собственных процессоров). Он использует msxsl:script.

Обязательно добавьте пространство имен xmlns:msxsl="urn:schemas-microsoft-com:xslt"в свой корень xsl:stylesheetили xsl:transformэлемент.

Кроме того, можно outletвыполнить привязку к любому пространству имен, которое вам нравится, например xmlns:outlet = "http://my.functions".

<msxsl:script implements-prefix="outlet" language="javascript">
function replace_str(str_text,str_replace,str_by)
{
     return str_text.replace(str_replace,str_by);
}
</msxsl:script>


<xsl:variable name="newtext" select="outlet:replace_str(string(@oldstring),'me','you')" />
Джон Джин
источник
Извините, если я туплю , но я получаю prefix outlet is not definedили 'xsl:script' cannot be a child of the 'xsl:stylesheet' element.меняю msxsl на свой префикс. Я предполагаю, что это какая-то магия XSLT от Microsoft?
Ян Грейнджер,
1
@IanGrainger, это не так xsl:script, но у msxsl:scriptнего другое пространство имен (я обновил ответ Джона).
Abel
1

Я продолжаю набирать этот ответ. Но ни один из них не перечисляет самое простое решение для xsltproc (и, вероятно, для большинства процессоров XSLT 1.0):

  1. Добавьте имя строки exslt в таблицу стилей, например:
<xsl:stylesheet
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  1. Затем используйте это как:
<xsl:value-of select="str:replace(., ' ', '')"/>
Беренд де Бур
источник
1
Xsltproc на моем компьютере (macOS 10.13) НЕ поддерживает эту str:replace()функцию. Как и ни один из других основных процессоров XSLT 1.0 - Xalan, Saxon 6.5 и Microsoft.
michael.hor257k
0

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

  <xsl:when test="$text = '' or $replace = ''or not($replace)" >
    <xsl:value-of select="$text" />
    <!-- Prevent thsi routine from hanging -->
  </xsl:when>

до того, как функция будет вызвана рекурсивно.

Я получил ответ отсюда: При тестировании зависает в бесконечном цикле

Спасибо!

Chesare
источник