Working with CDATA

This page contains information on standalone ReadyAPI that has been replaced with ReadyAPI.
To try the new functionality, feel free to download a ReadyAPI trial from our website.

Let's dig into the processing and validation of CDATA sections in your XML documents that are often used to embed blocks of XML data as strings inside an existing XML structure. Specifically, we are going to look at:

  • Property Transfers: How to transfer values to or from an embedded XML block.
  • Assertions: How to use the standard XPath assertion to assert embedded XML content.
  • Validations: How to create scripts to validate the XML data of these strings given that you have a schema available.

In the end, you are going to learn how the ReadyAPI event handler can help you do all this with ease.

1. CDATA Sections

CDATA sections are used in XML documents to avoid longer blocks of text that could otherwise be interpreted as markup, for example:

<message><![CDATA[<data>some embedded xml</data>]]></message>

In this example, the text <data>some embedded xml</data> is a string, not XML data. Another way of writing this could be:

<message>&lt;data&gt;some embedded xml&lt;/data&gt;</message>

This is an equivalent of the previous example that uses a CDATA section. Parsing either of these examples will return the content as a string, not as parsed XML data.

If an embedded XML document contains a CDATA section, the embedded closing tag (]]>) terminates the outer CDATA section. Therefore, you cannot embed a CDATA section straight off. You will have to temporarily terminate the outer CDATA section before inserting the terminator of the inner CDATA section. Suppose, you have the following string:

<data>some embedded xml <![CDATA[<text>with xml</text>]]></data>

To add it to an XML document, you can use standard XML entities:

<message>&lt;data&gt;some embedded xml &lt;![CDATA[&lt;text&gt;with xml&lt;/text&gt;]]&gt;&lt;/data&gt;</message>

or split it into three strings:

<message><![CDATA[<data>some embedded xml <![CDATA[<text>with xml</text>]]]]>><![CDATA[</data>]]></message>
  • The first CDATA section wraps the following text: <data>some embedded xml <![CDATA[<text>with xml</text>]]. Note that this CDATA section does not contain the > character that would turn the last three characters into the CDATA terminator.
  • Then comes a single character >. This character is not treated as a markup, so there is no need to convert it to the &gt; entity.
  • Another CDATA section contains the string </data>.

Either of these methods allows you to get the original string with any XML processor.

2. CDATA Sections in SoapUI

It is (unfortunately) quite common that SOAP messages contain some part of the payload in a request or response as a string, not as XML data, which has both advantages and disadvantages. In SoapUI, these XML strings are not easily validated against a schema unless you use scripting. They are not easily asserted with XPath, and using them as targets/sources for property transfers is difficult, since they are strings, not XML data. Also, the extended message viewers in ReadyAPI (Outline, Overview) show them as strings, not as markup, which can be confusing.

Suppose, you have the following response message for an item search:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
   xmlns:sam="http://www.example.org/sample/">
   <soapenv:Header/>
   <soapenv:Body>
      <sam:searchResponse>
         <sam:searchResponse>
            <item><id>1234</id><description><![CDATA[<item><width>123</width><height>345</height>
<length>098</length><isle>A34</isle></item>]]></description><price>123</price>
            </item>
         </sam:searchResponse>
      </sam:searchResponse>
   </soapenv:Body>
</soapenv:Envelope>

Here you see the description of the item being embedded as an XML string. On the ReadyAPI Outline and Overview tabs, it is displayed as a string:

The Overview tab

and

The Outline tab

Fortunately, there are some workarounds.

3. Property Transfers and CDATA Sections

You use the Property Transfers test steps to transfer property values between requests, responses, properties, and so on (to learn more, see Transferring Property Values). A common scenario is transferring a value from a response message to the following request (for example a session ID). In the standard case, this is straightforward:

  1. Set the response message as a source and specify an XPath statement to obtain the desired value.
  2. Set the request message as a target and specify an XPath statement to insert the transferred value.

Tip: In ReadyAPI, all this is done via point-and-click wizards.

In our scenario, the XPath statement can point only to the element containing the XML message string, not "inside" it.

There are several possible solutions:

  • You can use several temporary property transfers that extract CDATA XML and that use it as a source or a target property. In this case, the CDATA content will be treated as XML content and you can point to the desired node in it.
  • Another approach is to use the saxon:parse function that parses a string to an XML object.

Let's combine both approaches into an example. Suppose, you want to transfer the embedded "isle" value from the sample message above to the following search query, which also contains embedded XML data:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:sam="http://www.example.org/sample/">
   <soapenv:Header/>
   <soapenv:Body>
     <sam:search>
        <sessionid>123</sessionid>
        <searchstring><![CDATA[<isle>?</isle>]]></searchstring>
     </sam:search>
   </soapenv:Body>
</soapenv:Envelope>

To transfer the value from the CDATA section of the response message to the CDATA section of the request:

  1. Add a temporary custom property to the test case

    Test Case

  2. Insert the Property Transfer test step between the requests and configure it as follows:

    1. Create a transfer that transfers the CDATA section of the request (in the searchstring element) to the Temporary Target property:

      First Property Transfer

    2. Create another transfer that obtains the source value from the CDATA section of the response by using the saxon:parse function and put it to the Temporary Target property:

      Second Property Transfer

    3. Now you have the desired value of the searchstring element in the Temporary Target property, so transfer it back to the request with the needed value inserted:

      Third Property Transfer

Running these three transfers extracts the desired value from the embedded XML data in the response and writes it to the embedded XML data in the request.

4. XPath Assertions and CDATA

By using the saxon:parse function you can also assert an XML string embedded into a CDATA element with the standard XPath processor. In ReadyAPI, you can create it easily: right-click the desired node on the Outline tab and select Add Assertion > for Content:

Adding Assertion for Content

If you do not have ReadyAPI, add the XPath match assertion manually.

Then specify the desired XPath expression with the saxon:parse function:

Adding Assertion for Content

5. Validation of CDATA Content

Finally we'll look at validation. The schema of the message defines only the XML string as a string, not its complex content. You can create a script that loads an XSD schema from the file system and validates the XML data in the description:

import com.eviware.soapui.support.XmlHolder
import javax.xml.XMLConstants
import javax.xml.transform.stream.StreamSource
import javax.xml.validation.SchemaFactory

def holder = new XmlHolder( messageExchange.responseContentAsXml )
holder.namespaces["sam"] = "http://www.example.org/sample/"
def node = holder["//sam:searchResponse[1]/sam:searchResponse[1]/item[1]/description[1]"]

def factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
def schema = factory.newSchema(new StreamSource(new FileReader("..")))
def validator = schema.newValidator()
validator.validate(new StreamSource(new StringReader(node)))

Use the following XSD file for this example:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="" elementFormDefault="unqualified">
   <element name="item">
   <complexType>
      <sequence>
         <element name="width" type="long"></element>
         <element name="height" type="long"></element>
         <element name="length" type="long"></element>
         <element name="isle" type="string"></element>
      </sequence>
   </complexType>
   </element>
</schema>

This can come in handy if you want to validate REST/HTTP requests that do not have a formalized schema, and since Groovy can validate by using DTD and RelaxNG as well, this can be performed equally.

6. An Event Handler to the Rescue

Wouldn't it be nice if we could just remove those CDATA tags before ReadyAPIcesses the response so that it is seen as standard XML data? Sure, it wouldn't be compliant with the original schema, but it would make transfers and assertions much easier. Well, once again, event handlers in ReadyAPI can do this for us:

  1. Open the Project window.
  2. Select the Events tab and add a RequestFilter.afterRequest handler. Set its content to
def content = context.httpResponse.responseContent
content = content.replaceAll( "<!\\[CDATA\\[", "" )
content = content.replaceAll( "]]>", "" )

//log.info( content )

context.httpResponse.responseContent = content

This effectively removes any <![CDATA[ and ]]> strings from the response XML, which enables SoapUI to process the entire content as XML data, allowing you to view/handle responses as standard XML data. For example, you can now see nicer formatting in the Overview view:

niceoverview

Also, it allows you to create property transfers and assertions right in the Outline view as usual:

niceoutline

Of course, this approach has some severe limitations: it depends on the response formatting, and Schema Compliance assertions will probably fail, but it might be what you need to get the job done.

No one knows APIs better than SmartBear. Find out what our Pro version of SoapUI can do to improve your testing.