带有枚举的 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 使用嵌套数据反序列化时出现无效的线型异常(C++ 到 C#)