31

I'm trying to use a System.Dynamic.ExpandoObject so I can dynamically create properties at runtime. Later, I need to pass an instance of this object and the mechanism used requires serialization.

Of course, when I attempt to serialize my dynamic object, I get the exception:

System.Runtime.Serialization.SerializationException was unhandled.

Type 'System.Dynamic.ExpandoObject' in Assembly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.

Can I serialize the ExpandoObject? Is there another approach to creating a dynamic object that is serializable? Perhaps using a DynamicObject wrapper?

I've created a very simple Windows Forms example to duplicate the error:

using System;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Dynamic;

namespace DynamicTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {            
            dynamic dynamicContext = new ExpandoObject();
            dynamicContext.Greeting = "Hello";

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream("MyFile.bin", FileMode.Create,
                                           FileAccess.Write, FileShare.None);
            formatter.Serialize(stream, dynamicContext);
            stream.Close();
        }
    }
}
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
Michael Levy
  • 13,097
  • 15
  • 66
  • 100
  • 1
    Short of implementing a manual serialisation routine, I'm inclined to say that if it's not marked `Serializable` then, no, quite simply. – Grant Thomas Jan 31 '11 at 16:59

3 Answers3

23

I can't serialize ExpandoObject, but I can manually serialize DynamicObject. So using the TryGetMember/TrySetMember methods of DynamicObject and implementing ISerializable, I can solve my problem which was really to serialize a dynamic object.

I've implemented the following in my simple test app:

using System;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
using System.Dynamic;
using System.Security.Permissions;

namespace DynamicTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {            
            dynamic dynamicContext = new DynamicContext();
            dynamicContext.Greeting = "Hello";
            this.Text = dynamicContext.Greeting;

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);
            formatter.Serialize(stream, dynamicContext);
            stream.Close();
        }
    }

    [Serializable]
    public class DynamicContext : DynamicObject, ISerializable
    {
        private Dictionary<string, object> dynamicContext = new Dictionary<string, object>();

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return (dynamicContext.TryGetValue(binder.Name, out result));
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            dynamicContext.Add(binder.Name, value);
            return true;
        }

        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            foreach (KeyValuePair<string, object> kvp in dynamicContext)
            {
                info.AddValue(kvp.Key, kvp.Value);
            }
        }

        public DynamicContext()
        {
        }

        protected DynamicContext(SerializationInfo info, StreamingContext context)
        {
            // TODO: validate inputs before deserializing. See http://msdn.microsoft.com/en-us/library/ty01x675(VS.80).aspx
            foreach (SerializationEntry entry in info)
            {
                dynamicContext.Add(entry.Name, entry.Value);
            }
        }

    }
}

and Why does SerializationInfo not have TryGetValue methods? had the missing puzzle piece to keep it simple.

Community
  • 1
  • 1
Michael Levy
  • 13,097
  • 15
  • 66
  • 100
  • 3
    Had some doubts about answering my own question, but it seems to be encouraged: http://meta.stackexchange.com/questions/9933/is-there-a-convention-for-accepting-my-own-answer-to-my-own-question – Michael Levy Feb 01 '11 at 04:08
  • will it be possible to do it also for a custom serialization library ? For example versatile Serilization in my case ? – kuldeep May 18 '20 at 14:03
11

ExpandoObject implements IDictionary<string, object>, e.g.:

class Test
{
    static void Main()
    {
        dynamic e = new ExpandoObject();
        e.Name = "Hello";

        IDictionary<string, object> dict = (IDictionary<string, object>)e;

        foreach (var key in dict.Keys)
        {
            Console.WriteLine(key);
        }

        dict.Add("Test", "Something");

        Console.WriteLine(e.Test);

        Console.ReadKey();
    }
}

You could write the contents of the dictionary to a file, and then create a new ExpandoObject through deserialisation, cast it back to a dictionary and write the properties back in?

Matthew Abbott
  • 60,571
  • 9
  • 104
  • 129
  • 1
    If the output has to be in XML then `IDictionary<,>` can't be serialized with [XmlSerializer](http://msdn.microsoft.com/EN-US/library/swxzdhc0.aspx) but it can be serialized with [DataContractSerializer](http://msdn.microsoft.com/EN-US/library/ms405768.aspx) though the output is too verbose. Personally I think JsonFx mentioned in other answer does better job serializing `ExpandoObject`. – Mike Apr 10 '13 at 15:11
9

Maybe a bit late to answer but I use jsonFx to serialize and deserialize expandoObjects and it works very well :

serialization:

dim XMLwriter As New JsonFx.Xml.XmlWriter
dim serializedExpando as string =XMLwriter.Write(obj)

deserialization

dim XMLreader As New JsonFx.Xml.XmlReader
Dim obj As ExpandoObject = XMLreader.Read(Str)
alainb
  • 185
  • 1
  • 6
  • 3
    Beautiful. The JsonWriter just saved me a ton of time! Here is the Git for JsonFx if anyone needs it: https://github.com/jsonfx/jsonfx – Matt Cashatt Nov 14 '11 at 21:46
  • 1
    i tried this. It works perfectly for serializing the dynamic object, but the deserialization shows an exception : Expected Object Property Name or end of object(Object Begin). – sharmakeshav Jun 18 '13 at 08:31
  • Use JsonReader and Writer instead of Xml to fix the above exception :) – fahimalizain Dec 08 '15 at 11:42