Menu

Serialization – Xml – Readonly Properties

August 26, 2009 - .NET / C#, Geek Bits, Serialization

I was recently working on a project where I needed to serialize objects with properties marked as readonly (can only be set inside a constructor).  The objects are part of a WCF RESTful service security infrastructure so I wasn’t about to change their design just because I needed to serialize them, there is no need too!  Anyway baring that in mind the lions share of the serialization duties in this project rely on POX (Plain Old Xml) serialisation and XmlFormatter’s etc.  So … if you have an object like this:

    [Serializable()]
    public class MyComplexObject
    {
        private string _thisIsPrivate = "This is the private string";
        private List<string> _thisIsAPrivateList = new List<string>();

        public readonly string Name;
        public readonly int Age;
        public readonly string HairColour;

        [NonSerialized()]
        public string WeDontWantThisSerialized;
        public string ThisIsntReadonly { get; set; }
        public int StringsInTheCollection
        {
            get { return _thisIsAPrivateList.Count; }
        }

        public MyComplexObject(string name, int age, string haircolour)
        {
            Name = name;
            Age = age;
            HairColour = haircolour;
        }

        public string GetThePrivateString()
        {
            return _thisIsPrivate;
        }

        public void AddAStringToTheCollection(string input)
        {
            _thisIsAPrivateList.Add(input);
        }
    }

You’re almost certainly going to run into Xml serialization issues unless you use a BinaryFormatter object and Serialize/Deserialize that way. In the next snippet you’ll see two extension methods written in C# that perform these tasks and extend both the Object and String types.

This first method takes your CLR object as input and produces a base 64 encoded string object from it ready for storing somewhere.

 public static string BinarySerializeToXmlString(this Object obj)
 {
     StringBuilder outputString = new StringBuilder();
     using (MemoryStream memoryStream = new MemoryStream())
     {
         using (XmlWriter writer = XmlWriter.Create(outputString))
         {
             BinaryFormatter bf = new BinaryFormatter();
             bf.Serialize(memoryStream, obj);
             memoryStream.Seek(0, SeekOrigin.Begin);
             writer.WriteElementString("ObjectDataElement", System.Convert.ToBase64String(memoryStream.ToArray()));
         }
     }
     return outputString.ToString();
 }

This next method takes in the string produced from the previous method and turns it back into a a CLR object of the type specified by T.

        public static T DeserializeFromBinaryXmlString<T>(this String input) where T : class
        {
            T deserialisedObject = null;
            byte[] data;
            using (XmlReader reader = XmlReader.Create(new StringReader(input)))
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    reader.Read();
                    reader.ReadStartElement();
                    data = System.Convert.FromBase64String(reader.ReadString());
                    memoryStream.Write(data, 0, data.Length);
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    BinaryFormatter bf = new BinaryFormatter();
                    try
                    {
                        deserialisedObject = (T)bf.Deserialize(memoryStream);
                    }
                    catch (Exception ex)
                    {
                        // you'll probably want to log this instead!
                        if(Debugger.IsAttached) Console.WriteLine(ex.Message);
                    }
                }
            }
            return deserialisedObject;
        }

If you want to have a look at this in action I have produced a small demonstration solution which you can download and have a look at, pinch the code from etc

Share

Leave a Reply

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

*