views:

526

answers:

3

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?

A: 

Once the XML parser has finished with the XML, there is absolutely no difference between <![CDATA[abc]]> and abc. And the same is true for an empty string - <![CDATA[]]> resolves to nothing at all, and is silently ignored. It has no representation in the XML model. In fact, there is no way to tell the difference from CDATA and regular strings, and neither has any representation in the XML model.

Sorry.

Now, why would you want this? Perhaps there is another solution which can help you?

lavinio
A: 

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"&gt;
    <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>
Ben Blank
A: 

Found a solution:

<xsl:template match="string">
 <xsl:element name="string">
  <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text><xsl:value-of select="text()" disable-output-escaping="yes" /><xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
 </xsl:element>
</xsl:template>

I also removed the cdata-section-elements attribute from the <xsl:output> element.

Basically, since the CDATA sections are significant to the next tool in the chain, I take output them manually.

Brian Bassett