即使对于简单实体,ProtoBuf 序列化也会丢失数据

2024-03-11

[更新#1]:我已将修改和修复的“演示”项目上传到https://github.com/sidshetye/SerializersCompare https://github.com/sidshetye/SerializersCompare其他人是否有兴趣查看基准测试。

[更新#2]:我发现 ProtoBufs 仅在后续迭代中取得数量级领先。对于一次性序列化,BinaryFormatter 的速度要快一个数量级。为什么?单独提问...

我正在尝试比较 BinaryFormatter、Json.NET 和 ProtoBuf.NET(今天从 NuGet 中获取了后者)。我发现 ProtoBuf 不输出任何实际字段,全部为空和 0(见下文)。另外 BinaryFormatter 似乎要快得多。我基本上序列化=>反序列化对象并进行比较

  • 原始对象与重新生成的对象
  • 大小(以字节为单位)
  • 时间(以毫秒为单位)

Question

  1. 如何让 ProtoBuf 真正吐出真实值,而不仅仅是(默认?)值?
  2. 为了速度我做错了什么?我认为 ProtoBuf 应该是最快的序列化器?

我从测试应用程序得到的输出如下:

Json: Objects identical
Json in UTF-8: 180 bytes, 249.7054 ms

BinaryFormatter: Objects identical
BinaryFormatter: 512 bytes, 1.7864 ms

ProtoBuf: Original and regenerated objects differ !!
====Regenerated Object====
{
    "functionCall": null,
    "parameters": null,
    "name": null,
    "employeeId": 0,
    "raiseRate": 0.0,
    "addressLine1": null,
    "addressLine2": null
}
ProtoBuf: 256 bytes, 117.969 ms

我的测试是在控制台应用程序中使用一个简单的实体(见下文)。系统:Windows 8x64、VS2012 Update 1、.NET4.5。顺便说一下,我使用以下方法得到了相同的结果[ProtoContract] and [ProtoMember(X)]习俗。文档不清楚,但它appearsDataContract 是较新的“统一”支持约定(对吗?)

[Serializable]
[DataContract]
class SimpleEntity
{
    [DataMember(Order = 1)]
    public string functionCall {get;set;}

    [DataMember(Order = 2)]
    public string parameters { get; set; }

    [DataMember(Order = 3)]
    public string name { get; set; }

    [DataMember(Order = 4)]
    public int employeeId { get; set; }

    [DataMember(Order = 5)]
    public float raiseRate { get; set; }

    [DataMember(Order = 6)]
    public string addressLine1 { get; set; }

    [DataMember(Order = 7)]
    public string addressLine2 { get; set; }

    public SimpleEntity()
    {
    }

    public void FillDummyData()
    {
        functionCall = "FunctionNameHere";
        parameters = "x=1,y=2,z=3";

        name = "Mickey Mouse";
        employeeId = 1;
        raiseRate = 1.2F;
        addressLine1 = "1 Disney Street";
        addressLine2 = "Disneyland, CA";
    }
}

对于那些感兴趣的人,这里是我的 ProtoBufs AllSerializers 类的片段

public byte[] SerProtoBuf(object thisObj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        Serializer.Serialize(ms, thisObj);
        return ms.GetBuffer();
    }
}

public T DeserProtoBuf<T>(byte[] bytes)
{

    using (MemoryStream ms = new MemoryStream())
    {
        ms.Read(bytes, 0, bytes.Count());
        return Serializer.Deserialize<T>(ms);
    }
}

首先,你的序列化/反序列化方法都被破坏了;你过度报告了结果(GetBuffer(), 没有Length),并且您没有将任何内容写入流中进行反序列化。这是一个正确的实现(尽管您也可以使用GetBuffer()如果你要回来ArraySegment<byte>):

public static byte[] SerProtoBuf(object thisObj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        Serializer.NonGeneric.Serialize(ms, thisObj);
        return ms.ToArray();
    }
}

public static T DeserProtoBuf<T>(byte[] bytes)
{
    using (MemoryStream ms = new MemoryStream(bytes))
    {
        return Serializer.Deserialize<T>(ms);
    }
}

这就是为什么您没有收到任何数据的原因。其次,您没有说明如何计时,因此这是我根据您的代码编写的一些代码(其中还包括显示它正在获取所有值的代码)。结果先:

Via BinaryFormatter:
1 Disney Street
Disneyland, CA
1
FunctionNameHere
Mickey Mouse
x=1,y=2,z=3
1.2

Via protobuf-net:
1 Disney Street
Disneyland, CA
1
FunctionNameHere
Mickey Mouse
x=1,y=2,z=3
1.2

Serialize BinaryFormatter: 112 ms, 434 bytes
Deserialize BinaryFormatter: 113 ms
Serialize protobuf-net: 14 ms, 85 bytes
Deserialize protobuf-net: 19 ms

分析:

两个序列化器存储相同的数据; protobuf-net 的速度快了一个数量级,但输出却小了 5 倍。我宣布:获胜者。

Code:

static BinaryFormatter bf = new BinaryFormatter();
public static byte[] SerBinaryFormatter(object thisObj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        bf.Serialize(ms, thisObj);
        return ms.ToArray();
    }
}

public static T DeserBinaryFormatter<T>(byte[] bytes)
{
    using (MemoryStream ms = new MemoryStream(bytes))
    {
        return (T)bf.Deserialize(ms);
    }
}
static void Main()
{
    SimpleEntity obj = new SimpleEntity(), clone;
    obj.FillDummyData();

    // test that we get non-zero bytes
    var data = SerBinaryFormatter(obj);
    clone = DeserBinaryFormatter<SimpleEntity>(data);
    Console.WriteLine("Via BinaryFormatter:");
    Console.WriteLine(clone.addressLine1);
    Console.WriteLine(clone.addressLine2);
    Console.WriteLine(clone.employeeId);
    Console.WriteLine(clone.functionCall);
    Console.WriteLine(clone.name);
    Console.WriteLine(clone.parameters);
    Console.WriteLine(clone.raiseRate);
    Console.WriteLine();

    data = SerProtoBuf(obj);
    clone = DeserProtoBuf<SimpleEntity>(data);
    Console.WriteLine("Via protobuf-net:");
    Console.WriteLine(clone.addressLine1);
    Console.WriteLine(clone.addressLine2);
    Console.WriteLine(clone.employeeId);
    Console.WriteLine(clone.functionCall);
    Console.WriteLine(clone.name);
    Console.WriteLine(clone.parameters);
    Console.WriteLine(clone.raiseRate);
    Console.WriteLine();

    Stopwatch watch = new Stopwatch();
    const int LOOP = 10000;

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        data = SerBinaryFormatter(obj);
    }
    watch.Stop();
    Console.WriteLine("Serialize BinaryFormatter: {0} ms, {1} bytes", watch.ElapsedMilliseconds, data.Length);

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        clone = DeserBinaryFormatter<SimpleEntity>(data);
    }
    watch.Stop();
    Console.WriteLine("Deserialize BinaryFormatter: {0} ms", watch.ElapsedMilliseconds, data.Length);

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        data = SerProtoBuf(obj);
    }
    watch.Stop();
    Console.WriteLine("Serialize protobuf-net: {0} ms, {1} bytes", watch.ElapsedMilliseconds, data.Length);

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        clone = DeserProtoBuf<SimpleEntity>(data);
    }
    watch.Stop();
    Console.WriteLine("Deserialize protobuf-net: {0} ms", watch.ElapsedMilliseconds, data.Length);
}

Lastly, [DataMember(...)]support 并不是真正的“较新的‘统一’支持约定”——它当然不是“较新的”——我很确定它自从提交#4(可能更早)之类的东西以来就支持这两者。这只是为了方便而提供的选项:

  • 并非所有目标平台都有DataMemberAttribute
  • 有些人喜欢将 DTO 层限制为内置标记
  • 有些类型很大程度上超出了您的控制范围,但可能已经具有这些标记(例如,从 LINQ-to-SQL 生成的数据)
  • 另外,请注意,2.x 允许您在运行时定义模型,而无需添加属性(尽管属性仍然是最重要的)方便的方法来做到这一点)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

即使对于简单实体,ProtoBuf 序列化也会丢失数据 的相关文章

  • 在 C# 中使用枚举值反序列化 Dictionary

    我正在尝试序列化 反序列化Dictionary
  • 如何在Unity Inspector中创建多维数组?

    如何在 Unity Inspector 中创建枚举多维数组并使其可序列化 以便我可以从不同的脚本调用它 public enum colors red blue green yellow cyan white purple public in
  • ProtoBuf-net AsReference 需要 Activator.CreateInstance 中的公共构造函数吗?

    在我的两门课程中 看起来像这样 最少 using System using System Collections Generic using System Collections using System ComponentModel us
  • 序列化 ArrayList

    我正在尝试编写一个 Android 游戏 即使用户想要返回主菜单或者活动被系统终止 我也希望能够暂停游戏 onSaveInstanceState 似乎并没有给我很大的控制权来决定何时可以读回捆绑包 而且据我所知 捆绑包仅在短时间内有效 所以
  • C# Xml-使用 IXmlSerialized 序列化派生类

    我有一个与 XML 序列化兼容的基类和一个实现 IXmlSerialized 的派生类 在此示例中 基类确实实现了 IXmlSerializable using System Diagnostics using System Text us
  • C++网络序列化[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一种将 C 数据包序列化为网络流的解决方案 我在这里看到很多帖子提到人们 ACE 谷歌协议缓
  • 使用 Jackson 将 Json 反序列化为其他类层次结构

    现在我正在和杰克逊一起工作 我对此有一些疑问 首先 我有两个服务 第一个是数据收集和发送服务 第二个是接收该数据 例如将其记录到文件中 因此 第一个服务的类层次结构如下 ConcreteC Base ConcreteA ConcreteB
  • 在 .NET 中序列化,在 C++ 中反序列化

    我有一个 NET 应用程序 它以二进制格式序列化对象 该对象是一个由几个字段组成的结构 我必须在 C 应用程序中反序列化并使用该对象 我不知道是否有 C 的序列化库 谷歌搜索没有出现太多 实现这一目标最快的方法是什么 提前致谢 罗伊 更新
  • 使用 Protobuf-net 序列化 object[]

    我想序列化和反序列化存储在对象数组中的一些值 public class Sample public object Data 我知道在运行时数组中期望什么类型 实际上 我想将 Sample 内部的数据视为消息流 就个人而言 我知道我可以使用S
  • C# 中的序列化和对象版本控制

    如果我想序列化一个对象 我必须使用 Serializable 属性和所有成员变量将被写入该文件 我不知道如何进行版本控制 例如如果我添加一个新的成员变量 重命名变量或仅删除变量 然后打开 反序列化 文件 如何确定对象 文件版本 以便我可以正
  • djangorest框架:从序列化器validate()方法设置字段级错误

    我有一个序列化程序 它根据其他字段的值验证字段 在错误响应中 我想将每个字段错误显示为字段错误 而不是显示 non field errors 下的所有内容 如果我提出错误 就会发生这种情况对象级验证方法中的 ValidationError
  • .NET NewtonSoft JSON反序列化映射到不同的属性名称

    我有以下从外部接收的 JSON 字符串 team v1 attributes eighty min score home or away home score 22 team id 500 v1 attributes eighty min
  • 序列化代码导致未处理的异常

    我正在尝试创建一些代码 可以将类库序列化和反序列化为 AutoCAD 绘图 这个问题与 AutoCAD 没有什么关系 只是它是我无法通过正常方式调试它的原因 我从开始这个项目本文 http adndevblog typepad com au
  • 如何在 Java 中测试一个类是否正确实现了 Serialized(不仅仅是 Serialized 的实例)

    我正在实现一个可序列化的类 因此它是一个与 RMI 一起使用的值对象 但我需要测试一下 有没有办法轻松做到这一点 澄清 我正在实现该类 因此在类定义中添加 Serialized 很简单 我需要手动序列化 反序列化它以查看它是否有效 我找到了
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • 无法使用 Json.NET 8.0.1 反序列化具有字节数组属性的对象

    升级代码库以使用 Json NET 8 0 1 后 一些反序列化出现问题 使用 Json NET 7 0 1 一切正常 显然这是类型属性的反序列化byte 这会导致问题 如果我删除byte 属性它工作正常 我可以使用这个简单的控制台应用程序
  • 扩展 Protobuf 消息

    我有许多不同的模式 但是每个模式都包含一组字段 我想知道是否有一种方法可以让不同的模式扩展父模式并继承其字段 例如 这就是我想要的 message Parent required string common1 0 optional stri
  • 如何使用泛型类型的 DataContractSerializer 编写自定义序列化器?

    我想编写一个自定义序列化器 用于将会话状态存储到Azure 缓存 预览版 这意味着这个自定义序列化器必须实现IDataCacheObjectSerializer 如果我错了 请告诉我 我需要编写这个自定义序列化程序的原因是我需要序列化一些包
  • 使用 Protobuf-net,我收到有关 List 未知线路类型的异常

    我已经开始将 Unity iOS 游戏转换为使用 Protobuf net 保存状态 看起来一切正常 直到我将此实例变量添加到GameState ProtoMember 10 public List
  • 空列表和空列表之间的区别无法使用 protobuf-net 中的代理来解决[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions proto

随机推荐