protobuf-net和de / serializing对象

时间:2012-11-05 15:32:26

标签: c# .net protobuf-net

我正在检查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);

    }
}

3 个答案:

答案 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存储的实体,所以我计划很快完成初始版本...