I've approached this slightly differently. Inspired by Martin Verdejo's answer with replacing the <![CDATA[]]>
sections, simply removing them still doesn't protect you from the purpose of having the CDATA tags to begin with: nested content that itself contains XML tags.
In my approach I use regular expression to match the CDATA sections then replace them with the escaped xml of the CDATA content. This attempts to preserve the protections of the CDATA tags and allow us to use Dom.XMLNode to retrieve the text of those elements.
Escape XML content within CDATA tags then remove CDATA tags
String xml =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<root>' +
' <SomeNode><![CDATA[<b>contains html tags</b>]]></SomeNode>' +
'</root>';
// replace CDATA sections with parseable tokens
xml = xml.replaceAll( '<!\\[CDATA\\[', 'XML_CDATA_START' ).replaceAll( ']]>', 'XML_CDATA_END' );
// we will build up a map of original text and replacement text
Map<String, String> replacementMap = new Map<String, String>();
// regular expression to match sections we want to replace
Pattern myPattern = Pattern.compile( '(XML_CDATA_START)(.*?)(XML_CDATA_END)' );
Matcher myMatcher = myPattern.matcher( xml );
while ( myMatcher.find() ) {
// the regex was too complicated for Matcher.replaceFirst(..)
// so have to do it manually so just put in this map the
// original text and the replacement text, we do replacing later
replacementMap.put( myMatcher.group(), myMatcher.group(2).escapeXML() );
}
// replace in the xml each CDATA section with the escaped XML of its inner content
for ( String key : replacementMap.keySet() ) {
xml = xml.replace( key, replacementMap.get( key ) );
}
// parse the xml like normal
Dom.Document doc = new Dom.Document();
doc.load( xml );
Dom.XMLNode rootNode = doc.getRootElement();
String text = rootNode.getChildElement( 'SomeNode', null ).getText();
System.debug( text ); // prints: <b>contains html tags</b>
Encode/Decode Fails if CDATA inner content contains XML tags
String xml =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<root>' +
' <SomeNode><![CDATA[<b>contains html tags</b>]]></SomeNode>' +
'</root>';
// this does remove the CDATA tags but does not handle the inner content
String encoded = EncodingUtil.urlEncode( xml, 'UTF-8' );
encoded = encoded.replaceAll( '%3C%21%5BCDATA%5B', '' ); // i.e. '<![CDATA['
encoded = encoded.replaceAll( '%5D%5D%3E', '' ); // i.e. ']]>'
xml = EncodingUtil.urlDecode( encoded, 'UTF-8' );
Dom.Document doc = new Dom.Document();
doc.load( xml );
Dom.XMLNode rootNode = doc.getRootElement();
String text = rootNode.getChildElement( 'SomeNode', null ).getText();
System.debug( text ); // nothing prints because without CDATA tags 'SomeNode' has a child element <b>
Vote on IdeaExchange
Please vote for this idea to be implemented natively: https://success.salesforce.com/ideaView?id=0873000000081cUAAQ