Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I serialize an ExpandoObject in .NET 4?

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();         }     } } 
like image 597
Michael Levy Avatar asked Jan 31 '11 16:01

Michael Levy


1 Answers

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.

like image 62
Michael Levy Avatar answered Oct 14 '22 05:10

Michael Levy