我正在检查protobuf-net是否可以替代DataContracts。除了出色的表现外,它还是一个整洁的图书馆。我唯一的问题是.NET序列化程序不做任何假设他们当前正在/序列化。特别是包含对打字对象的引用的对象是一个问题。
[DataMember(Order = 3)]
public object Tag1 // The DataContract did contain a object which becomes now a SimulatedObject
{
get;
set;
}
我试图用协议缓冲区模仿对象,并使用一个小的通用帮助器,它可以在不同的强类型字段中存储每种可能的类型。
这是一种推荐的方法来处理de / serialize到许多不同的不相关类型的字段吗?
以下是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);
}
}
答案 0 :(得分:1)
不,这个解决方案很难长期保持。
我建议您在序列化过程中将序列化类型的全名添加到序列化数据中,并在反序列化过程开始时读取类型名称(无需更改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 });
}
}
答案 1 :(得分:0)
我正在研究类似的东西,我已经提供了第一版的lib: http://bitcare.codeplex.com/
当前版本尚不支持泛型,但我打算在最近的时间添加它。 我只在那里上传了源代码 - 当我准备好使用泛型时我也准备了bin版本......
它假设双方(客户端和服务器)知道他们序列化/反序列化的内容,因此没有任何理由嵌入完整的元数据信息。由于这种序列化结果非常小,并且生成的序列化器工作得非常快。它具有数据字典,使用智能数据存储(仅存储重要位)并在必要时进行最终压缩。如果您需要它,只需尝试解决您的问题。
许可证是GPL,但我会很快将其更改为限制较少的许可证(也可以免费用于商业用途,但与GPL一样,风险自负)
答案 2 :(得分:0)
我上传到codeplex的版本正在使用我的部分产品。当然,它使用不同的单元测试进行测试。它们不会上传到那里,因为我将它移植到vs2012和.net 4.5并决定为传入版本创建新的测试用例集。
我不处理抽象(所谓的打开)泛型。我处理参数化泛型。从数据协定的角度来看,参数化泛型只是专门的类,所以我可以像往常一样处理它们(与其他类一样) - 区别在于对象构造和存储优化。
当我在Nullable&lt;&gt;上存储有关空值的信息时它只占用存储流中的一位,如果它不是空值我根据作为泛型参数提供的类型进行序列化(所以我做了DateTime的序列化,例如可以从一位获取所谓的默认值到几个字节)。目标是根据关于类的数据合同的当前知识生成序列化代码,而不是动态地执行它并浪费内存和处理能力。当我在代码生成期间基于某些泛型看到某个类中的属性时,我知道该泛型的所有属性,并且我知道每个属性的类型:)从这个角度看它是具体的类。
我很快就会更改许可证。我必须首先弄清楚如何做到这一点:),因为我看到可以从提供的许可证类型列表中进行选择,但我无法提供自己的许可证文本。我看到Newtonsoft.Json的许可证也是我的,但我还不知道如何改变它......
尚未提供文档,但简而言之,您可以轻松准备自己的序列化测试。你必须使用你想要存储/序列化有效方式的类型编译程序集,然后在序列化库中创建* .tt文件(比如person类 - 它检查依赖项并为其他依赖类生成代码)并保存文件(当你保存它们然后它生成所有与序列化库合作的代码)。您还可以在构建配置中创建任务,以便在每次构建解决方案时从tt文件重新生成源代码(可能实体框架在构建期间以类似的方式生成代码)。 您现在可以编译序列化库并测量结果的性能和大小。
我的框架需要这个序列化库,以便有效地使用Azure Table和Blob存储的实体,所以我计划很快完成初始版本...