views:

732

answers:

4

I want to save my program's DataModel objects to a file, and be able to reload the same object graph again. I want it to be in some flavor of text file, so I can diff it and open it in a text editor. XML would be fine, and seems like a good place to start since .NET has XML serialization built in.

But which flavor of XML serialization should I pick? I know about SoapFormatter (but it's deprecated now), XamlWriter (nice but very limited), XmlSerializer and DataContractSerializer (neither of which I know much about yet). (And that's just the ones from Microsoft - good grief!)

I'm also open to open-source frameworks, and I'm not tied to XML (JavaScriptSerializer looks interesting too).

Some of my general preferences in a serialization framework:

  • I really shouldn't have to write any code (or XML configuration files) to get a newly-added property to serialize. I could (grudgingly) accept having to add an attribute, but the less overhead there is on me, the better.
  • Convention over configuration is good.
  • Bonus points for anything that can serialize a cyclic object graph. I could avoid this, if I had to, but I'd rather not make my coding harder just for the convenience of someone else's serialization framework.
  • I prefer something that saves property values, rather than reaching directly into the private fields.
  • I'd like something that allows some future-proofing. If I rename a property or an enum value, I don't want my old files to be toast; I want some way to define an upgrade path and remap the values on load (preferably without writing reams of XML configuration files), and I want to be able to write automated regression tests that prove I can open old files.
  • If it can automatically ignore my objects' events, without me needing to add extra attributes to every one, that'd be great. Events would never make sense to write to a text format.
  • It would be extra-cool if it worked in Silverlight as well as full-fledged .NET. (I don't have any near-term need for this; it would just be cool.)

Suggestions?

+1  A: 

I think the DataContractSerializer is your best bet - it is a much more modern serializer for the .NET framework and works quite well.

However, in the interest of fairness, I would suggest you read XmlSerializer vs DataContractSerializer: Serialization in Wcf for a detailed comparison.

Andrew Hare
+1  A: 

use XmlSerializer or DataContractSerializer, unless and until they cannot deliver on your requirements. I'm betting they can deliver and you will need nothing else.

Cheeso
Speaking from experience, we found XmlSerializer annoying because it does not allow you to serialize private fields. This ultimately drove us to binary serialization (http://msdn.microsoft.com/en-us/library/72hyey7b(VS.71).aspx). DataContractSeraializer looks like it has resolved this issue. I have yet to try it, though.
Matthew Flaschen
+1  A: 

That's a big list of requirements. Personally, I think that DataContractSerializer would meet most of your needs.

1) You can add properties and they will be picked up automatically (assuming you have .Net 3.5 SP1)

2) It has some versioning support

3) It exists in Silverlight, albeit missing a few features, like PreserveObjectReferences (I think)

4) You can explicitly define what you want serialized, so you can exclude your private fields, although they will be included if you don't specify anything at all.

5) Pretty sure it handles cyclic object graphs, but don't quote me on that.

womp
Here's some good info on its versioning support: Data Contract Versioning http://msdn.microsoft.com/en-us/library/ms731138.aspx, Best Practices: Data Contract Versioning http://msdn.microsoft.com/en-us/library/ms733832.aspx. – Joe White 26 secs ago
Joe White
It looks like the Silverlight one *does* support PreserveObjectReferences (which is how you get cyclic object graphs).
Joe White
A: 

I wrote sth.. hope to help u..

public class TAObjectSerializer
{
    private static void __serializeData(object result, Type propType, XmlWriter wr)
    {
        if (result != null)
        {
            TypeConverter tc = TypeDescriptor.GetConverter(result);
            if (tc != null && tc.CanConvertTo(typeof(string)) && tc.CanConvertFrom(typeof(string)))
            {
                wr.WriteString(tc.ConvertTo(result, typeof(string)) as string);
            }
            else if (propType.IsArray)
            {
                Array tmp = result as Array;
                if (propType.GetElementType() == typeof(object))
                {
                    for (int i = 0; i < tmp.Length; i++)
                    {
                        object v = tmp.GetValue(i);
                        wr.WriteStartElement("item");
                        if (v == null)
                        {
                            wr.WriteAttributeString("type", "");
                        }
                        else
                        {
                            Type vt = v.GetType();
                            wr.WriteAttributeString("type", (vt.IsPrimitive || v is string || v is decimal || v is DateTime || vt.IsArray) ? vt.ToString() : vt.AssemblyQualifiedName);
                            __serializeData(v, v.GetType(), wr);
                        }
                        wr.WriteEndElement();
                    }
                }
                else
                {
                    for (int i = 0; i < tmp.Length; i++)
                    {
                        object v = tmp.GetValue(i);
                        wr.WriteStartElement("item");
                        if (v != null)
                        {
                            __serializeData(v, v.GetType(), wr);
                        }
                        wr.WriteEndElement();
                    }
                }
            }
            else if (propType.IsSerializable)
            {
                using (MemoryStream __ = new MemoryStream())
                {
                    BinaryFormatter bf = new BinaryFormatter();
                    bf.Serialize(__, result);
                    wr.WriteString(Convert.ToBase64String(__.ToArray()));
                }
            }
            else if (propType.IsClass)
            {
                wr.WriteRaw(__serialize(result));
            }
        }
    }
    private static void __serializeItem(object obj, PropertyInfo pi, XmlWriter wr)
    {
        Type propType = pi.PropertyType;
        object result = pi.GetValue(obj, null);
        wr.WriteStartElement("property");
        wr.WriteAttributeString("type", (propType.IsPrimitive || result is string || result is decimal || result is DateTime || propType.IsArray) ? propType.ToString() : propType.AssemblyQualifiedName);
        wr.WriteAttributeString("name", pi.Name);
        __serializeData(result, propType, wr);
        wr.WriteEndElement();
    }
    private static string __serialize(object obj)
    {
        StringBuilder sb = new StringBuilder();
        XmlWriterSettings set = new XmlWriterSettings();
        set.OmitXmlDeclaration = true;
        using (XmlWriter wr = XmlWriter.Create(sb, set))
        {
            Type t = obj.GetType();
            wr.WriteStartElement("object");
            wr.WriteAttributeString("type", t.AssemblyQualifiedName);
            if (t.IsClass && !(obj is string))
            {
                PropertyInfo[] list = t.GetProperties();
                foreach (PropertyInfo pi in list)
                {
                    if (pi.CanRead && pi.CanWrite && pi.GetCustomAttributes(typeof(NonSerializedAttribute), true).Length == 0)
                    {
                        __serializeItem(obj, pi, wr);
                    }
                }
            }
            wr.WriteEndElement();
        }
        return sb.ToString();
    }
    public static XmlDocument Serialize(object obj)
    {
        if (obj == null)
            throw new ArgumentNullException("obj");
        XmlDocument doc = new XmlDocument();
        string str = __serialize(obj);
        if (!string.IsNullOrEmpty(str))
            doc.LoadXml(str);
        return doc;
    }
    private static object __deserializeItem(Type propType, XmlNode node)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(propType);
        if (tc != null && tc.CanConvertTo(typeof(string)) && tc.CanConvertFrom(typeof(string)))
        {
            return tc.ConvertFrom(node.InnerText);
        }
        else if (propType.IsArray)
        {
            if (propType.GetElementType() == typeof(object))
            {
                XmlNodeList nl = node.SelectNodes("item");
                Array tmp = Array.CreateInstance(typeof(object), nl.Count);
                for (int i = 0; i < nl.Count; i++)
                {
                    XmlNode p = nl[i];
                    Type _t = Type.GetType(p.Attributes["type"].Value);
                    if (_t == null)
                        tmp.SetValue(null, i);
                    else
                        tmp.SetValue(__deserializeItem(_t, p), i);
                }
                return tmp;
            }
            else
            {
                Type _t = propType.GetElementType();
                XmlNodeList nl = node.SelectNodes("item");
                Array tmp = Array.CreateInstance(_t, nl.Count);
                for (int i = 0; i < nl.Count; i++)
                {
                    XmlNode p = nl[i];
                    tmp.SetValue(__deserializeItem(_t, p), i);
                }
                return tmp;
            }
        }
        else if (propType.IsSerializable)
        {
            using (MemoryStream __ = new MemoryStream(Convert.FromBase64String(node.InnerText)))
            {
                BinaryFormatter bf = new BinaryFormatter();
                return bf.Deserialize(__);
            }
        }
        else if (propType.IsClass)
        {
            return __deserialize(node);
        }
        return null;
    }
    private static object __deserialize(XmlNode t)
    {
        try
        {
            object tmp = Activator.CreateInstance(Type.GetType(t.Attributes["type"].Value));
            XmlNodeList nl = t.SelectNodes("property");
            Type objType = tmp.GetType();
            foreach (XmlNode p in nl)
            {
                string name = p.Attributes["name"].Value;
                PropertyInfo pi = objType.GetProperty(name);
                Type propType = Type.GetType(p.Attributes["type"].Value);
                if (propType == pi.PropertyType)
                {
                    pi.SetValue(tmp, __deserializeItem(propType, p), null);
                }
            }
            return tmp;
        }
        catch
        {
        }
        return null;
    }
    public static object Deserialize(XmlDocument doc)
    {
        XmlNode nd = doc.SelectSingleNode("object");
        if (nd == null)
            throw new ArgumentOutOfRangeException();
        return __deserialize(nd);
    }
}

**

Usage :
Serialize : TAObjectSerializer.Serialize(myClassInstance); //this returns as XmlDocument
Deserialize : TAObjectSerializer.Deserialize(myXmlDocument); //this returns as object instance

**

this class will serialize all READ_WRITE properties if property is not marked by NonSerializedAttribute attribute

Good luck!

Tolgahan Albayrak