Как заменить несколько текстовых подстрок в XSLT 1 - PullRequest
4 голосов
/ 18 января 2012

В XSLT 1.0 методы регулярных выражений XSLT 2.0 обычно недоступны. Есть ли не-регулярный способ замены нескольких полей в узле исходного XML-документа, например, для преобразования:

<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1">
  <file>
    <source>abc [[field1]] def [[field2]] ghi</source>
  </file>
</xliff>

до:

<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1">
  <file>
    <source>abc F def F ghi</source>
  </file>
</xliff>

Ответы [ 4 ]

4 голосов
/ 18 января 2012

I. Решение XSLT 1.0:

Это преобразование :

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

 <xsl:param name="pTargetStart" select="'[['"/>
 <xsl:param name="pTargetEnd" select="']]'"/>
 <xsl:param name="pReplacement" select="'F'"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="source/text()" name="replace">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pTargetStart" select="$pTargetStart"/>
  <xsl:param name="pTargetEnd" select="$pTargetEnd"/>
  <xsl:param name="pRep" select="$pReplacement"/>

  <xsl:choose>
   <xsl:when test=
    "not(contains($pText, $pTargetStart)
       and
        contains($pText, $pTargetEnd)
        )
     or
      not(contains(substring-after($pText, $pTargetStart),
                   $pTargetEnd
                   )
         )
    ">
     <xsl:value-of select="$pText"/>
    </xsl:when>

    <xsl:otherwise>
     <xsl:value-of select="substring-before($pText, $pTargetStart)"/>
     <xsl:value-of select="$pRep"/>

     <xsl:variable name="vremText" select=
     "substring-after(substring-after($pText, $pTargetStart),
                      $pTargetEnd
                      )"/>
     <xsl:call-template name="replace">
      <xsl:with-param name="pText" select="$vremText"/>
      <xsl:with-param name="pTargetStart" select="$pTargetStart"/>
      <xsl:with-param name="pTargetEnd" select="$pTargetEnd"/>
      <xsl:with-param name="pRep" select="$pRep"/>
     </xsl:call-template>
    </xsl:otherwise>

  </xsl:choose>

 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1">
    <file>
        <source>abc [[field1]] def [[field2]] ghi</source>
    </file>
</xliff>

дает желаемый, правильный результат :

<xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1">
   <file>
      <source>abc F def F ghi</source>
   </file>
</xliff>

II. Решение 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:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="source/text()">
  <xsl:sequence select="replace(., '\[\[(.*?)\]\]', 'F')"/>
 </xsl:template>
</xsl:stylesheet>
2 голосов
/ 18 января 2012

EXSLT имеет несколько хороших функций для вас. Если вам нужно заменить простые строки, попробуйте str: replace . Дается реализация шаблона XSLT 1.0 .

1 голос
/ 31 июля 2012

Вы можете использовать Java внутри XSL, например для replaceAll:

<xsl:template name="replace_all" xmlns:string="java.lang.String">
    <xsl:param name="text"/>
    <xsl:param name="pattern"/>
    <xsl:param name="replace"/>
    <xsl:variable name="text_string" select="string:new($text)"/>
    <xsl:value-of select="string:replaceAll($text_string, $pattern, $replace)"/>
</xsl:template>

- это регулярное выражение.Для получения дополнительной информации см .: String Javadoc

1 голос
/ 18 января 2012

РЕДАКТИРОВАТЬ 1

Я только что понял, что версия Димитра использует рекурсию и очень похожа; поэтому мое первое предложение кажется глупым.

Вот версия, которая использует рекурсию:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:variable name="fld-beg" select="'[['"/>
  <xsl:variable name="fld-end" select="']]'"/>
  <xsl:variable name="replacement" select="'F'"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="source/text()">
    <xsl:call-template name="replace">
      <xsl:with-param name="str" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="replace">
    <xsl:param name="str"/>
    <xsl:choose>
      <xsl:when test="contains($str, $fld-beg) and contains($str, $fld-end)">
        <xsl:call-template name="replace">
          <xsl:with-param name="str" select="concat(
            substring-before($str, $fld-beg),
            $replacement,
            substring-after($str, $fld-end))"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$str"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

match="source/text()" сопоставляет весь текст в узле «источник» как одну строку и передает его в именованный шаблон «заменить». «replace» ищет вхождения начального и конечного разделителей ('[[' и ']]'), и, если найден, разбивает текст на (и, следовательно, игнорирует) разделители, вставляет строку замены и передает все это себе повторить процесс.

Я говорю «сплит», но, учитывая отсутствие реального split() в XPath 1.0, мы можем получить, объединившись substring-before() и substring-after().

Учитывая текст в источнике, 'abc [[field1]] def [[field2]] ghi', рекурсия выглядит следующим образом, показывая, как она разбивается, заменяется и передается:

  1. 'abc ' + 'F' + def [[field2]] ghi', снова передан в 'замену'
  2. 'abc F def ' + 'F' + ' ghi', снова передан в «замену»
  3. , поскольку разделители отсутствуют, 'abc F def F ghi' передается обратно до match="source/text()"

Вот как это выглядит с xsltproc:

$ xsltproc so.xsl so.xml
<?xml version="1.0"?>
<xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1">
  <file>
    <source>abc F def F ghi</source>
  </file>
</xliff>

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...