c# - lista - Serialización dinámica de objetos
objeto de objetos c# (4)
Estoy 98% seguro de que esta secuencia funcionará para un objeto dinámico.
- convertir objeto a un objeto Expando
- lanzar objeto expando para que sea de tipo Diccionario
- use ProtoBuf-net Serializer.Serialize / .Deserialize como de costumbre
- convertir diccionario al objeto Expando
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 ti.
Hay un 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.
refs:
- Diccionario de IDictionary usando el constructor - http://msdn.microsoft.com/en-us/library/et0ke8sz(v=vs.110).aspx
- IDictionary / Dictionary to Expando - http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-extension-method/
Traté de serializar una clase BinaryFormatter
con BinaryFormatter
, pero:
- El archivo de salida es demasiado grande, no exactamente compatible con cables
- Referencias circulares no manejadas (bloqueadas durante la serialización)
Como serializar un DynamicObject
significa muy poco por sí mismo, esta es la clase que traté de 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 usado un ExpandoObject, pero esa es otra historia).
Aquí hay un programa de prueba simple:
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 echar un vistazo a los contenidos de obj2 y parece que los datos originales están correctamente deserializados, aunque con las deficiencias anteriores si se vuelve imaginativo y mueve datos.
Eché un vistazo a la protobuf-net de Marc Gravell, pero no estoy 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 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.
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. Aunque Json.net no devuelve objetos dinámicos directamente, es muy fácil convertir la salida deserializada de 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");
}
No sé si SharpSerializer admite Dynamic Objects, pero podría valer la pena intentarlo:
Primero, el tamaño del archivo depende de 2 cosas (si entiendo cómo funciona BinaryFormatter, corrígeme si me equivoco):
- El tamaño de los valores reales que se serializan, y
- Los nombres utilizados para serializar los valores del objeto con el método
SerializationInfo.AddValue
, que se almacenan en el archivo de salida para que los valores puedan utilizarse 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 imperceptible causado por el # 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 perfectamente, independientemente de los nombres reales de los valores que se están deserializando, siempre que itere a través de los miembros del objeto en el mismo orden. se agregaron. 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 usado un ExpandoObject, pero esa es otra historia).
No tan; Cambié tu ejemplo de código para usar ExpandoObject
lugar de tu clase Entity
, y obtuve una SerializationException
. ExpandoObject
no está marcado con un SerializableAttribute
, y no tiene los constructores apropiados para ser deserializado o serializado. Sin embargo, esto no significa que no pueda usar ExpandoObject si realmente lo desea: implementa IDictionary<string, object>
, que a su vez implementa ICollection<KeyValuePair<string, object>>
. Por lo tanto, una instancia de ExpandoObject
es una colección de KeyValuePair<string, object>
de KeyValuePair<string, object>
, que están marcadas como serializables. Entonces, podría serializar un ExpandoObject, pero tendría que convertirlo en 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 forma 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 saber el tamaño del objeto de antemano (por definición de dinámico) .