繁体   English   中英

Protobuf-net枚举向后兼容

[英]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

很明显,它告诉我int3没有枚举值,但是我一直有这样的想法,即协议缓冲区默认为零值(“默认”)枚举值 (如果存在),在实际的枚举值无法映射的情况。

为了阐明这一点,可以使用下面的示例进行复制(我有意将反序列化步骤转换为另一个类,以模仿试图加载新格式的旧应用程序):

// --- 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM