How to unescape XML characters with help of XSLT?

2019-01-08 23:36发布

问题:

I need to unescape XML characters from inside of XML nodes with the help of only XSLT transformations. I have <text>&lt;&gt;and other possible characters</text> and need to get it as valid formatted HTML when I place it inside of the body tag.

回答1:

<xsl:template match="text">
  <body>
    <xsl:value-of select="." disable-output-escaping="yes" />
  </body>
</xsl:template>

Note that the output is not guaranteed to be well-formed XML anymore.



回答2:

With xslt 2.0 I have come up with this one. Note that the output is not guaranteed to be correct xml, a simple unequality can mess up your output.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<xsl:character-map name="a">
    <xsl:output-character character="&lt;" string="&lt;"/>
    <xsl:output-character character="&gt;" string="&gt;"/>
</xsl:character-map>

<xsl:output use-character-maps="a"/>

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

</xsl:stylesheet>


回答3:

I haven't found an answer to this question. So I came to the conclusion that this is no way to do this. I found workaround for this problem, unescaping file on server side.



回答4:

Another solution. This one does not use the xml postprocessor, so readily useable as input of further xslt processing. Also guaranteed to create valid xml. This is a xslt 2.0 solution, escaping text within "documentation" tags, tested with saxon. You should modify the "allowedtags" variable to define your own data model. The immediate children are the tags, the ones below them are the attributes possible. Reading the allowed tags from an xsd is left as an excercise for the reader (please share it with me).

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="http://magwas.rulez.org/my"
>
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:variable name="allowedtags">
<li/>
<ul/>
<br/>
<a><href/></a>
</xsl:variable>

<xsl:template match="@*|*|processing-instruction()|comment()" mode="unescape">
    <xsl:copy>
        <xsl:apply-templates select="*|@*|text()|processing-instruction()|comment()" mode="unescape"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="documentation" mode="unescape">
    <documentation>
    <xsl:call-template name="doc">
        <xsl:with-param name="str">
            <xsl:value-of select="."/>
        </xsl:with-param>
    </xsl:call-template>
    </documentation>
</xsl:template>

<xsl:template name="doc">
    <xsl:param name="str"/>
    <xsl:variable name="start" select="fn:substring-before($str,'&lt;')"/>
    <xsl:variable name="rest" select="fn:substring-after($str,'&lt;')"/>
    <xsl:variable name="fulltag" select="fn:substring-before($rest,'&gt;')"/>
    <xsl:variable name="tagparts" select="fn:tokenize($fulltag,'[  &#xA;]')"/>
    <xsl:variable name="tag" select="$tagparts[1]"/>
    <xsl:variable name="aftertag" select="fn:substring-after($rest,'&gt;')"/>
    <xsl:variable name="intag" select="fn:substring-before($aftertag,fn:concat(fn:concat('&lt;/',$tag),'&gt;'))"/>
    <xsl:variable name="afterall" select="fn:substring-after($aftertag,fn:concat(fn:concat('&lt;/',$tag),'&gt;'))"/>
    <xsl:value-of select="$start"/>
    <xsl:choose>
    <xsl:when test="$tag">
        <xsl:variable name="currtag" select="$allowedtags/*[$tag = local-name()]"/>
        <xsl:if test="$currtag">
            <xsl:element name="{$currtag/local-name()}">
                <xsl:for-each select="$tagparts[position()>1]">
                    <xsl:variable name="anstring" select="fn:replace(.,'^([^ &#xA;=]*)=.*$','$1')"/>
                    <xsl:variable name="antag" select="$currtag/*[$anstring = local-name()]"/>
                    <xsl:if test="$antag">
                        <xsl:attribute name="{$antag/local-name()}">
                            <xsl:value-of select="fn:replace(.,'^.*[^ &#34;]*&#34;([^&#34;]*)&#34;.*','$1')"/>
                        </xsl:attribute>
                    </xsl:if>
                </xsl:for-each>
                <xsl:if test="$intag">
                    <xsl:call-template name="doc">
                        <xsl:with-param name="str">
                            <xsl:value-of select="$intag"/>
                        </xsl:with-param>
                    </xsl:call-template>
                </xsl:if>
            </xsl:element>
        </xsl:if>
        <xsl:if test="$afterall">
            <xsl:call-template name="doc">
                <xsl:with-param name="str">
                    <xsl:value-of select="$afterall"/>
                </xsl:with-param>
            </xsl:call-template>
        </xsl:if>
    </xsl:when>
    <xsl:otherwise>
                    <xsl:value-of select="$str"/>
    </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>


标签: xml xslt