2011-01-31 19 views
27

Estoy tratando de usar un System.Dynamic.ExpandoObject para poder crear dinámicamente propiedades en tiempo de ejecución. Más tarde, necesito pasar una instancia de este objeto y el mecanismo utilizado requiere serialización.¿Puedo serializar un ExpandoObject en .NET 4?

Por supuesto, cuando intento para serializar mi objeto dinámico, consigo la excepción:

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.

¿Puedo serializar el ExpandoObject? ¿Hay algún otro enfoque para crear un objeto dinámico que sea serializable? Tal vez usando un contenedor DynamicObject?

He creado un muy simple de Windows Forms ejemplo para duplicar el 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(); 
     } 
    } 
} 
+1

corto de implementar un manual de rutina de serialización, me inclino a decir que si no está marcado como 'Serializable', entonces, no, simplemente. –

Respuesta

20

No puedo serializar ExpandoObject, pero puedo serializar manualmente DynamicObject. Entonces, usando los métodos TryGetMember/TrySetMember de DynamicObject e implementando ISerializable, puedo resolver mi problema, que era realmente serializar un objeto dinámico.

He implementado el siguiente en mi aplicación sencilla prueba:

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); 
      } 
     } 

    } 
} 

y Why does SerializationInfo not have TryGetValue methods? tenían la pieza del rompecabezas que falta para que sea sencillo.

+3

Tuve algunas dudas sobre la respuesta a mi propia pregunta, pero parece ser alentada: http://meta.stackexchange.com/questions/9933/is-there-a-convention-for-accepting-my-own-withour-to -mi-propia-pregunta –

9

ExpandoObject implementa IDictionary<string, object>, por ejemplo:

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(); 
    } 
} 

Se puede escribir el contenido del diccionario en un archivo , y luego crear un nuevo ExpandoObject a través de deserialización, devolverlo a un diccionario y volver a escribir las propiedades?

+0

Si la salida tiene que estar en XML, 'IDictionary <,>' no se puede serializar con [XmlSerializer] (http://msdn.microsoft.com/EN-US/library/swxzdhc0.aspx) pero se puede serializar con [DataContractSerializer] (http://msdn.microsoft.com/EN-US/library/ms405768.aspx) aunque el resultado es demasiado detallado. Personalmente creo que JsonFx mencionado en otra respuesta hace mejor trabajo serializando 'ExpandoObject'. – Mike

8

Tal vez un poco tarde para responder pero lo uso jsonFx para serializar y deserializar expandoObjects y funciona muy bien:

serialización:

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

deserialización

dim XMLreader As New JsonFx.Xml.XmlReader 
Dim obj As ExpandoObject = XMLreader.Read(Str) 
+3

Hermosa. ¡El JsonWriter me ha ahorrado un montón de tiempo! Aquí está el Git para JsonFx si alguien lo necesita: https://github.com/jsonfx/jsonfx –

+1

intenté esto. Funciona perfectamente para serializar el objeto dinámico, pero la deserialización muestra una excepción: Nombre de propiedad del objeto esperado o fin del objeto (Object Begin). – sharmakeshav

+0

Utilice JsonReader y Writer en lugar de Xml para corregir la excepción anterior :) – faztp12

Cuestiones relacionadas