动态对象序列化

我尝试使用BinaryFormatter序列化DynamicObject类,但是:

  • 输出文件太大,不完全是导线友好的
  • 未处理循环引用(序列化时卡住)

由于序列化DynamicObject本身意味着很少,这里是我尝试序列化的类:

 [Serializable()] class Entity : DynamicObject, ISerializable { IDictionary values = new Dictionary(); 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); } } } 

(我想我可以使用ExpandoObject,但这是另一个故事。)

这是一个简单的测试程序:

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

在这里放一些断点帮助我查看obj2内容,看起来原始数据被正确反序列化,但如果你有想象力并移动数据,就会出现上述缺点。

我看了一下Marc Gravell的protobuf-net,但我不确定如何在这样的环境中使用它(我甚至不确定我从存储库中选择了正确的版本,但是嘿)。

我知道它的代码多于单词,但我认为我不能更好地解释这种情况。 请告诉我是否可以添加一些内容以使此问题更清晰。

任何帮助深表感谢。

我98%确定这个序列适用于动态对象。

  1. 将对象转换为Expando对象
  2. 将expando对象转换为Dictionary类型
  3. 按照正常使用ProtoBuf-net Serializer.Serialize / .Deserialize
  4. 将字典转换为Expando对象

您可以将对象转换为名称/值对的集合以进行传输。

这只是动态可以做的一小部分,但也许对你来说已经足够了。

有一些自定义代码可以处理上面的一些转换,如果有兴趣我可以告诉你。

我没有动态是一个占位符的类的解决方案。 对于这种情况,我建议获取类型并使用switch语句根据需要进行序列化/反序列化。 对于最后一种情况,您需要放置一些东西来指示您需要哪种类型的通用反序列化(字符串/ id /完全限定类型名称/等)。 假设您正在处理预期类型的​​列表。

注意:Expando实现了IDictionary。 Expando仅仅是键/值对的列表。 即。 你所涉及的是关键,而值是从任何函数链实现的回报。 有一组用于自定义语法糖体验的动态界面,但大多数时候你不会看到它们。

裁判:

我不确定JSON在您的Senario中是否可以接受,但如果是,我使用Json.net( http://json.codeplex.com )来序列化动态类型。 它运行良好,速度快,输出尺寸小。 虽然Json.net不直接返回动态对象,但很容易将Json.Net的反序列化输出转换为任何动态类型。 在下面的示例中,我使用ExpandoObject作为我的动态类型。 下面的代码是我在Facebook Graph Toolkit中使用的代码。 请参阅此链接以获取原始来源: 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)expando).Add(property.Name, ConvertJson(property.Value)); }); return expando; } else if (token is JArray) { List items = new List(); foreach (JToken arrayItem in ((JArray)token)) { items.Add(ConvertJson(arrayItem)); } return items; } throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token"); } 

首先,你的文件大小取决于2件事(如果我理解BinaryFormatter如何工作,如果我错了请纠正我):

  1. 要序列化的实际值的大小,以及
  2. 用于使用SerializationInfo.AddValue方法序列化对象值的名称,这些名称存储在输出文件中,因此可以在反序列化期间使用相同名称的值。

显然,#1将导致您最大的减速,这只能通过优化您尝试序列化的对象来减少。

因为你正在使用动态对象,所以由#2引起的几乎不可察觉的小尺寸增加是不可避免的。 如果您提前了解对象成员的类型和名称,则可以在迭代时为对象的每个成员提供非常短的,按顺序确定的名称(“1”,“2”,“3”等)在对象的成员上,通过SerializationInfo.AddValue添加它们。 然后,在反SerializationInfo.GetValue期间,您可以使用具有相同顺序确定的名称的SerializationInfo.GetValue ,并且反序列化将正常工作,无论要反序列化的值的实际名称如何,只要您以相同的顺序迭代对象的成员它们被添加进去。当然,这可能只会为每个成员平均节省4或5个字节,但这些少量可以在大对象中加起来。

@Raine :(我想我可以使用ExpandoObject,但这是另一个故事。)

不是这样; 我更改了您的代码示例以使用ExpandoObject而不是您的Entity类,并且向我抛出了SerializationExceptionExpandoObject未标记SerializableAttribute ,并且没有要反序列化或序列化的相应构造函数。 但是,这并不意味着你不能使用ExpandoObject,如果你真的想:它实现IDictionary ,它反过来实现ICollection> 。 因此, ExpandoObject实例是KeyValuePair实例的集合,它们标记为可序列化。 因此,您可以序列化ExpandoObject,但是您必须将其转换为ICollection>并单独序列化其中的每个KeyValuePair 。 但是,就优化原始代码示例而言,这将毫无意义,因为它需要同样多的文件空间。

总而言之,我真的不认为有任何方法可以优化序列化动态对象 – 每次序列化时都必须遍历对象的成员,并且您无法预先知道对象的大小(通过动态定义) 。

我不知道SharpSerializer是否支持动态对象但是值得一试:

http://www.sharpserializer.com/en/index.html