Заміна рядка XSLT


85

Я насправді не знаю 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), і підтримує заміну регулярних виразів.
Абель

Відповіді:


147

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як у вашому вихідному коді.


Дякую Марк, але зараз я отримую цю помилку:
Викликана

@aximili, вибачте, переплутав XSLT 1.0 і 2.0, відредагував ... зараз добре.
Mark Elliot

19
Ця відповідь неправильна! Функція заміна в 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 «s (не дивився , як зробити це в 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'.
Ian Grainger

Який ваш двигун XSLT?
Milan Aleksić

3
Мій коментар стосувався найпопулярнішого механізму Java XSLT 1.0 Xalan ( xml.apache.org/xalan-j ), який підтримує пряме відображення доступних типів усередині доступного шляху до класу Java; ви не можете застосувати моє рішення для .Net stack
Milan Aleksić

@IanGrainger, ви можете використовувати його з .NET, додавши <msxsl:script>блок, який може викликати будь-який метод .NET, бібліотеку тощо. Хоча .NET також підтримує функції розширення EXSLT, тому вам не потрібно.
Абель

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 для мого префікса. Я здогадуюсь, що це якась особлива магія Microsoft XSLT?
Ian Grainger

1
@IanGrainger, це не так xsl:script, але msxsl:script, і він має інший простір імен (я оновив відповідь Джона).
Абель

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>

перед викликом функції рекурсивно.

Я отримав відповідь звідси: Коли тест висить у нескінченній петлі

Дякую!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.