如何配置Saxon/XSL保留HTML编码实体不被转换?
Great question! The issue here is that when Saxon parses your input XML, it resolves valid character references like to their actual character values (in this case, a newline) before the transform even runs. The default identity transform just copies those actual characters to the output, but your downstream tools need the original escaped sequences.
Luckily, there are two solid ways to fix this with Saxon/XSLT:
1. Use an XSLT Character Map (Recommended)
Character maps are designed exactly for this scenario—they let you control how specific characters are serialized in the output. You can define a map that converts actual characters back to their escaped reference strings.
Here's a modified identity transform that preserves (and can be extended for other characters):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Define a character map to map newline characters to their escaped form --> <xsl:character-map name="preserve-escaped-chars"> <xsl:output-character character=" " string="&#10;"/> <!-- Add more entries for other characters you need to preserve, e.g.: --> <!-- <xsl:output-character character=" " string="&#13;"/> --> <!-- <xsl:output-character character="	" string="&#9;"/> --> </xsl:character-map> <!-- Configure output to use our character map --> <xsl:output method="xml" use-character-maps="preserve-escaped-chars"/> <!-- Standard identity template to copy all nodes --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
How this works:
- The
<xsl:character-map>maps the actual newline character ( ) to the string&#10;—which gets serialized as in the final output. - The
<xsl:output>element tells Saxon to apply this map when writing the XML. - The identity template copies everything else exactly as-is.
2. Explicitly Replace Characters in Text Nodes
If you prefer a more hands-on approach, you can override the template for text nodes to replace specific characters with their escaped references using the replace() function:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Identity template for all nodes except text --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- Override text nodes to replace newlines with escaped references --> <xsl:template match="text()"> <xsl:value-of select="replace(., ' ', '&#10;')"/> <!-- Chain more replaces for other characters: --> <!-- <xsl:value-of select="replace(replace(., ' ', '&#10;'), ' ', '&#13;')"/> --> </xsl:template> </xsl:stylesheet>
This approach directly modifies the text content of each text node, replacing actual characters with their escaped string equivalents.
Key Notes
- Both methods work with Saxon (since they use standard XSLT 2.0 features, which Saxon fully supports).
- Remember that the input parser resolves the escaped sequences first—so you're always working with the actual characters in the XSLT, not the original
string. - Extend the character map or replace functions with any other escaped characters your downstream tools need (like
for carriage returns,	for tabs, etc.).
内容的提问来源于stack exchange,提问作者MidnightLightning

