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(); } } }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With