2012-03-27 26 views
1

我有一個帶有以下標記和數據的xml文件main.xml。根據外部xml文件搜索關鍵字並替換xml文件中的文本

main.xml 

<xml> 
    <content> 
    <para> 
    This is a para. 
    </para> 
    <sub para> 
    This is para. 
    </sub para> 
    </content> 
</xml> 

我有,我們需要找到在上面的XML任何地方和替換關鍵字值的關鍵字列表另一個XML文件keyword.xml。

keyword.xml 

<xml> 
    <keywordList> 
     <keyword> 
      <value>para</value> 
      <replace> paragraph </replace> 
     </keyword> 
     <keyword> 
      <value>is</value> 
      <replace>IS</replace> 
     </keyword> 
    </xml> 

我們能夠做到在XSLT這樣輸出應該是

output 
     <xml> 
    <content> 
    <para> 
    This IS a paragraph. 
    </para> 
    <sub para> 
    This IS paragraph. 
    </sub para> 
    </content> 
</xml> 
+0

您的輸入XML格式不正確。 – 2012-03-27 09:10:33

+0

請參閱http://stackoverflow.com/questions/2145004/efficient-code-for-replacing-a-text-node-with-some-other-text-using-xslt – Jon 2012-03-27 09:12:59

+0

您僅限於XSLT1嗎?還是可以使用xslt2 。這在xslt2中使用xsl:analyze-string是微不足道的,但在xslt1中,字符串處理相當基本,所以您需要在空白區域(可能)分割遞歸模板。 – 2012-03-27 10:21:08

回答

0

這是一個XSLT 1.0溶液(當然,可以用XSLT 2.0使用,太):

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

     <my:params xml:space="preserve"> 
      <pattern> 
       <old>para</old> 
       <new> paragraph </new> 
      </pattern> 
      <pattern> 
       <old> is </old> 
       <new> IS </new> 
      </pattern> 
     </my:params> 

     <xsl:variable name="vrtfPats"> 
     <xsl:for-each select="document('')/*/my:params/*"> 
      <xsl:sort select="string-length(old)" 
       data-type="number" order="descending"/> 
      <xsl:copy-of select="."/> 
     </xsl:for-each> 
     </xsl:variable> 

     <xsl:variable name="vPats" select= 
     "ext:node-set($vrtfPats)/*"/> 

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

     <xsl:template match="text()" name="multiReplace" priority="2"> 
      <xsl:param name="pText" select="."/> 
      <xsl:param name="pPatterns" select="$vPats"/> 
      <xsl:if test= "string-length($pText) >0"> 
       <xsl:variable name="vPat" select= 
       "$vPats[starts-with($pText, old)][1]"/> 

       <xsl:choose> 
        <xsl:when test="not($vPat)"> 
         <xsl:copy-of select="substring($pText,1,1)"/> 
        </xsl:when> 
        <xsl:otherwise> 
         <xsl:copy-of select="$vPat/new/node()"/> 
        </xsl:otherwise> 
       </xsl:choose> 

       <xsl:call-template name="multiReplace"> 
        <xsl:with-param name="pText" select= 
        "substring($pText, 
           1 + not($vPat) + string-length($vPat/old/node()) 
          )"/> 
       </xsl:call-template> 
      </xsl:if> 
     </xsl:template> 
</xsl:stylesheet> 

當這個變換所提供的XML文檔施加(校正爲良好的形成) :

<xml> 
    <content> 
     <para> 
     This is a para. 
     </para> 
     <sub_para> 
     This is para. 
     </sub_para> 
    </content> 
</xml> 

想要的,正確的結果產生

<xml> 
    <content> 
     <para> 
     This IS a paragraph . 
     </para> 
     <sub_para> 
     This IS paragraph . 
     </sub_para> 
    </content> 
</xml> 

說明:逐個字符地掃描文本,並將文本中該位置開始的最長可能的目標字符串替換爲其指定的替換字符。

+0

嗨Dimitre,我使用的是撒克遜處理器,它沒有重新調整節點集函數。我嘗試使用document('')來修改它,但無法做到這一點...... – atif 2012-03-27 16:23:19

+0

@atif:只需將'$ vPats'變量的定義替換爲:'' ' – 2012-03-27 16:50:30

+0

嗨Dimitre,它確實有效,但它爲每個不匹配的字符增加了額外的空間。輸出似乎是T我是一段。任何建議...... – atif 2012-03-27 17:03:13

1

請嘗試以下

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet 
    version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 

    <xsl:variable name="keywords" select="document('keyword.xml')"/> 

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

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

    <xsl:template match="text()"> 
     <xsl:analyze-string select="." regex="[A-Za-z]+"> 
      <xsl:matching-substring> 
       <xsl:variable name="repl" select="$keywords//keyword[value = current()]"/> 
       <xsl:choose> 
        <xsl:when test="$repl"> 
         <xsl:value-of select="$repl/replace"/> 
        </xsl:when> 
        <xsl:otherwise> 
         <xsl:value-of select="current()"/> 
        </xsl:otherwise> 
       </xsl:choose> 
      </xsl:matching-substring> 
      <xsl:non-matching-substring> 
       <xsl:value-of select="current()"/> 
      </xsl:non-matching-substring> 
     </xsl:analyze-string> 
    </xsl:template> 
</xsl:stylesheet> 

注意,對於對替換值包括圍繞新詞的空間,因此額外的空間:

<?xml version="1.0" encoding="UTF-8"?> 
<xml> 
    <content> 
     <para> 
      This IS a paragraph . 
     </para> 
     <subpara> 
      This IS paragraph . 
     </subpara> 
    </content> 
</xml> 
+0

嗨Maestro,它工作得很好,如果關鍵字是單個單詞,但如果關鍵詞是多個單詞,那麼它沒有找到關鍵字。所以例如,如果關鍵字是「This is」,那麼它並不會取代xml內容中的「This is」...任何幫助都將被設置爲 – atif 2012-03-27 16:25:42

+0

我建議只用其他字符串替換完整的單詞,例如This並且分開:-)或者也許使用Dimitre的解決方案 - 我沒有詳細的瞭解它,但它可能會爲你製造詭計。 – Maestro13 2012-03-27 17:21:48