2010-06-16 21 views
21

que intentó serializar una clase DynamicObject con BinaryFormatter, pero:serialización de objetos dinámicos

  • archivo de salida es demasiado grande, no exactamente alambre de usar
  • referencias circulares no se manejan (pegados al serializar)

Desde serializar un DynamicObject significa muy poco a sí mismo, aquí está la clase I intentado serializar:

[Serializable()] 
class Entity 
    : DynamicObject, ISerializable 
{ 

    IDictionary<string, object> values = new Dictionary<string, object>(); 

    public Entity() 
    { 

    } 

    protected Entity(SerializationInfo info, StreamingContext ctx) 
    { 
     string fieldName = string.Empty; 
     object fieldValue = null; 

     foreach (var field in info) 
     { 
      fieldName = field.Name; 
      fieldValue = field.Value; 

      if (string.IsNullOrWhiteSpace(fieldName)) 
       continue; 

      if (fieldValue == null) 
       continue; 

      this.values.Add(fieldName, fieldValue); 
     } 

    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     this.values.TryGetValue(binder.Name, out result); 

     return true; 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     this.values[binder.Name] = value; 

     return true; 
    }   

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 
    {    
     foreach (var kvp in this.values) 
     { 
      info.AddValue(kvp.Key, kvp.Value);     
     } 
    } 

} 

(supongo que podría haber utilizado un ExpandoObject, pero eso es otra historia).

Aquí es un simple programa de pruebas:

static void Main(string[] args) 
    { 
     BinaryFormatter binFmt = new BinaryFormatter(); 

     dynamic obj = new Entity(); 
     dynamic subObj = new Entity(); 
     dynamic obj2 = null; 

     obj.Value = 100; 
     obj.Dictionary = new Dictionary<string, int>() { { "la la la", 1000 } }; 

     subObj.Value = 200; 
     subObj.Name = "SubObject"; 

     obj.Child = subObj; 

     using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate)) 
     { 
      binFmt.Serialize(stream, obj);     
     } 

     using (var stream = new FileStream("test.txt", FileMode.Open)) 
     { 
      try 
      { 
       obj2 = binFmt.Deserialize(stream);      
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex); 
      }     
     } 

     Console.ReadLine(); 

    } 

poner algunos puntos de interrupción aquí y allá me ayudó a tener una mirada en obj2 contenidos y parece que los datos originales se deserializar correctamente, aunque con las limitaciones anteriores si se obtiene imaginativo y mover datos.

He echado un vistazo a la protobuf-net de Marc Gravell, pero no estoy muy seguro de cómo usarla en ese contexto (ni siquiera estoy seguro de haber elegido la versión correcta del repositorio, pero bueno) .

sé que es más código que las palabras, pero no creo que pueda explicar el escenario mejor. Dígame si hay algo que pueda agregar para aclarar esta pregunta.

Cualquier ayuda es muy apreciada.

+0

Como referencia, * * protobuf-net no tiene soporte para 'dinámico'. Sugeriría pasar a una capa DTO para la serialización. –

+0

@Marc - Gracias, investigaré eso. Todavía abierto a otras sugerencias también. – Raine

+0

bueno, a largo plazo es algo que pienso apoyar en protobuf-net. Pero no puedo prometer nada en este momento. –

Respuesta

12

Estoy 98% seguro de que esta secuencia funcionará para un objeto dinámico.

  1. convert objeto a un objeto Expando
  2. fundido objeto expando a ser de diccionario tipo
  3. Serializer.Serialize
  4. uso protobuf-net/.Deserialize como por diccionario normal de
  5. convertido al expando Objeto

Puede convertir objetos en una colección de pares de nombre/valor para transferir.

Eso es solo un pequeño subconjunto de lo que la dinámica puede hacer, pero quizás es suficiente para usted.

Hay algún código personalizado para manejar algunas de las conversiones anteriores que puedo mostrar si hay interés.

No tengo una solución para cuando dynamic es un marcador de posición para una clase. Para este caso, sugiero obtener el tipo y usar una instrucción switch para serializar/deserializar según lo requiera. Para este último caso, necesitaría colocar algo para indicar qué tipo de deserialización genérica necesita (string/id/nombre completo del tipo/etc). La suposición es que estás tratando con una lista de tipos esperados.

Nota: Expando implementa IDictionary. Un Expando es simplemente una mera lista de pares clave/valor. es decir. lo que sobresale es la clave, y el valor es el retorno de cualquier cadena de funciones que implemente eso. Hay un conjunto de interfaces dinámicas para personalizar la experiencia del azúcar sintáctico, pero la mayoría de las veces no las mira.

árbitros:

+0

Sí, ¡algún código sería realmente bueno! – ErikE

+0

@ErikE: Debería haber hecho un ejemplo en ese momento. He puesto algunos enlaces de referencia en la respuesta para ti. Espero que esos ayuden. – sgtz

+0

Esto no funciona porque ProtoBuf-net no serializará valores de tipo objeto. El Diccionario subyacente es del tipo 'IDictionary '. – ChrisW

9

No estoy seguro de si JSON sería aceptable en su servidor, pero si lo es, he usado Json.net (http://json.codeplex.com) para serializar un tipo dinámico. Funciona bastante bien, es rápido y la salida es pequeña. Mientras Json.net no vuelve directamente objetos dinámicos, es muy fácil de convertir la salida de deserializado Json.Net a cualquier tipo dinámico. En el siguiente ejemplo, estoy usando ExpandoObject como mi tipo dinámico. El siguiente código es lo que he usado en Facebook Graph Toolkit. Vea este enlace para la fuente original: http://facebookgraphtoolkit.codeplex.com/SourceControl/changeset/view/48442#904504

public static dynamic Convert(string s) { 
      object obj = Newtonsoft.Json.JsonConvert.DeserializeObject(s); 
      if (obj is string) { 
       return obj as string; 
      } else { 
       return ConvertJson((JToken)obj); 
      } 
    } 

    private static dynamic ConvertJson(JToken token) { 
     // FROM : http://blog.petegoo.com/archive/2009/10/27/using-json.net-to-eval-json-into-a-dynamic-variable-in.aspx 
     // Ideally in the future Json.Net will support dynamic and this can be eliminated. 
     if (token is JValue) { 
      return ((JValue)token).Value; 
     } else if (token is JObject) { 
      ExpandoObject expando = new ExpandoObject(); 
      (from childToken in ((JToken)token) where childToken is JProperty select childToken as JProperty).ToList().ForEach(property => { 
       ((IDictionary<string, object>)expando).Add(property.Name, ConvertJson(property.Value)); 
      }); 
      return expando; 
     } else if (token is JArray) { 
      List<ExpandoObject> items = new List<ExpandoObject>(); 
      foreach (JToken arrayItem in ((JArray)token)) { 
       items.Add(ConvertJson(arrayItem)); 
      } 
      return items; 
     } 
     throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token"); 
    } 
+0

gracias, interesante. Voy a echar un vistazo lo antes posible. Mi única preocupación es que (supuestamente) no será tan eficiente como la serialización binaria. – Raine

+2

Después de probar algunas soluciones diferentes a mis problemas, su código era el código que hacía lo que yo necesitaba. Entonces, aunque su respuesta no haya ayudado tanto al OP como a mí, ¡aún aprecio encontrarlo! :) –

1

En primer lugar, el tamaño de su archivo depende de 2 cosas (si entiendo cómo funciona BinaryFormatter, por favor, corríjanme si me equivoco):

  1. El tamaño de los valores reales de ser en serie ized, y
  2. Los nombres utilizados para serializar los valores del objeto con el método SerializationInfo.AddValue, que se almacenan en el archivo de salida para valores se pueden utilizar durante la deserialización con el mismo nombre.

Obviamente, el # 1 va a causar su mayor desaceleración, que solo se puede reducir optimizando los objetos que intenta serializar.

Debido a que está utilizando objetos dinámicos, el aumento de tamaño casi imperceptiblemente pequeño causado por el n. ° 2 es inevitable. Si conocía los tipos y nombres de los miembros del objeto antes de tiempo, podría dar a cada miembro del objeto un nombre muy corto, determinado secuencialmente ("1", "2", "3", etc.) a medida que iteraba sobre los miembros del objeto, agregándolos a través de SerializationInfo.AddValue. Luego, durante la deserialización, podría usar SerializationInfo.GetValue con el mismo nombre determinado secuencialmente, y la deserialización funcionaría bien, independientemente de los nombres reales de los valores que se deserializaron, siempre que iterara a través de los miembros del objeto en el mismo orden en el que estaban agregado. De acuerdo, esto solo podría ahorrarle un promedio de 4 o 5 bytes por miembro, pero esas pequeñas cantidades pueden acumularse en objetos grandes.

@Raine: (. Creo que podría haber utilizado un ExpandoObject, pero eso es otra historia)

No

modo; Cambié tu muestra de código para usar ExpandoObject en lugar de tu clase Entity, y obtuve un SerializationException arrojándome. ExpandoObject no está marcado con un SerializableAttribute, y no tiene los constructores apropiados para deserializar o serializar. Sin embargo, esto no significa que usted no puede usar ExpandoObject si realmente lo desea: implementa IDictionary<string, object>, que a su vez implementa ICollection<KeyValuePair<string, object>>. Por lo tanto, una instancia ExpandoObject es una colección de instancias KeyValuePair<string, object>, que son marcadas como serializables. Entonces, podría serializar un ExpandoObject, pero tendría que convertirlo como ICollection<KeyValuePair<string, object>> y serializar cada KeyValuePair<string, object> en él individualmente. Sin embargo, esto no tendría sentido en términos de optimización de la muestra de código original, ya que ocupa el mismo espacio de archivo.

En resumen, realmente no creo que haya ninguna manera de optimizar la serialización de un objeto dinámico; hay que recorrer los miembros del objeto cada vez que se serializa, y no hay manera de conocer el tamaño del objeto de antemano definición de dinámica).

Cuestiones relacionadas