2011-01-31 11 views
27

Próbuję użyć System.Dynamic.ExpandoObject, dzięki czemu mogę dynamicznie tworzyć właściwości w czasie wykonywania. Później muszę przekazać instancję tego obiektu, a zastosowany mechanizm wymaga serializacji.Czy można szeregować ExpandoObject w .NET 4?

Oczywiście, gdy próbuję serializować mój obiekt dynamiczny, otrzymuję wyjątek:

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.

Mogę serializacji ExpandoObject? Czy istnieje inne podejście do tworzenia dynamicznego obiektu, który można serializować? Być może używając opakowania DynamicObject?

Utworzyłem przykład bardzo prosty Windows Forms powielić błąd:

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

Krótki wdrażania podręcznika rutyna serializacji, jestem skłonny powiedzieć, że jeśli nie jest oznaczona jako "Serializable", nie, po prostu. –

Odpowiedz

20

Nie mogę serializować ExpandoObject, ale mogę ręcznie serializować DynamicObject. Tak więc używając metod DynamicObject i implementacji ISerializable TryGetMember/TrySetMember, mogę rozwiązać mój problem polegający na serializacji dynamicznego obiektu.

I zostały wdrożone następujące w moim prostych aplikacji testowej:

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

    } 
} 

i Why does SerializationInfo not have TryGetValue methods? miał brakujący kawałek układanki keep it simple.

+3

Miałem wątpliwości dotyczące odpowiedzi na moje własne pytanie, ale wydaje się, że jest to zachęcające: http://meta.stackexchange.com/questions/9933/is-there-a-convention-for-accepting-my-own-answer-to -my-own-question –

9

ExpandoObject implementuje IDictionary<string, object>, np

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

można napisać zawartość słownika do pliku , a następnie utworzyć nowy ExpandoObject poprzez deserialisation, przesłać go z powrotem do słownika i ponownie zapisać właściwości?

+0

Jeśli dane wyjściowe muszą być w formacie XML, to 'IDictionary <,>' nie może być serializowane za pomocą [XmlSerializer] (http://msdn.microsoft.com/PL/library/swxzdhc0.aspx), ale może być serializowane [DataContractSerializer] (http://msdn.microsoft.com/EN-US/library/ms405768.aspx), chociaż wynik jest zbyt szczegółowy. Osobiście uważam, że JsonFx, o którym mowa w innej odpowiedzi, lepiej nadaje się do serializacji 'ExpandoObject'. – Mike

8

Może trochę późno, by odpowiedzieć, ale używam jsonFx do serializacji i deserializacji expandoObjects i działa bardzo dobrze:

serializacji:

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

deserializacji

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

Piękny. JsonWriter właśnie zaoszczędził mi mnóstwo czasu! Oto Git dla JsonFx, jeśli ktoś tego potrzebuje: https://github.com/jsonfx/jsonfx –

+1

Próbowałem tego. Działa idealnie do serializacji obiektu dynamicznego, ale deserializacja pokazuje wyjątek: Oczekiwana nazwa właściwości obiektu lub koniec obiektu (Object Begin). – sharmakeshav

+0

Użyj JsonReader i Writer zamiast Xml, aby naprawić powyższy wyjątek :) – faztp12