J'ai essayé de sérialiser une classe DynamicObject
avec BinaryFormatter
, mais:
Puisque la sérialisation d'une DynamicObject
signifie très peu par lui-même, voici la classe que j'ai essayé de sérialiser:
[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);
}
}
}
(J'imagine que j'aurais pu utiliser un ExpandoObject, mais c'est une autre histoire.)
Voici un programme de test 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();
}
Mettre quelques points d'arrêt ici et là m'a aidé à jeter un coup d'œil au contenu de obj2 et il semble que les données d'origine soient correctement désérialisées, mais avec les inconvénients ci-dessus si vous faites preuve d'imagination et déplacez les données.
J'ai jeté un coup d'œil au protobuf-net de Marc Gravell, mais je ne sais pas trop comment l'utiliser dans un tel contexte (je ne suis même pas sûr d'avoir pris la bonne version du référentiel, mais bon).
Je sais que c'est plus du code que des mots, mais je ne pense pas pouvoir expliquer le scénario mieux. S'il vous plaît dites-moi s'il y a quelque chose que je peux ajouter pour clarifier cette question.
Toute aide est très appréciée.
Je suis 98% certain que cette séquence fonctionnera pour un objet dynamique.
Vous pouvez convertir des objets en une collection de paires nom/valeur à transférer.
Ce n'est qu'un petit sous-ensemble de ce que la dynamique peut faire, mais peut-être que cela vous suffit.
Il existe un code personnalisé pour gérer certaines des conversions ci-dessus que je peux vous montrer si cela vous intéresse.
Je n'ai pas de solution pour quand dynamique est un espace réservé à une classe. Dans ce cas, je suggérerais d’obtenir le type et d’utiliser une instruction switch pour sérialiser/désérialiser selon vos besoins. Dans ce dernier cas, vous devez placer un objet pour indiquer le type de désérialisation générique dont vous avez besoin (chaîne/id/nom de type complet/etc). L’hypothèse est que vous avez affaire à une liste de types attendus.
Remarque: Expando implémente IDictionary. Un Expando est simplement une liste de paires clé/valeur. c'est à dire. la chose dans laquelle vous pointez est la clé, et la valeur est le retour de la chaîne de fonctions implémentée. Il existe un ensemble d'interfaces dynamiques permettant de personnaliser l'expérience du sucre syntaxique, mais la plupart du temps, vous n'aurez pas à les examiner.
réfs:
Je ne sais pas si JSON serait acceptable dans votre scénario, mais si c'est le cas, j'ai utilisé Json.net ( http://json.codeplex.com ) pour sérialiser un type dynamique. Cela fonctionne assez bien, il est rapide et la sortie est de petite taille. Bien que Json.net ne renvoie pas directement d’objets dynamiques, il est très facile de convertir la sortie désérialisée de Json.Net en n’importe quel type dynamique. Dans l'exemple ci-dessous, j'utilise ExpandoObject comme type dynamique. Le code ci-dessous est ce que j'ai utilisé dans la boîte à outils Graph Facebook. Voir le lien de la source originale: 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");
}
Tout d’abord, la taille de votre fichier dépend de 2 choses (si je comprends comment fonctionne BinaryFormatter, corrigez-moi si je me trompe):
SerializationInfo.AddValue
, qui sont stockés dans le fichier de sortie afin que les valeurs puissent être utilisées lors de la désérialisation sous le même nom.De toute évidence, le n ° 1 va provoquer votre plus grand ralentissement, qui ne peut être réduit qu'en optimisant les objets que vous essayez de sérialiser.
Parce que vous utilisez des objets dynamiques, l’augmentation de taille presque imperceptible causée par le n ° 2 est inévitable. Si vous connaissez les types et les noms des membres de l'objet à l'avance, vous pouvez simplement attribuer à chaque membre de l'objet un nom très court, déterminé séquentiellement ("1", "2", "3", etc.) lors de votre itération. sur les membres de l'objet, en les ajoutant via SerializationInfo.AddValue
. Ensuite, lors de la désérialisation, vous pouvez utiliser SerializationInfo.GetValue
avec le même nom déterminé séquentiellement, et la désérialisation fonctionnerait parfaitement, quels que soient les noms réels des valeurs désérialisées, tant que vous parcourez les membres de l'objet dans le même ordre. Cela peut vous faire économiser en moyenne 4 ou 5 octets par membre, mais ces petites quantités peuvent s’additionner dans des objets volumineux.
@Raine: (j'imagine que j'aurais pu utiliser un ExpandoObject, mais c'est une autre histoire.)
Pas si; J'ai modifié votre exemple de code afin qu'il utilise ExpandoObject
à la place de votre classe Entity
et j'ai lancé une SerializationException
. ExpandoObject
n'est pas marqué avec une SerializableAttribute
et il n'a pas les constructeurs appropriés pour être désérialisé ou sérialisé. Cependant, cela ne signifie pas que vous ne pouvez pas utilisez ExpandoObject si vous le souhaitez vraiment: il implémente IDictionary<string, object>
, qui à son tour implémente ICollection<KeyValuePair<string, object>>
. Ainsi, une instance ExpandoObject
est une collection d'instances KeyValuePair<string, object>
, qui sont marquées comme sérialisables. Vous pouvez donc sérialiser un ExpandoObject, mais vous devez le convertir en ICollection<KeyValuePair<string, object>>
et sérialiser chaque KeyValuePair<string, object>
individuellement. Cela n’a aucun intérêt, cependant, en termes d’optimisation de votre exemple de code original, car il prend tout autant d’espace fichier.
En résumé, je ne pense vraiment pas qu'il soit possible d'optimiser la sérialisation d'un objet dynamique; vous devez parcourir les membres de l'objet à chaque fois qu'il est sérialisé et vous n'avez aucun moyen de connaître la taille de l'objet à l'avance (par définition de dynamique). .
Je ne sais pas si SharpSerializer prend en charge les objets dynamiques, mais cela vaut la peine d'essayer: