2009-07-21 58 views
21

如何使用XSLT將以下XML轉換爲轉義文本?將XML轉換爲XSLT中的轉義文本

來源:

<?xml version="1.0" encoding="utf-8"?> 
<abc> 
    <def ghi="jkl"> 
    mnop 
    </def> 
</abc> 

輸出:

<TestElement>&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;abc&gt;&lt;def ghi="jkl"&gt; 
    mnop 
    &lt;/def&gt;&lt;/abc&gt;</TestElement> 

目前,我想下面的XSLT,它似乎並沒有正常工作:的

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="utf-8" /> 
    <xsl:template match="/"> 
    <xsl:variable name="testVar"> 
     <xsl:copy> 
     <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
    </xsl:variable> 

    <TestElement> 
     <xsl:value-of select="$testVar"/> 
    </TestElement> 
    </xsl:template> 
</xsl:stylesheet> 

輸出.NET XslCompiledTransform的XSLT語句如下所示:

<?xml version="1.0" encoding="utf-8"?><TestElement> 

    mnop 

</TestElement> 
+1

究竟是如何不能正常工作? – 2009-07-21 23:52:19

+0

我爲John的評論添加了XSLT的輸出。 – 2009-07-21 23:56:33

回答

33

你的代碼的工作確實因爲xsl:value-of檢索節點集的string-value的方式。

做你想要什麼,我怕,你必須把它明確代碼:

<xsl:template match="/"> 
     <TestElement> 
      <xsl:apply-templates mode="escape"/> 
     </TestElement> 
    </xsl:template> 

    <xsl:template match="*" mode="escape"> 
     <!-- Begin opening tag --> 
     <xsl:text>&lt;</xsl:text> 
     <xsl:value-of select="name()"/> 

     <!-- Namespaces --> 
     <xsl:for-each select="namespace::*"> 
      <xsl:text> xmlns</xsl:text> 
      <xsl:if test="name() != ''"> 
       <xsl:text>:</xsl:text> 
       <xsl:value-of select="name()"/> 
      </xsl:if> 
      <xsl:text>='</xsl:text> 
      <xsl:call-template name="escape-xml"> 
       <xsl:with-param name="text" select="."/> 
      </xsl:call-template> 
      <xsl:text>'</xsl:text> 
     </xsl:for-each> 

     <!-- Attributes --> 
     <xsl:for-each select="@*"> 
      <xsl:text> </xsl:text> 
      <xsl:value-of select="name()"/> 
      <xsl:text>='</xsl:text> 
      <xsl:call-template name="escape-xml"> 
       <xsl:with-param name="text" select="."/> 
      </xsl:call-template> 
      <xsl:text>'</xsl:text> 
     </xsl:for-each> 

     <!-- End opening tag --> 
     <xsl:text>&gt;</xsl:text> 

     <!-- Content (child elements, text nodes, and PIs) --> 
     <xsl:apply-templates select="node()" mode="escape" /> 

     <!-- Closing tag --> 
     <xsl:text>&lt;/</xsl:text> 
     <xsl:value-of select="name()"/> 
     <xsl:text>&gt;</xsl:text> 
    </xsl:template> 

    <xsl:template match="text()" mode="escape"> 
     <xsl:call-template name="escape-xml"> 
      <xsl:with-param name="text" select="."/> 
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template match="processing-instruction()" mode="escape"> 
     <xsl:text>&lt;?</xsl:text> 
     <xsl:value-of select="name()"/> 
     <xsl:text> </xsl:text> 
     <xsl:call-template name="escape-xml"> 
      <xsl:with-param name="text" select="."/> 
     </xsl:call-template> 
     <xsl:text>?&gt;</xsl:text> 
    </xsl:template> 

    <xsl:template name="escape-xml"> 
     <xsl:param name="text"/> 
     <xsl:if test="$text != ''"> 
      <xsl:variable name="head" select="substring($text, 1, 1)"/> 
      <xsl:variable name="tail" select="substring($text, 2)"/> 
      <xsl:choose> 
       <xsl:when test="$head = '&amp;'">&amp;amp;</xsl:when> 
       <xsl:when test="$head = '&lt;'">&amp;lt;</xsl:when> 
       <xsl:when test="$head = '&gt;'">&amp;gt;</xsl:when> 
       <xsl:when test="$head = '&quot;'">&amp;quot;</xsl:when> 
       <xsl:when test="$head = &quot;&apos;&quot;">&amp;apos;</xsl:when> 
       <xsl:otherwise><xsl:value-of select="$head"/></xsl:otherwise> 
      </xsl:choose> 
      <xsl:call-template name="escape-xml"> 
       <xsl:with-param name="text" select="$tail"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

注意,這個解決方案忽略註釋節點,並插入不必要的命名空間節點(如namespace::軸將包括所有節點都從父節點繼承)。然而,關於命名空間,結果引用的XML將在語義上等同於您在回覆中提供的示例(因爲這些重複的聲明不會真正改變任何內容)。

此外,這不會逃避<?xml ... ?>聲明,只是因爲它不存在於XPath 1.0數據模型中(它不是處理指令)。如果您確實需要在輸出中使用它,則必須手動插入它(並確保它指定的編碼與XSLT處理器的序列化編碼一致)。

+0

驚人!!!!! +1 – 2012-03-22 04:40:49

-1

爲什麼你就不能運行

<xsl:template match="/"> 
    <TestElement> 
    <xsl:copy-of select="." /> 
    </TestElement> 
</xsl:template> 
+2

因爲它將生成XML樹,而不是表示XML樹的字符串(如OP所要求的)。 – bortzmeyer 2009-07-22 12:26:47

0

需要使用XSLT?因爲由Pavel Minaev解釋的原因,使用另一種工具會簡單得多。與xmlstartlet一個例子:

 
% xmlstarlet escape 
<?xml version="1.0" encoding="utf-8"?> 
<abc> 
    <def ghi="jkl"> 
    mnop 
    </def> 
</abc> 
[Control-D] 
&lt;?xml version="1.0" encoding="utf-8"?&gt; 
&lt;abc&gt; 
    &lt;def ghi="jkl"&gt; 
    mnop 
    &lt;/def&gt; 
&lt;/abc&gt; 
+0

不幸的是,這不在我所問的範圍之內。最近,我看到以字符串作爲參數的Web服務。更糟糕的是,字符串參數將XML作爲輸入。根據所需功能,我需要將XML文檔轉換爲SOAP信封。問題是我需要將XML文檔轉換爲轉義文本,以便將其作爲SOAP信封的字符串參數傳遞(假設其他所有內容都是靜態的)。因此,如果你沒有在中間使用另一個代理類庫,那麼我問的問題就出現了。 – 2009-07-22 13:34:10

0

如果你訪問它,我會建議撒克遜extention serialize。它正是你想要它做的。如果您不想這樣做,那麼您在構建文檔時必須手動插入實體引用。這將會是脆弱的,但它會爲大多數文檔工作:

<xsl:template match="/"> 
    <TestElement> 
     <xsl:apply-templates/> 
    </TestElement> 
</xsl:template> 
<xsl:template match="*"> 
    <xsl:text>&lt;</xsl:text> 
    <xsl:value-of select="name()"/> 
    <xsl:apply-templates select="@*"/> 
    <xsl:text>&gt;</xsl:text> 
    <xsl:apply-templates select="node()"/> 
    <xsl:text>&lt;/</xsl:text> 
    <xsl:value-of select="name()"/> 
    <xsl:text>&gt;</xsl:text> 
</xsl:template> 
<xsl:template match="@*"> 
    <xsl:text>&#32;</xsl:text> 
    <xsl:value-of select="name()"/> 
    <xsl:text>="</xsl:text> 
    <xsl:value-of select="."/> 
    <xsl:text>"</xsl:text> 
</xsl:template> 
<xsl:template match="text()"> 
    <xsl:value-of select="."/> 
</xsl:template> 

最值得注意的是,這將可能打破,如果你的屬性具有雙引號字符。使用saxon真的更好,或者使用用戶編寫的擴展,如果不能使用正確的序列化程序。

16

而不是轉義您可以添加CDATA部分中的文本。 解析器將忽略CDATA部分中的文本,類似於它是否已被轉義。

你的例子是這樣的

<TestElement> 
<![CDATA[ 
<abc> 
    <def ghi="jkl"> 
    mnop 
    </def> 
</abc> 
]]> 
</TestElement> 

使用下面的XSLT片段:

<xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text> 
<xsl:copy-of select="/"/> 
<xsl:text disable-output-escaping="yes">]]</xsl:text> 
<xsl:text disable-output-escaping="yes">&gt;</xsl:text> 
+0

+1幫助我解決問題(與此問題分開)。 – 2011-01-28 17:23:40

1

您可以通過在命名空間的輸出增加了測試防止多餘的命名空間節點:


<xsl:variable name="curnode" select="."/> 
    <xsl:for-each select="namespace::*"> 
     <xsl:variable name="nsuri" select="."/> 
     <xsl:if test="$curnode/descendant-or-self::*[namespace-uri()=$nsuri]"> 
     ... 
3

我試圖實現由帕維爾Minaev提供的答案,要指出的是,這是一個大的字符串很危險因爲輸入字符串中的每個字符都單獨遞歸,導致遞歸深度快速耗盡。我試圖通過幾行文本運行它,並導致堆棧溢出(lol)。

取而代之,我使用的模板不需要檢查每個單獨的字符,而是直接找到需要替換的字符串。這可以被用來轉義字符:

<xsl:template name="Search-And-Replace"> 
    <xsl:param name="Input-String"/> 
    <xsl:param name="Search-String"/> 
    <xsl:param name="Replace-String"/> 
    <xsl:choose> 
     <xsl:when test="$Search-String and contains($Input-String, $Search-String)"> 
      <xsl:value-of select="substring-before($Input-String, $Search-String)"/> 
      <xsl:value-of select="$Replace-String"/>   
      <xsl:call-template name="Search-And-Replace"> 
       <xsl:with-param name="Input-String" select="substring-after($Input-String, $Search-String)"/> 
       <xsl:with-param name="Search-String" select="$Search-String"/> 
       <xsl:with-param name="Replace-String" select="$Replace-String"/> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:value-of select="$Input-String"/> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

然後它只是呼籲要逃脫字符該模板的問題..

<xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="Hi I am a string &amp; I am awesome"/> 
      <xsl:with-param name="Search-String" select="'&amp;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;amp;'"/> 
    </xsl:call-template> 

爲了逃避一個字符串中有多個字符,我使用了一個使用變量的包裝模板...

<xsl:template name="EscapeText"> 
    <xsl:param name="text" /> 

    <xsl:variable name="a"> 
    <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$text"/> 
      <xsl:with-param name="Search-String" select="'&amp;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;amp;'"/> 
     </xsl:call-template>    
    </xsl:variable> 

    <xsl:variable name="b">  
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$a"/> 
      <xsl:with-param name="Search-String" select="'&quot;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;quot;'"/> 
     </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="c">  
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$b"/> 
      <xsl:with-param name="Search-String">&apos;</xsl:with-param> 
      <xsl:with-param name="Replace-String" select="'&amp;apos;'"/> 
     </xsl:call-template> 
    </xsl:variable>   

    <xsl:variable name="d">  
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$c"/> 
      <xsl:with-param name="Search-String" select="'&gt;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;gt;'"/> 
     </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="e"> 
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$d"/> 
      <xsl:with-param name="Search-String" select="'&lt;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;lt;'"/> 
     </xsl:call-template> 
    </xsl:variable>  
    <!--this is the final output--> 
    <xsl:value-of select="$e"/>  
</xsl:template> 

這對於大型字符串來說證明是更安全的,因爲它不再需要爲輸入字符串中的每個單獨字符遞歸。