xsltinstallanywhere

Can an XSLT processor preserve empty CDATA sections?


I'm processing an XML document (an InstallAnywhere .iap_xml installer) before handing it off to another tool (InstallAnywhere itself) to update some values. However, it appears that the XSLT transform I am using is stripping CDATA sections (which appear to be significant to InstallAnywhere) from the document.

I'm using Ant 1.7.0, JDK 1.6.0_16, and a stylesheet based on the identity:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" encoding="UTF-8" cdata-section-elements="string" />
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Basically, "string" nodes that look like:

<string><![CDATA[]]></string>

are being processed into:

<string/>

From reading XSLT FAQs, I can see that what is happening is legal as far as the XSLT spec is concerned. Is there any way I can prevent this from happening and convince the XSLT processor to emit the CDATA section?


Solution

  • To do this, you'll need to add a special case for empty string elements and use disable-output-escaping. I don't have a copy of Ant to test with, but the following template worked for me with libxml's xsltproc, which exhibits the same behavior you describe:

    <?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" indent="yes" omit-xml-declaration="yes" cdata-section-elements="string"/>
    
        <xsl:template match="string">
            <xsl:choose>
                <xsl:when test=". = ''">
                    <string>
                        <xsl:text disable-output-escaping="yes"><![CDATA[]]></xsl:text>
                    </string>
                </xsl:when>
    
                <xsl:otherwise>
                    <xsl:copy-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    Input:

    <input>
        <string><![CDATA[foo]]></string>
        <string><![CDATA[]]></string>
    </input>
    

    Output:

    <input>
        <string><![CDATA[foo]]></string>
        <string><![CDATA[]]></string>
    </input>