.NET XML Serialization of CDATA ATTRIBUTE

Recently I needed to create a .NET class (C#) that when serialized should contain a CDATA attribute similar to this:

<?xml version="1.0" encoding="utf-8" ?>
<RootNode Type="zzzzz" Version="1" >    
    <CDataAttribute>
        <![CDATA[
         <?xml version="1.0" encoding="utf-8" ?>
        ]]>
    </CDataAttribute>    
</RootNode>

 

I started by creating a schema for validating this xml that can be (generated with xsd.exe and slightly altered):

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Example" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="RootNode">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="CDataAttribute" type="xs:string" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="Type" type="xs:string" />
      <xs:attribute name="Version" type="xs:string" />
    </xs:complexType>
  </xs:element>
  <xs:element name="Example">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="RootNode" />
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

Note that the CDataAttribute is specified as type string and I could not find any type for referring a CDATA.

Now if you take this schema and generate a class from it (again with xsd.exe) you will see that the CDataAttribute is, as expected, represented by a string.

private string cDataAttributeField;

If you use the generated class and serialize a object from it, the .NET serializer will not encode the CDataAttribute between a CDATA xml structure, but instead will escape all the special characters that you place inside the CDATA.

<?xml version="1.0"?>
<Example xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <RootNode Type="xxxx" Version="1">
    <CDataAttribute>&lt;?xml version="1.0" encoding="utf-8" ?&gt;</CDataAttribute>
  </RootNode>
</Example>

As I really needed it to be placed inside a CData section, I searched on the Internet but did found a satisfying solution for this “problem”, but using the idea behind some of the articles (use XmlNode for the class CDataAttribute member instead of string) my solution was the following.

 

First I generated my schema class without the CDataAttribute.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Example" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="RootNode">
    <xs:complexType>
      <xs:sequence>
        <!-- NOTE: We could not generate a CDATA directly from XSD.
             Generated by partial class -->
        <!--<xs:element name="CDataAttribute" type="xs:string" minOccurs="0"/>-->
      </xs:sequence>
      <xs:attribute name="Type" type="xs:string" />
      <xs:attribute name="Version" type="xs:string" />
    </xs:complexType>
  </xs:element>
  <xs:element name="Example">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="RootNode" />
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

Then I extended the partial generated class with my own implementation of the CDataAttribute.

using System;
using System.Xml;
 
public partial class RootNode
{
    private XmlNode cDataAttributeField;
 
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public XmlNode CDataAttribute
    {
        get {return this.cDataAttributeField;}
        set {this.cDataAttributeField = value;}
    }
    
    /// <summary>
    /// Getter/Setter method for the CDATA document content field.
    /// Encapsulates the internal CDATA XmlNode
    /// </summary>
    [System.Xml.Serialization.XmlIgnore()]
    public String CDataAttributeByString
    {
        get 
        { // Retrieves the content of the encapsulated CDATA
          return cDataAttributeField.Value;
        }
 
        set
        { // Encapsulate in a CDATA XmlNode
          XmlDocument xmlDocument = new XmlDocument();
          this.cDataAttributeField = xmlDocument.CreateCDataSection(value);
        }
    }
}

Note that I used a XMLNode for representing the class member that will be serialized. For setting this XMLNode I created a property (CDataAttributeByString) that receives a string and places in the XmlNode a CDataSection containing the received string value.

As so I only work with the CDataAttributeByString property, that is excluded from the serialization, and I let the .NET serializer use the CDataAttribute property for properly creating my CDataAttribute.

 

Using the extended class, the serialized xml will now be:

<?xml version="1.0"?>
<Example xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <RootNode Type="xxxx" Version="1">
    <CDataAttribute>
        <![CDATA[<?xml version="1.0" encoding="utf-8" ?>]]>
    </CDataAttribute>
  </RootNode>
</Example>

 

I hope that this can help some of you, and if you know a better solution please tell me.

I leave a sample project here.

Leave a Reply

Your email address will not be published. Required fields are marked *