带有枚举的 Protobuf-net 反序列化异常

Posted

技术标签:

【中文标题】带有枚举的 Protobuf-net 反序列化异常【英文标题】:Protobuf-net deserialization exception with enum 【发布时间】:2012-12-27 03:17:43 【问题描述】:

我有一个带有标志的枚举,我用 [ProtoMember] 属性进行了装饰,该属性可以在运行 Win7 x64 的本地机器上很好地序列化 和反序列化

但是,我的用例涉及在运行 Windows Server 2008 R2 Enterprise 64 位的服务器上进行序列化,并在我的本地机器上进行反序列化。 当我反序列化时,出现异常:“溢出异常未处理;算术运算导致溢出”。它似乎是从 ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source) 抛出的。

我尝试将枚举更改为 int 并在服务器上序列化 /反序列化本地工作。我想使用枚举而不是int。我做错了什么?

不确定这是否是相关信息,但我在服务器上运行的可执行文件是在我的本地机器上构建的。

枚举来自引用的外部 dll。当我在我的解决方案中复制枚举代码时,反序列化工作。仅当我使用来自外部 dll 的枚举(我怀疑源代码未知)并且枚举值大于(似乎)128 时才会引发异常。在我的情况下,Status.Zeta 和 Status。全部抛出异常;其他枚举值正确反序列化。枚举是这样定义的:

[Flags]
public enum Status

    None = 0,
    Alpha = 1,
    Beta = 8,
    Gamma = 16,
    Delta = 32,
    Epsilon = 64,
    Zeta = 132,
    All = 255,

我无法更改 dll 中的代码。我怎样才能使这项工作?我需要一个 .proto 文件吗?如果可能的话,我会尽量避免这种情况。

【问题讨论】:

你能澄清你正在使用的库的版本吗?另外:您是否有机会显示失败的枚举(或其中的一部分)?枚举的底层类型是什么? 马克,我希望你能回应——谢谢。我正在使用 r602>Full>net30>protobuf-net.dll。我稍后会在我的问题中添加一些代码。 很奇怪。我创建了一个控制台测试应用程序来复制问题,但它没有出现。 我做了一些测试,即使在本地机器上反序列化时,我也遇到了同样的异常。我正在用我的发现更新问题。 枚举值真的很大吗?像 2^31 或更高? 【参考方案1】:

这只影响: byte的枚举

天啊!找出脑死的错误:

case ProtoTypeCode.SByte: Emit(OpCodes.Conv_Ovf_U1); break;
case ProtoTypeCode.Byte: Emit(OpCodes.Conv_Ovf_I1); break;

这将在今天晚些时候进行逆转和部署。谢谢。

正确解释:byte 是无符号的(0 到 255),sbyte 是有符号的(-128 到 127); Conv_Ovf_U1 基本上是 IL 表示“转换为 byte,检查溢出”(就像 C# 中的 checked 关键字如何工作),Conv_Ovf_I1 是“转换为 sbyte,检查溢出”。因此,任何超过 127 的值都会触发溢出标志,从而导致异常。这已在 r614 中修复,现已部署。

【讨论】:

感谢您对此进行调查!我刚刚回到这个问题。我尝试在主页上查找 r614,但看到可供下载的最新版本仍然是 r602。如何通过此修复获得最新的(可能是二进制文件)? @JohnC 啊,我的错。我更新了 nuget 但没有更新谷歌代码。我会启动我的虚拟机来上传它......当然,这就是说:使用 nuget 保持最新状态要容易得多;p 虚幻响应时间!我刚刚测试了它,它可以工作!感谢您在 protobuf-net 上所做的一切。 我一定也会去看看 nuget :)【参考方案2】:

确实有点奇怪。 CLR 中可能存在影响 ProtoBuf 的差异(例如,CLR 附带许多不同的 GC)。比较两台机器的 Machine.config 文件可能会发现一些差异。

至于解决问题,您可以尝试使用 ProtoContract 标记枚举本身,并使用 ProtoMember 标记每个枚举成员。后者允许您设置一个 Value 属性供 ProtoBuf 使用。您还可以将 DataFormat 设置为 Fixed 并查看它是否比默认设置更好。

你可以找到一些examples here。

【讨论】:

我不明白“比较 Machine.config 文件”的部分?请原谅我,总的来说,我对 protobuf 很陌生。至于将枚举标记为 [ProtoContract],我不能这样做,因为我不拥有该代码。 Machine.config 是 .NET 框架安装的配置文件(因此与 ProtoBuf 无关)。您可以在框架安装文件夹中找到它,并且每个 CLR 版本都有一个新副本。如果您的目标是 64 位 Windows 上的 .NET 4.0 或更高版本,您应该可以在 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config 中找到它。请注意,这是一个远景:您的错误很可能出现在其他地方(例如,正在使用不同版本的 ProtoBuf 等)。

以上是关于带有枚举的 Protobuf-net 反序列化异常的主要内容,如果未能解决你的问题,请参考以下文章

使用带有标志枚举的 ProtoBuf-Net 时出错

Protobuf-net 使用嵌套数据反序列化时出现无效的线型异常(C++ 到 C#)

protobuf-net 反序列化错误 无效标签:0

protobuf-net:不正确的线型反序列化 TimeSpan

使用 protobuf-net 进行枚举序列化

使用带有 protobuf-net 异常的代理对 List<T> 进行序列化