2012-11-05 53 views
0

我正在检查protobuf-net是否可以代替DataContracts。除了出色的表现,它真的是一个整洁的图书馆。我唯一的问题是,.NET序列化程序不会假定它们当前正在进行的序列化。特别是包含对被打印对象的引用的对象是一个问题。protobuf-net和de /序列化对象

[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); 

    } 
} 

回答

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 }); 
    } 
} 
+0

我没有改变protobuf的源代码。 SimulatedObject可以放置在DataContract所在的位置。唯一的区别是你需要为SimulateObject中的对象指定所有预期的类型作为通用约束。是的,我正试图将现有的大型代码库转换为协议缓冲区。通常我不会需要10种不同的类型,但可能需要两三种。我想检查我的测试最糟糕的情况。 –

+0

当您序列化数据时,您可能会打开一个Streamwriter。只需将类型FullName(typeof().FullName)和分隔符(即:“|”)发送到发送序列化数据的写入器即可。 读取数据时,请先阅读第一个字节,直到找到您选择的分隔符。现在你已经是序列化类型,并且可以反序列化你的数据,而且还有其他复杂的问题。 – lstern

+0

使用协议缓冲区,这不是那么容易,因为在反序列化时,我需要传递序列化一个通用参数。如果我动态执行此操作,则需要返回到Reflection以使用正确的类型参数创建相应的泛型方法。我不明白这比现在的方法更容易或更快。 –

0

我的工作类似的东西,现在我所提供的lib已经是第一个版本: http://bitcare.codeplex.com/

当前版本不支持泛型还,但我打算在最近的时间添加它。 我只在那里上传源代码 - 当我准备好泛型时,我还准备bin版本...

它假设双方(客户端和服务器)知道他们序列化/反序列化,所以没有任何理由嵌入有完整的元数据信息。由于这个序列化结果非常小,生成的序列化器工作速度非常快。它具有数据字典,使用智能数据存储(简称只存储重要位),并在必要时进行最终压缩。如果您需要它,只需尝试解决您的问题。

许可证是GPL,但我会很快改变它限制较少的一个(免费用于商业用途还可以,但对自己的风险像GPL)

+0

这很有趣,但我需要一些经过良好测试的产品。你如何生成代码。从示例项目中,我还没有看到为您的Person类完成的代码。如果你静静地做,你如何应对泛型,直到运行时,它是不明确的。但如果你能做到这一点,我一定会感兴趣。如果你想卖你的软件,GPL也是不行的;-)。 –

+0

我在下面提供了答案。 StackOverflow不能保存我的答案作为评论在这里:(... –

+0

我提供了上面的答案(为什么它在上面 - 不是下面???)。StackOverflow无法保存我的答案作为评论在这里:(。 –

0

我上传到CodePlex上的版本正在与一些我的产品。当然,它用不同的单元测试进行测试。他们没有上传,因为我将它移植到vs2012和.net 4.5,并决定为即将发布的版本创建新的测试用例集。

我不处理抽象(所谓的开放)泛型。我处理参数化的泛型。从数据契约的角度来看,参数化泛型只是专门的类,所以我可以像往常一样处理它们(与其他类一样) - 区别仅在于对象构造和存储优化。

当我存储有关上可为空<空值信息>它需要一个比特在存储流只,如果它不是空值我根据泛型参数设置(所以例如做日期时间的序列化类型做系列化,可以从一位获取所谓的默认值到几个字节)。目标是根据当前关于类的数据合同的知识生成序列化代码,而不是在运行中浪费内存和处理能力。当我在代码生成过程中根据某些泛型看到属性时,我知道泛型的所有属性,并且我知道每个属性的类型:)从这个角度来看,它是具体类。

我会尽快更改许可证。我必须先弄清楚如何做到这一点:),因为我发现可以从提供的许可证类型列表中进行选择,但我无法提供自己的许可证文本。我看到了Newtonsoft的许可证.Json也是我想要的,但我还不知道如何改变它...

该文档还没有提供给那里,但总之很容易准备自己的序列化测试。你必须编译你想要存储/序列化类型的程序集,然后在你的序列化库中创建* .tt文件(比如person类 - 它检查依赖关系并为其他依赖类生成代码)并保存文件(当你保存它们时,它会生成与序列化库合作的所有代码)。您也可以在构建配置中创建任务,以便每次构建解决方案时从tt文件重新生成源代码(可能Entity Framework在生成过程中以类似的方式生成代码)。 您现在可以编译序列化库并测量结果的性能和大小。

我需要这个序列库,我与Azure Table中和BLOB存储实体的有效使用的框架,所以我打算尽快完成最初的版本...