我试图在新的应用程序版本中为某个protobuf序列化类添加新的枚举值,并且在测试时,注意到以前的版本将抛出异常,给定这种新的文件格式:
An unhandled exception of type 'ProtoBuf.ProtoException' occurred in protobuf-net.dll Additional information: No {enum-type-name} enum is mapped to the wire-value 3
很明显,它告诉我int
的{{1}}值没有枚举值,但我始终认为Protocol Buffers defaulted to the zero-valued ("default") enum value(如果如果实际的枚举值无法映射到。
为了澄清,可以使用以下示例重现这一点(我有意将反序列化步骤转换为另一个类,以模仿试图加载新格式的旧应用程序):
3
以下代码将失败:
// --- version 1 ---
public enum EnumV1
{
Default = 0,
One = 1,
Two = 2
}
[ProtoContract]
public class ClassV1
{
[ProtoMember(1)]
public EnumV1 Value { get; set; }
}
// --- version 2 ---
public enum EnumV2
{
Default = 0,
One = 1,
Two = 2,
Three = 3 // <- newly added
}
[ProtoContract]
public class ClassV2
{
[ProtoMember(1)]
public EnumV2 Value { get; set; }
}
由于v1已经打开,在v2中序列化时是否可以使用一些元数据以避免此问题?当然,我可以通过重写v2以使用单独的属性并使枚举值保持不变来让自己摆脱这个麻烦。但是如果可能的话,我想让枚举向后兼容。
答案 0 :(得分:3)
在您的枚举中添加[ProtoContract(EnumPassthru=true)]
将允许protobuf-net反序列化未知值。
很遗憾,无法追溯修复您的v1。你必须使用不同的财产。
答案 1 :(得分:1)
由于v1已经打开,在v2中序列化时是否可以使用一些元数据以避免此问题?当然,我可以通过重写v2以使用单独的属性并使枚举值保持不变来让自己摆脱这个麻烦。但是如果可能的话,我想让枚举向后兼容。
您遇到的是此处描述的 protobuf-net 错误protobuf-net - issue #422: Invalid behaviour while deserializing unknown enum value。
根据protobuf-net faulty enum exception (issue 422) need a good workaround(当然还有你的帖子),它似乎尚未修复。
不幸的是,您需要修复protobuf-net
源代码或使用提到的解决方法。
更新:我已检查GitHub存储库中的代码并确认问题仍未修复。以下是EnumSerializer.cs中的有问题的代码(ISSUE #422
评论是我的):
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
int wireValue = source.ReadInt32();
if(map == null) {
return WireToEnum(wireValue);
}
for(int i = 0 ; i < map.Length ; i++) {
if(map[i].WireValue == wireValue) {
return map[i].TypedValue;
}
}
// ISSUE #422
source.ThrowEnumException(ExpectedType, wireValue);
return null; // to make compiler happy
}
答案 2 :(得分:0)
你的ClassV1缺乏向前兼容性。
我会以这样的方式实现Proto契约,它序列化/反序列化枚举值的字符串表示。这样您就可以自己处理回退到默认值。 Value属性不会被序列化/反序列化。
public enum EnumV1
{
Default = 0,
One = 1,
Two = 2
}
public enum EnumV2
{
Default = 0,
One = 1,
Two = 2,
Three = 3 // <- newly added
}
[ProtoContract]
public class ClassV1
{
[ProtoMember(1)]
public string ValueAsString
{
get { return Value.ToString(); }
set
{
try
{
Value = (EnumV1) Enum.Parse(typeof (EnumV1), value);
}
catch (Exception)
{
Value = EnumV1.Default;
}
}
}
public EnumV1 Value { get; set; }
}
[ProtoContract]
public class ClassV2
{
[ProtoMember(1)]
public string ValueAsString
{
get { return Value.ToString(); }
set
{
try
{
Value = (EnumV2)Enum.Parse(typeof(EnumV2), value);
}
catch (Exception)
{
Value = EnumV2.Default;
}
}
}
public EnumV2 Value { get; set; }
}
仍然没有解决生产中具有非正向可比类的问题。
答案 3 :(得分:0)
您可以将DefaultValue属性添加到proto成员属性。
[ProtoContract]
public class ClassV1
{
[ProtoMember(1), DefaultValue(EnumV1.Default)]
public EnumV1 Value { get; set; }
}
要明确如何为默认情况初始化属性。