Reputation: 966
This issue is similar to the one mentioned here how to unescape xml with xslt but slightly different as my text is coming from a processing instruction.
I have an instruction like this:
<?xm-mark data="<p>Here is the text</p>" ?>
And I would like to output the data part with the <
unencoded. My attempt so far is:
<xsl:template match="processing-instruction('xm-mark')">
<mymark>
<xsl:value-of select="substring-before(substring-after(., 'data="'), '"')"
disable-output-escaping="yes" />
</mymark>
</xsl:template>
However, this gives me back the text as <p>
. If I remove the disable-output-escaping="yes", i get back &lt;
(double encoded as i would expect). Since I can't put a value-of around the value-of in my template, any idea how i unescape the data?
Upvotes: 2
Views: 5715
Reputation: 12075
Processing instructions don't require anything to be escaped, they're parsed similar to comments, in that anything between the <?
and ?>
is treated exactly as-is. If you can, you need to amend whatever's generating that instruction to generate this instead:
<?xm-mark data="<p>Here is the text</p>" ?>
If you can't do that, I wouldn't even attempt to use XSLT to parse it.
EDIT: I should probably clarify, as you're likely making things more complicated than you need to here: A processing instruction doesn't have attributes, and even the " and the space at the end are part of the 'value' of the processing instruction node. You've actually got a processing instruction with the name xm-mark
and the value data="<p>Here is the text</p>"
here (including a space at the end, which doesn't display here); data
is as much a part of the value as the <p>..</p>
part.
In your case <?xm-mark <p>Here is the text</p>?>
is probably enough, then the value of the processing-instruction node is just <p>Here is the text</p>
, which is all you're likely interested in.
EDIT: Ouch.. well, you could try this:
<xsl:template match="processing-instruction('xm-mark')">
<xsl:element name="mymark">
<xsl:call-template name="unescape">
<xsl:with-param name="input" select="substring-before(substring-after(., 'data="'), '"')" />
</xsl:call-template>
</xsl:element>
</xsl:template>
<xsl:template name="unescape">
<xsl:param name="input" />
<xsl:choose>
<xsl:when test="contains($input, '&lt;')">
<xsl:call-template name="unescape">
<xsl:with-param name="input" select="substring-before($input, '&lt;')" />
</xsl:call-template>
<xsl:text><</xsl:text>
<xsl:call-template name="unescape">
<xsl:with-param name="input" select="substring-after($input, '&lt;')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$input" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
NB: Because the &
is taken as text rather than markup, when processing with an xslt you need to use &
to refer to it. Hence, your processing-instruction's value is actually represented as &lt;p>etc..
if it were output 'as-is' in an xml document. The xsl above will at least convert that to <p>etc..
but if you wanted actual p
tags, use an extension method.
Upvotes: 1
Reputation: 243529
This is what you get when you destroy markup by converting it to text.
Remember never to "architect" such horrible things.
Also, resorting to DOE is a sign of desperation and is not guaranteed to work at all (DOE is not a mandatory feature and some major XSLT 1.0 processors, such as the one used by FF don't implement it).
So, what other alternative is there?
One possible solution is to write an extension function (there is no such standard function in XSLT/XPath version 1.0 and 2.0) that takes a string, parses it as XML and returns the resulting XML document. It will be used like that:
<xsl:copy-of select=
"xx:parse(substring-before(substring-after(., 'data="'), '"'))/*"/>
Upvotes: 3