Problems serializing a class to XML and including a CDATA section

c#, xml, xml-serialization

Solution

In response to the 'spaces' you are seeing after your edit, it is because of the encoding you are using (Unicode, 2 bytes per character).

Try:

settings.Encoding = new Utf8Encoding(false);

EDIT:

Also, note that format of the `MemoryStream` is not necessarily a valid UTF-8 encoded string! You can use a `StringBuilder` instead of `MemoryStream` to create your inner writer.

    public void WriteXml(XmlWriter writer)   
    {   
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();   
        ns.Add("", "");   

        XmlWriterSettings settings = new XmlWriterSettings();   

        settings.OmitXmlDeclaration = true;   
        settings.Indent = true;   

        StringBuilder sb = new StringBuilder();
        using (XmlWriter innerWriter = XmlWriter.Create(sb, settings))   
        {   
            shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns);   
            innerWriter.Flush();   
            writer.WriteCData(sb.ToString());   
        }   
    }

Problem

I have a class that is serialized into XML for consumption by a web service. In this classes instance the XML must include a CDATA section for the web service to read it but I am at a loss on how to implement this. The XML needs to look like: ``` <UpdateOrderStatus> <Action>2</Action> <Value> <![CDATA[ <Shipment> <Header> <SellerID> ... ]]> </Value> </UpdateOrderStatus> ``` I am able to generate the appropriate XML, except for the CDATA part. My class structure looks like: ``` public class UpdateOrderStatus { public int Action { get; set; } public ValueInfo Value { get; set; } public UpdateOrderStatus() { Value = new ValueInfo(); } public class ValueInfo { public ShipmentInfo Shipment { get; set; } public ValueInfo() { Shipment = new ShipmentInfo(); } public class ShipmentInfo { public PackageListInfo PackageList { get; set; } public HeaderInfo Header { get; set; } public ShipmentInfo() { PackageList = new PackageListInfo(); Header = new HeaderInfo(); } .... ``` I have seen some suggestions on using: ``` [XmlElement("node", typeof(XmlCDataSection))] ``` but that causes an exception I have also tried ``` [XmlElement("Value" + "<![CDATA[")] ``` but the resulting XML is incorrect showing ``` <Value_x003C__x0021__x005B_CDATA_x005B_> .... </Value_x003C__x0021__x005B_CDATA_x005B_> ``` Can anyone show me what I am doing wrong, or where I need to go with this? --Edit-- making shipmentInfo serializable per carlosfigueira works for the most part, however I get extra ? characters in the resulting XML ( see post Writing an XML fragment using XmlWriterSettings and XmlSerializer is giving an extra character for details ) As such I changed the Write XML method to: ``` public void WriteXml(XmlWriter writer) { using (MemoryStream ms = new MemoryStream()) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); XmlWriterSettings settings = new XmlWriterSettings(); settings.OmitXmlDeclaration = true; settings.Encoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false); settings.Indent = true; using (XmlWriter innerWriter = XmlWriter.Create(ms, settings)) { shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns); innerWriter.Flush(); writer.WriteCData(Encoding.UTF8.GetString(ms.ToArray())); } } } ``` However I am not getting an exception: ``` System.InvalidOperationException: There was an error generating the XML document. ---> System.ArgumentException: '.', hexadecimal value 0x00, is an invalid character. ``` --Edit -- The exception was caused by the inclusion of my previous serializeToString method. Since removing that the CDATA output is correct, except for a spacing issue, but I am also getting a namespace and xml declaration that should be removed by the XML settings specified. Output is: ``` <?xml version="1.0"?> <UpdateOrderStatus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Action>1</Action> <Value><![CDATA[< S h i p m e n t I n f o > < P a c k a g e L i s t > < P a c k a g e > < S h i p D a t e > 2 0 1 2 - 0 7 - 1 3 T 1 1 : 5 8 : 5 1 . 0 9 2 5 6 1 5 - 0 4 : 0 0 < / S h i p D a t e > < I t e m L i s t > < I t e m > < S h i p p e d Q t y > 0 < / S h i p p e d Q t y > < / I t e m > < / I t e m L i s t > < / P a c k a g e > < / P a c k a g e L i s t > < H e a d e r > < S e l l e r I d > S h i p m e n t h e a d e r < / S e l l e r I d > < S O N u m b e r > 0 < / S O N u m b e r > < / H e a d e r > < / S h i p m e n t I n f o > ]]></Value> </UpdateOrderStatus> ``` Any ideas of avoiding the BOM using the new class? --Edit 3 -- SUCCESS! I have implemented changes suggested below and now have the following writer class and test methods: ``` UpdateOrderStatus obj = new UpdateOrderStatus(); obj.Action = 1; obj.Value = new UpdateOrderStatus.ValueInfo(); obj.Value.Shipment = new UpdateOrderStatus.ValueInfo.ShipmentInfo(); obj.Value.Shipment.Header.SellerId = "Shipment header"; obj.Value.Shipment.PackageList = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo(); obj.Value.Shipment.PackageList.Package = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo.PackageInfo(); obj.Value.Shipment.PackageList.Package.ShipDate = DateTime.Now; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); XmlWriterSettings settings = new XmlWriterSettings(); settings.OmitXmlDeclaration = true; settings.Encoding = new UTF8Encoding(false); settings.Indent = true; XmlSerializer xs = new XmlSerializer(typeof(UpdateOrderStatus)); MemoryStream ms = new MemoryStream(); XmlWriter writer = XmlWriter.Create(ms, settings); xs.Serialize(writer, obj, ns); Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); } public void WriteXml(XmlWriter writer) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); XmlWriterSettings settings = new XmlWriterSettings(); settings.OmitXmlDeclaration = true; settings.Indent = true; StringBuilder sb = new StringBuilder(); using (XmlWriter innerWriter = XmlWriter.Create(sb, settings)) { shipmentInfoSerializer.Serialize(innerWriter, this.Shipment, ns); innerWriter.Flush(); writer.WriteCData(sb.ToString()); } } ``` This produces the following XML: ``` <UpdateOrderStatus> <Action>1</Action> <Value><![CDATA[<ShipmentInfo> <PackageList> <Package> <ShipDate>2012-07-13T14:05:36.6170802-04:00</ShipDate> <ItemList> <Item> <ShippedQty>0</ShippedQty> </Item> </ItemList> </Package> </PackageList> <Header> <SellerId>Shipment header</SellerId> <SONumber>0</SONumber> </Header> </ShipmentInfo>]]></Value> </UpdateOrderStatus> ```

Original source

Related problems