7
votes

I am trying to get the CDATA content of an XML node using XSL. The node currently looks like this:

<node id="1" text="Book Information" ><![CDATA[This is sample text]]></node>

I need the This is sample text piece. Does anyone have any idea about this?

Thanks in advance.

5
tried with the template. But it is not returning any value.Vijay Balkawade
Actually, from various resources I found something like; <xsl:output cdata-section-elements="text"/> and then to fetch CDATA; <xsl:value-of select="node" /> It returns nothing.Vijay Balkawade
@Vijay: CDATA is an encoding artefact (to avoid lots of entities), they don't show up in the XML infoset and thus are usually not present in APIs and tools.Richard
I edited your question: I thought you were trying to match only xml nodes that contain CDATA sections.Alberto
Edited question to make CDATA valid; there must be 2 close brackets.james.garriss

5 Answers

10
votes

Well, if I use this stylesheet:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="node/text()">
    <xsl:copy/>
  </xsl:template>
</xsl:stylesheet>

on this XML file:

<?xml version="1.0" encoding="utf-8"?>
<node id=1 text="Book Information" ><![CDATA[This is sample text]]></node>

I get a parse error, because id=1 is invalid XML.

Putting quotes around the attribute value (id="1") and rerunning the stylesheet, I get as output:

This is sample text

So there's a start. Basically, just treat the CDATA as a text node and you're on your way.

You said:

I found something like:
<xsl:output cdata-section-elements="text"/>
and then to fetch CDATA:
<xsl:value-of select="node" />

This approach works just fine if you're using value-of as well. Here would be an example along the lines of your comment, using value-of instead. Note, though, that cdata-section-elements only works on the output side, indicating which output XML elements you want to print as CDATA sections instead of plain old character data. It doesn't have anything to do with fetching the data.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output cdata-section-elements="foo"/>
  <xsl:template match="/">
    <foo>
      <xsl:value-of select="node"/>
    </foo>
  </xsl:template>
</xsl:stylesheet>

prints out

<?xml version="1.0"?>
<foo><![CDATA[This is sample text]]></foo>
1
votes

Some other easy steps to achieve this;
Used W3cschools editor to try out.
Sample XML File :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<catalog>
    <cd>
        <disk id="title"><![CDATA[Sample xml]]></disk >
        <disk id="artist"><![CDATA[Vijay]]></disk >
    </cd>
</catalog>


Sample XSL file :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
    <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Title</th>
        <th>Artist</th>
      </tr>
<xsl:for-each select="catalog/cd">

      <tr>
       <td><xsl:value-of select="/catalog/cd/disk[@id='title']"/></td>
       <td><xsl:value-of select="/catalog/cd/disk[@id='artist']"/></td>
       </tr>
</xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>


Final result is;
alt text

0
votes

For output CDATA sections:

You have to use xsl:output/@cdata-section-elements. From http://www.w3.org/TR/xslt#output

The cdata-section-elements attribute contains a whitespace-separated list of QNames. Each QName is expanded into an expanded-name using the namespace declarations in effect on the xsl:output element in which the QName occurs; if there is a default namespace, it is used for QNames that do not have a prefix. The expansion is performed before the merging of multiple xsl:output elements into a single effective xsl:output element. If the expanded-name of the parent of a text node is a member of the list, then the text node should be output as a CDATA section.

Besides DOE, of course.

You can't select CDATA sections with XPath. According to http://www.w3.org/TR/xpath/#data-model

There are seven types of node:

  • root nodes

  • element nodes

  • text nodes

  • attribute nodes

  • namespace nodes

  • processing instruction nodes

  • comment nodes

And from http://www.w3.org/TR/xpath/#section-Text-Nodes

Each character within a CDATA section is treated as character data. Thus, <![CDATA[<]]> in the source document will treated the same as <. Both will result in a single < character in a text node in the tree. Thus, a CDATA section is treated as if the <![CDATA[ and ]]> were removed and every occurrence of < and & were replaced by &lt; and &amp; respectively.

0
votes

The only solution I've found on the web that works is this -

{XSLT}    
<title>
    <xsl:text disable-output-escaping="yes"><![CDATA[ <![CDATA[  ]]></xsl:text>
    <xsl:value-of select="label" disable-output-escaping="yes"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[]]]]><![CDATA[>]]></xsl:text>
</title>

However, this isn't the cleanest of solutions especially if you need to implement this in various parts of your XSLT. Also, if your input XML already has the CDATA (i.e. you are attempting to preserve CDATA) using disable-output-escaping won't work since by that time the CDATA has already been parsed by the XSLT engine and all that'll be left is the content which can end up breaking the xml.

Here's my solution -

Depending on how you're using XSLT, it is possible to use external/injected functions. Doing that you can easily minimise the amount of code you are writing and end up with a much cleaner looking template:

{C#}
public string CDATAWrap(string data)
{
    return "<![CDATA[" + data + "]]>";
}

{XSLT}     
<title>
    <xsl:value-of select="CDataType:CDATAWrap(label)" disable-output-escaping="yes" />
</title>
-1
votes

I tried with various combinations and got the solution for this;

 <xsl:value-of select="/node/."/>