Serialization to XML with CDATA tags
Sometimes you need CDATA tags around complex text in your destination XML.
There is no build in way of doing so (that I have found).
I found this nifty CdataWrapper class on Marc Gravell's blog. Thanks Marc!
Here is the basic class.
public sealed class CDataWrapper : IXmlSerializable
{
// implicit to/from string
public static implicit operator string(CDataWrapper value)
{
return value == null ? null : value.Value;
}
public static implicit operator CDataWrapper(string value)
{
return value == null ? null : new CDataWrapper
{
Value =
value
};
}
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
// "" => <Node/>
// "Foo" => <Node><![CDATA[Foo]]></Node>
public void WriteXml(XmlWriter writer)
{
if (!string.IsNullOrEmpty(Value))
{
writer.WriteCData(Value);
}
}
// <Node/> => ""
// <Node></Node> => ""
// <Node>Foo</Node> => "Foo"
// <Node><![CDATA[Foo]]></Node> => "Foo"
public void ReadXml(XmlReader reader)
{
if (reader.IsEmptyElement)
{
Value = "";
}
else
{
reader.Read();
switch (reader.NodeType)
{
case XmlNodeType.EndElement:
Value = ""; // empty after all...
break;
case XmlNodeType.Text:
case XmlNodeType.CDATA:
Value = reader.ReadContentAsString();
break;
default:
throw new InvalidOperationException("Expected text/cdata");
}
}
}
// underlying value
public string Value { get; set; }
public override string ToString()
{
return Value;
}
}
To use it, you must change your DataMember from String to CDataWrapper, make it private and give it a public property.
This:
[DataMember]
pubblic string Answer { get; set; }
Becomes this:
pubblic string Answer { get; set; }
[DataMember(Name="Answer", EmitDefaultValue=false)]
private CDataWrapper AnswerCDATA
{
get { return Answer; }
set { Answer = value; }
}
Then the serialization is done with one line:
. . . XElement xml = Microsoft.ServiceModel.Web.SerializationExtensions.ToXml(object); .
So simple!!!!
More on Object Serialization to XML
It seems there are hundreds of ways to serialize object.
Here are just a few:
This one now seems very clumbsy. You pass in the object and the object type. It returns a string. I can't see the point of using this one any more now that I have found the next one.
public static string ContractObjectToXml<T>(T obj)
{
DataContractSerializer dataContractSerializer = new DataContractSerializer(obj.GetType());
String text;
using (MemoryStream memoryStream = new MemoryStream())
{
dataContractSerializer.WriteObject(memoryStream, obj);
byte[] data = new byte[memoryStream.Length];
Array.Copy(memoryStream.GetBuffer(), data, data.Length);
text = Encoding.UTF8.GetString(data);
}
return text;
}
This one I have started using is the most simple I have seen so far.
. . . XElement xml = Microsoft.ServiceModel.Web.SerializationExtensions.ToXml(object); .
This one returns a stream. It gives you a bit more control over what is shown and included in the XML.
public static Stream Serialize(Object obj)
{
MemoryStream memoryStream = new MemoryStream();
DataContractSerializer dcSerializer = new DataContractSerializer(obj.GetType());
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = new UTF8Encoding(false);
xmlWriterSettings.ConformanceLevel = ConformanceLevel.Document;
xmlWriterSettings.Indent = true;
//xmlWriterSettings.OmitXmlDeclaration = true;
using (XmlWriter xWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
dcSerializer.WriteObject(xWriter, obj);
xWriter.Flush();
//return Encoding.UTF8.GetString(memoryStream.ToArray());
memoryStream.Position = 0;
return memoryStream;
}
}