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><?xml version="1.0" encoding="utf-8"?><abc><def ghi="jkl"> mnop </def></abc></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>
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.
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.
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.
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><</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>></xsl:text> <!-- Content (child elements, text nodes, and PIs) --> <xsl:apply-templates select="node()" mode="escape" /> <!-- Closing tag --> <xsl:text></</xsl:text> <xsl:value-of select="name()"/> <xsl:text>></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><?</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: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;</xsl:when> <xsl:when test="$head = '<'">&lt;</xsl:when> <xsl:when test="$head = '>'">&gt;</xsl:when> <xsl:when test="$head = '"'">&quot;</xsl:when> <xsl:when test="$head = "'"">&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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With