Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting XML to escaped text in XSLT

Tags:

xml

xslt

How can I convert the following XML to an escaped text using XSLT?

Source:

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

Output:

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

Currently, I'm trying the following XSLT and it doesn't seem to work properly:

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

Output of XSLT statement by the .NET XslCompiledTransform comes out as the following:

<?xml version="1.0" encoding="utf-8"?><TestElement>      mnop  </TestElement> 
like image 680
Frank Liao Avatar asked Jul 21 '09 23:07

Frank Liao


People also ask

Is there any benefit of converting XML to XSLT?

XSLT is commonly used to convert XML to HTML, but can also be used to transform XML documents that comply with one XML schema into documents that comply with another schema. XSLT can also be used to convert XML data into unrelated formats, like comma-delimited text or formatting languages such as troff.

What is text () in XSLT?

XSLT <xsl:text> The <xsl:text> element is used to write literal text to the output. Tip: This element may contain literal text, entity references, and #PCDATA.

Can XSLT transform XML to CSV?

The following XSL Style Sheet (compatible with XSLT 1.0) can be used to transform the XML into CSV. It is quite generic and can easily be configured to handle different xml elements by changing the list of fields defined ar the beginning.


1 Answers

Your code works the way it does because xsl:value-of retrieves the string-value of the node set.

To do what you want, I'm afraid that you'll have to code it explicitly:

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

Note that this solution ignores comment nodes, and inserts unneccessary namespace nodes (as namespace:: axis will include all nodes inherited from parent). Regarding namespaces, however, the resulting quoted XML will be semantically equivalent to the example that you provided in your reply (since those repeated redeclarations don't really change anything).

Also, this won't escape the <?xml ... ?> declaration, simply because it is not present in XPath 1.0 data model (it's not a processing instruction). If you actually need it in the output, you'll have to insert it manually (and make sure that encoding it specifies is consistent with serialization encoding of your XSLT processor).

like image 182
Pavel Minaev Avatar answered Oct 03 '22 01:10

Pavel Minaev