2016-01-13 96 views
2

我有這樣的XML,(<p>/text()是不同的)XSLT - 替換字符串多次

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

我需求量的是analize <p>節點並替換文本下列字符串,

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

SO,預期的輸出應該是這樣的,

<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來做這個t請問,

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

但是在這裏我只能一次傳遞一個參數。我努力在這種情況下派系編程中實施一種方法,任何人都可以爲我提供一種方法來完成這項任務嗎?

+0

使用的,每個功能 –

+0

@sanjay,還有一個更通用的解決方案,不使用可變數量的嵌套'replace()'函數調用。該解決方案允許指定更換的外部映射。請享用! –

回答

1

一種更靈活的解決方案,其不使用嵌套replace()呼叫的N個:

<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. 我們可以指定儘可能多的映射(不僅適用於「soli d「和」點綴「)。

  2. 在這裏,我們不再認爲新值的兩倍舊值 - 我們甚至不假設值是一個數字

例如,如果我們要添加替代「厚」,使得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

0

只需使用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> 

在這種情況下,你必須小心第一次替換「固體2」,然後在「固體1」。如果您首先將「固體1」替換爲「固體2」,則由於順序而將再次替換爲「固體4」。最內層的函數將首先應用於字符串。