[英]Protobuf-net enum backwards compatibility
我试图在新的应用程序版本中为某个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
值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; }
}
并且以下代码将失败:
// serialize v2 using the new app
var v2 = new ClassV2() { Value = EnumV2.Three };
var v2data = Serialize(v2);
// try to deserialize this inside the old app to v1
var v1roundtrip = Deserialize<ClassV1>(v2data);
由于v1是公开的,因此在v2中进行序列化时是否可以使用一些元数据来避免此问题? 我当然可以通过重写v2来使用单独的属性并使枚举值保持不变,从而摆脱麻烦,但是我想尽可能使枚举向后兼容。
将[ProtoContract(EnumPassthru=true)]
到枚举将允许protobuf-net对未知值进行反序列化。
不幸的是,无法追溯修复v1。 您将不得不使用其他属性。
由于v1是公开的,因此在v2中进行序列化时是否可以使用一些元数据来避免此问题? 我当然可以通过重写v2来使用单独的属性并使枚举值保持不变,从而摆脱麻烦,但是我想尽可能使枚举向后兼容。
您遇到的是一个protobuf-net错误,此处描述了protobuf-net问题#422:在反序列化未知枚举值时无效的行为 。
似乎还没有解决,根据这里protobuf-net错误的枚举异常(问题422)需要一个好的解决方法 (当然还有您的帖子)。
不幸的是,您需要修复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
}
您的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; }
}
仍然不能解决生产中具有不可前转的类的问题。
您可以将DefaultValue属性添加到您的原型成员属性。
[ProtoContract]
public class ClassV1
{
[ProtoMember(1), DefaultValue(EnumV1.Default)]
public EnumV1 Value { get; set; }
}
明确说明如何针对默认情况初始化属性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.