XSLT - 多次替换字符串

时间:2016-01-13 07:03:25

标签: xml xslt xslt-2.0

我有一个像这样的xml,(<p>/text()正在变化)

<doc>
    <p> solid 1; thick 2; solid 2;</p>
    <p> double 2; thick 2; dotted 1;</p>
    <p> dotted 1; double 2; dotted 2;</p>
    <p> solid 2; thick 2; dotted 2;</p>
</doc>

我的要求是分析<p>节点文本并替换以下字符串,

solid 1; to solid 2;
solid 2; to solid 4;
dotted 1; to dotted 2;
dotted 2; to dotted 4;

所以,预期的输出应该是这样的,

<doc>
    <p> solid 2; thick 2; solid 4;</p>
    <p> double 2; thick 2; dotted 2;</p>
    <p> dotted 2; double 2; dotted 4;</p>
    <p> solid 4; thick 2; dotted 4;</p>
</doc>

我写了以下xslt来完成这项任务,

<xsl:template name='replace-text'>
        <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' disable-output-escaping='yes'/>
                <xsl:call-template name='replace-text'>
                    <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:template match="p/text()">
        <xsl:call-template name="replace-text">
            <xsl:with-param name="text" select="."/>
            <xsl:with-param name="replace" select="'solid 1'"/>
            <xsl:with-param name="by" select="'solid 2'"/>
        </xsl:call-template>
    </xsl:template>

但在这里我一次只能传递一个参数。我,努力在派系编程中实现这种方案的方法,有人能建议我一个方法来完成这个任务吗?

2 个答案:

答案 0 :(得分:1)

只需使用replace()函数和for-each循环。

     <doc>
        <xsl:for-each select="p">
            <p>
                <xsl:value-of select="  fn:replace(
                                            fn:replace(
                                                fn:replace(
                                                    fn:replace(.,'solid 2','solid 4'),
                                                'solid 1','solid 2'),
                                            'dotted 2','dotted 4'),
                                        'dotted 1','dotted 2')"/>
            </p>
        </xsl:for-each>
    </doc>

在这种情况下,你必须注意首先更换&#34; solid 2&#34;之后&#34;固体1&#34;。如果你更换&#34;固体1&#34; by&#34; solid 2&#34;首先它将被&#34; solid 4&#34;再次替换。因为订单。最里面的函数将首先应用于字符串。

答案 1 :(得分:1)

更灵活的解决方案,不使用N个嵌套replace()来电

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <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="p/text()">
    <xsl:for-each select="tokenize(., ';')">
      <xsl:analyze-string select="." regex="((solid)|(dotted)) (\d)">
        <xsl:matching-substring>
          <xsl:value-of select=
               "concat(regex-group(1), ' ', 2*xs:integer(regex-group(4)), ';')"/>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
          <xsl:sequence select="."></xsl:sequence>
        </xsl:non-matching-substring>
      </xsl:analyze-string>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

一种更通用的解决方案,其中令牌的替换在参数中指定(并且可以在单独的XML文档中指定):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pMapping">
   <mapArgOf name="solid" old="1" new="2"/>
   <mapArgOf name="solid" old="2" new="4"/>
   <mapArgOf name="dotted" old="1" new="2"/>
   <mapArgOf name="dotted" old="2" new="4"/>
 </xsl:param>

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

  <xsl:template match="p/text()">
    <xsl:for-each select="tokenize(., ';')[.]">
      <xsl:variable name="vTokens" select="tokenize(., '\s+')[.]"/>
      <xsl:variable name="vMatch" 
          select="$pMapping/*[@name eq $vTokens[1] and @old eq $vTokens[2]]"/>

      <xsl:value-of select=
       "concat(replace(., $vTokens[2], ($vMatch/@new, $vTokens[2])[1]), ';')"/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

这两种转换都应用于提供的XML文档

<doc>
    <p> solid 1; thick 2; solid 2;</p>
    <p> double 2; thick 2; dotted 1;</p>
    <p> dotted 1; double 2; dotted 2;</p>
    <p> solid 2; thick 2; dotted 2;</p>
</doc>

生成想要的正确结果

<doc>
    <p> solid 2; thick 2; solid 4;</p>
    <p> double 2; thick 2; dotted 2;</p>
    <p> dotted 2; double 2; dotted 4;</p>
    <p> solid 4; thick 2; dotted 4;</p>
</doc>

请注意

  1. 我们可以根据需要指定任意数量的映射(不仅仅是“实体”和“点缀”)。

  2. 这里我们不再假设新值是旧值的两倍 - 我们甚至不假设该值是数字

  3. 例如,如果我们想要为“thick”添加替换,使得1替换为5,2替换为8而3替换为10,我们只需更改映射,如下所示:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:param name="pMapping">
       <mapArgOf name="solid" old="1" new="2"/>
       <mapArgOf name="solid" old="2" new="4"/>
       <mapArgOf name="dotted" old="1" new="2"/>
       <mapArgOf name="dotted" old="2" new="4"/>
       <mapArgOf name="thick" old="1" new="5"/>
       <mapArgOf name="thick" old="2" new="8"/>
       <mapArgOf name="thick" old="3" new="10"/>
     </xsl:param>
    
      <xsl:template match="node()|@*">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="p/text()">
        <xsl:for-each select="tokenize(., ';')[.]">
          <xsl:variable name="vTokens" select="tokenize(., '\s+')[.]"/>
          <xsl:variable name="vMatch" select=
             "$pMapping/*[@name eq $vTokens[1] and @old eq $vTokens[2]]"/>
    
          <xsl:value-of select=
            "concat(replace(., $vTokens[2], ($vMatch/@new, $vTokens[2])[1]), ';')"/>
        </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>
    

    现在我们再次得到新的,想要的结果:

    <doc>
        <p> solid 2; thick 5; solid 4;</p>
        <p> double 2; thick 8; dotted 2;</p>
        <p> dotted 2; double 2; dotted 4;</p>
        <p> solid 4; thick 10; dotted 4;</p>
    </doc>
    

    最后,对于最通用的多替换解决方案,请参阅此答案

    <强> XSL Multiple search and replace function