0

We are using an XSL transformer to convert hexcode entity to mdash. This transformation happens as expected. But we also have to convert all & in xml to output as &. Right now, the output contains &. During xsl transformation, below error is displayed:-

An attribute node cannot be created after a child of the containing element.

While using the below xsl, the error is thrown:-


<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' >
<xsl:output method="xml" omit-xml-declaration="no" use-character-maps="mdash" />
<xsl:character-map name="mdash">
<xsl:output-character character="&#x2014;" string="&amp;mdash;" />
</xsl:character-map>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:text disable-output-escaping="yes">&amp;</xsl:text>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
1
  • If you're using Saxon, then please do not tag your question as xalan. Commented May 16, 2023 at 11:21

1 Answer 1

0

You have a template matching on match="@* | node()", inside the template you make a shallow copy with xsl:copy, then, you create a text node with <xsl:text disable-output-escaping="yes">&amp;</xsl:text> as the first child node to then expect <xsl:apply-templates select="@*|node()" /> to process attribute nodes (and child nodes) further, i.e. copy them as well, which can't work for the attribute nodes, as once you have output that child node, you can't add attribute nodes.

I have not really understood what you need the xsl:text for, but you can't output a child node and then try to output an attribute node; first process attribute nodes, then create and/or process child nodes.

As for the task to "to convert all &amp; in xml to output as &" in addition to "to convert hexcode entity to mdash", I guess you want e.g.

<xsl:stylesheet version='3.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
  
<xsl:output method="xml" omit-xml-declaration="no" use-character-maps="mdash amp" />

<xsl:character-map name="mdash">
  <xsl:output-character character="&#x2014;" string="&amp;mdash;" />
</xsl:character-map>

<xsl:character-map name="amp">
  <xsl:output-character character="&amp;" string="&amp;" />
</xsl:character-map>

<xsl:mode on-no-match="shallow-copy"/>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

4 Comments

This one did the trick. Thanks a lot. May i know, what is the shallow-copy part doing over here, and why we used string="&amp" instead of & directly.
The <xsl:mode on-no-match="shallow-copy"/> is XSLT 3.0's declarative way of setting up the identity transformation as the base template so you don't have to spell out your usual (XSLT 2 or 1) template for match="@* | node()" doing an xsl:copy and to process attributes and child nodes inside with <xsl:apply-templates select="@* | node()"/>.
XSLT is XML so it has to follow the XML syntax rules in its program code, therefore the <xsl:output-character character="&amp;" string="&amp;" /> is the syntactically necessary way to declare that the serializer, on output tree serialization, should output any & character literally as an & character instead of enforcing XML serialization as &amp;.
It's worth adding the caveat that if you adopt this solution, there is no guarantee that the result of the transformation will be well-formed XML.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.