[英]protobuf-net and de/serializing objects
我正在检查protobuf-net是否可以代替DataContracts。 除了出色的性能外,它实际上是一个整洁的库。 我唯一的问题是.NET序列化程序不作任何假设,即它们当前正在反序列化/序列化。 特别是确实包含对类型化对象的引用的对象是一个问题。
[DataMember(Order = 3)]
public object Tag1 // The DataContract did contain a object which becomes now a SimulatedObject
{
get;
set;
}
我尝试使用带有少许通用帮助程序的协议缓冲区来模拟对象,该帮助程序确实将每种可能的类型存储在不同的强类型字段中。
是否建议使用这种方法来处理将字段反序列化为许多不同的不相关类型的字段?
下面是SimulatedObject的示例代码,该代码最多可以容纳10种不同的类型。
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using ProtoBuf;
using System.Diagnostics;
[DataContract]
public class SimulatedObject<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>
{
[DataMember(Order = 20)]
byte FieldHasValue; // the number indicates which field actually has a value
[DataMember(Order = 1)]
T1 I1;
[DataMember(Order = 2)]
T2 I2;
[DataMember(Order = 3)]
T3 I3;
[DataMember(Order = 4)]
T4 I4;
[DataMember(Order = 5)]
T5 I5;
[DataMember(Order = 6)]
T6 I6;
[DataMember(Order = 7)]
T7 I7;
[DataMember(Order = 8)]
T8 I8;
[DataMember(Order = 9)]
T9 I9;
[DataMember(Order = 10)]
T10 I10;
public object Data
{
get
{
switch(FieldHasValue)
{
case 0: return null;
case 1: return I1;
case 2: return I2;
case 3: return I3;
case 4: return I4;
case 5: return I5;
case 6: return I6;
case 7: return I7;
case 8: return I8;
case 9: return I9;
case 10: return I10;
default:
throw new NotSupportedException(String.Format("The FieldHasValue field has an invlaid value {0}. This indicates corrupt data or incompatible data layout chagnes", FieldHasValue));
}
}
set
{
I1 = default(T1);
I2 = default(T2);
I3 = default(T3);
I4 = default(T4);
I5 = default(T5);
I6 = default(T6);
I7 = default(T7);
I8 = default(T8);
I9 = default(T9);
I10 = default(T10);
if (value != null)
{
Type t = value.GetType();
if (t == typeof(T1))
{
FieldHasValue = 1;
I1 = (T1) value;
}
else if (t == typeof(T2))
{
FieldHasValue = 2;
I2 = (T2) value;
}
else if (t == typeof(T3))
{
FieldHasValue = 3;
I3 = (T3) value;
}
else if (t == typeof(T4))
{
FieldHasValue = 4;
I4 = (T4) value;
}
else if (t == typeof(T5))
{
FieldHasValue = 5;
I5 = (T5) value;
}
else if (t == typeof(T6))
{
FieldHasValue = 6;
I6 = (T6) value;
}
else if (t == typeof(T7))
{
FieldHasValue = 7;
I7 = (T7) value;
}
else if (t == typeof(T8))
{
FieldHasValue = 8;
I8 = (T8) value;
}
else if (t == typeof(T9))
{
FieldHasValue = 9;
I9 = (T9) value;
}
else if (t == typeof(T10))
{
FieldHasValue = 10;
I10 = (T10) value;
}
else
{
throw new NotSupportedException(String.Format("The type {0} is not supported for serialization. Please add the type to the SimulatedObject generic argument list.", t.FullName));
}
}
}
}
}
[DataContract]
class Customer
{
/*
[DataMember(Order = 3)]
public object Tag1 // The DataContract did contain a object which becomes now a SimulatedObject
{
get;
set;
}
*/
[DataMember(Order = 3)]
public SimulatedObject<bool, Other, Other, Other, Other, Other, Other, Other, Other, SomethingDifferent> Tag1 // Can contain up to 10 different types
{
get;
set;
}
[DataMember(Order = 4)]
public List<string> Strings
{
get;
set;
}
}
[DataContract]
public class Other
{
[DataMember(Order = 1)]
public string OtherData
{
get;
set;
}
}
[DataContract]
public class SomethingDifferent
{
[DataMember(Order = 1)]
public string OtherData
{
get;
set;
}
}
class Program
{
static void Main(string[] args)
{
Customer c = new Customer
{
Strings = new List<string> { "First", "Second", "Third" },
Tag1 = new SimulatedObject<bool, Other, Other, Other, Other, Other, Other, Other, Other, SomethingDifferent>
{
Data = new Other { OtherData = "String value "}
}
};
const int Runs = 1000 * 1000;
var stream = new MemoryStream();
var sw = Stopwatch.StartNew();
Serializer.Serialize<Customer>(stream, c);
sw = Stopwatch.StartNew();
for (int i = 0; i < Runs; i++)
{
stream.Position = 0;
stream.SetLength(0);
Serializer.Serialize<Customer>(stream, c);
}
sw.Stop();
Console.WriteLine("Data Size with Protocol buffer Serializer: {0}, {1} objects did take {2}s", stream.ToArray().Length, Runs, sw.Elapsed.TotalSeconds);
stream.Position = 0;
var newCustw = Serializer.Deserialize<Customer>(stream);
sw = Stopwatch.StartNew();
for (int i = 0; i < Runs; i++)
{
stream.Position = 0;
var newCust = Serializer.Deserialize<Customer>(stream);
}
sw.Stop();
Console.WriteLine("Read object with Protocol buffer deserializer: {0} objects did take {1}s", Runs, sw.Elapsed.TotalSeconds);
}
}
不,这种解决方案很难长期维持。
我建议您在序列化过程中在序列化数据之前添加序列化类型的全名,并在反序列化过程开始时读取类型名称(无需更改protobuf源代码)
附带说明,您应尽量避免在反序列化过程中混合对象类型。 我假设您正在更新现有的.net应用程序,并且无法对其进行重新设计。
更新:示例代码
public byte[] Serialize(object myObject)
{
using (var ms = new MemoryStream())
{
Type type = myObject.GetType();
var id = System.Text.ASCIIEncoding.ASCII.GetBytes(type.FullName + '|');
ms.Write(id, 0, id.Length);
Serializer.Serialize(ms, myObject);
var bytes = ms.ToArray();
return bytes;
}
}
public object Deserialize(byte[] serializedData)
{
StringBuilder sb = new StringBuilder();
using (var ms = new MemoryStream(serializedData))
{
while (true)
{
var currentChar = (char)ms.ReadByte();
if (currentChar == '|')
{
break;
}
sb.Append(currentChar);
}
string typeName = sb.ToString();
// assuming that the calling assembly contains the desired type.
// You can include aditional assembly information if necessary
Type deserializationType = Assembly.GetCallingAssembly().GetType(typeName);
MethodInfo mi = typeof(Serializer).GetMethod("Deserialize");
MethodInfo genericMethod = mi.MakeGenericMethod(new[] { deserializationType });
return genericMethod.Invoke(null, new[] { ms });
}
}
我现在正在从事类似的工作,并且已经提供了该库的第一个版本: http : //bitcare.codeplex.com/
当前版本尚不支持泛型,但我计划在最近的时间添加它。 我只在那上传源代码-当我准备好使用泛型时,我还要准备bin版本...
它假设双方(客户端和服务器)都知道他们要进行序列化/反序列化的内容,因此没有任何理由在其中嵌入完整的元数据信息。 由于此序列化的结果非常小,因此生成的序列化器的运行速度非常快。 它具有数据字典,使用智能数据存储(仅存储简短的重要位),并在必要时进行最终压缩。 如果需要,请尝试解决问题。
该许可证是GPL,但我会尽快将其更改为限制性较小的许可证(也可免费用于商业用途,但风险自负,如GPL)
我上传到Codeplex的版本正在与我的某些产品配合使用。 当然,它已通过不同的单元测试集进行了测试。 它们没有上传到那里,因为我将其移植到vs2012和.net 4.5并决定为即将发布的版本创建新的测试用例集。
我不处理抽象(所谓的打开的)泛型。 我处理参数化的泛型。 从数据协定的角度来看,参数化的泛型只是专门的类,因此我可以像其他类一样照常处理它们-区别仅在于对象构造和存储优化。
当我将有关null值的信息存储在Nullable <>上时,它仅占用存储流中的一位,如果不是null值,则根据作为泛型参数提供的类型进行序列化(因此,我对DateTime进行序列化,例如可以从一位获取所谓的默认值是几个字节)。 目的是根据有关类上数据协定的当前知识来生成序列化代码,而不是动态地进行序列化并浪费内存和处理能力。 当我在代码生成过程中看到某个基于某个泛型的类中的属性时,我知道该泛型的所有属性,并且知道每个属性的类型:)从这个角度来看,它是具体的类。
我将尽快更改许可证。 我必须首先弄清楚该如何做:),因为我看到可以从提供的许可证类型列表中进行选择,但是我不能提供自己的许可证文本。 我看到了Newtonsoft的许可证.Json也是我想要的,但我还不知道如何更改它...
尚未提供文档,但是总而言之,准备自己的序列化测试很容易。 您必须使用要存储/序列化有效方式的类型来编译程序集,然后在序列化库中创建* .tt文件(例如,对于人员类,它会检查依赖项并为其他依赖类生成代码)并保存文件(当您保存它们时,它会生成用于与序列化库合作的所有代码。 您还可以在构建配置中创建任务,以在每次构建解决方案时从tt文件重新生成源代码(可能是Entity Framework在构建过程中以类似方式生成代码)。 您现在可以编译序列化库,并测量结果的性能和大小。
我需要为我的框架使用该序列化库,以有效地将实体与Azure Table和Blob存储结合使用,因此我计划尽快完成初始发行版...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.