简体   繁体   English

Protobuf-net枚举向后兼容

[英]Protobuf-net enum backwards compatibility

I was trying to add a new enum value for a certain protobuf-serialized class in a new app version, and while testing, noticed that the previous version will throw an exception, given this new file format: 我试图在新的应用程序版本中为某个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

It is fairly obvious that it's telling me that there is no enum value for the int value of 3 , but I always had the idea that Protocol Buffers defaulted to the zero-valued ("default") enum value (if such exists), in case that an actual enum value couldn't be mapped to. 很明显,它告诉我int3没有枚举值,但是我一直有这样的想法,即协议缓冲区默认为零值(“默认”)枚举值 (如果存在),在实际的枚举值无法映射的情况。

To clarify, this can be reproduced using the following example (I am intentionally doing the deserialization step into a different class to mimic old app trying to load the new format): 为了阐明这一点,可以使用下面的示例进行复制(我有意将反序列化步骤转换为另一个类,以模仿试图加载新格式的旧应用程序):

// --- 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; }
}

And the following code will fail: 并且以下代码将失败:

// 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);

Since v1 is out in the open, is there some metadata I can use when serializing in v2 to avoid this issue? 由于v1是公开的,因此在v2中进行序列化时是否可以使用一些元数据来避免此问题? I can, of course, get myself out of this trouble by rewriting v2 to use a separate property and leave the enum values unmodified, but I'd like to make enums backwards compatible if possible. 我当然可以通过重写v2来使用单独的属性并使枚举值保持不变,从而摆脱麻烦,但是我想尽可能使枚举向后兼容。

Adding [ProtoContract(EnumPassthru=true)] to your enums will allow protobuf-net to deserialize unknown values. [ProtoContract(EnumPassthru=true)]到枚举将允许protobuf-net对未知值进行反序列化。

Unfortunately, there is no way to retroactively fix your v1. 不幸的是,无法追溯修复v1。 You'll have to use a different property. 您将不得不使用其他属性。

Since v1 is out in the open, is there some metadata I can use when serializing in v2 to avoid this issue? 由于v1是公开的,因此在v2中进行序列化时是否可以使用一些元数据来避免此问题? I can, of course, get myself out of this trouble by rewriting v2 to use a separate property and leave the enum values unmodified, but I'd like to make enums backwards compatible if possible. 我当然可以通过重写v2来使用单独的属性并使枚举值保持不变,从而摆脱麻烦,但是我想尽可能使枚举向后兼容。

What you are experiencing is a protobuf-net bug described here protobuf-net - issue #422: Invalid behaviour while deserializing unknown enum value . 您遇到的是一个protobuf-net错误,此处描述了protobuf-net问题#422:在反序列化未知枚举值时无效的行为

It seems that it's not fixed yet according to here protobuf-net faulty enum exception (issue 422) need a good workaround (and of course your post). 似乎还没有解决,根据这里protobuf-net错误的枚举异常(问题422)需要一个好的解决方法 (当然还有您的帖子)。

Unfortunately you need to either fix the protobuf-net source code or use the workarounds mentioned. 不幸的是,您需要修复protobuf-net源代码或使用上述解决方法。

UPDATE: I've checked the code in the GitHub repository and confirming that the issue is still not fixed. 更新:我已经检查了GitHub存储库中的代码,并确认问题仍然没有得到解决。 Here is the problematic code inside the EnumSerializer.cs (the ISSUE #422 comment is mine): 这是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
}

Your ClassV1 lacks forward compatiblity. 您的ClassV1缺乏向前兼容性。

I would have implemented the Proto contract in such a way that it serializes/deserializes the string representation of the enum value. 我将以某种方式实现Proto合约,以使其对枚举值的字符串表示进行序列化/反序列化。 This way you can handle the fallback to the default value by yourself. 这样,您可以自己处理回退到默认值的情况。 The Value property would not be serialized/deserialized. 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; }
}

Still it does not solve the problem of having a non-forward-compabtible class in production. 仍然不能解决生产中具有不可前转的类的问题。

You could add the DefaultValue attribute to your proto member property. 您可以将DefaultValue属性添加到您的原型成员属性。

[ProtoContract]
public class ClassV1
{
    [ProtoMember(1), DefaultValue(EnumV1.Default)]
    public EnumV1 Value { get; set; }
}

To make clear how the property should be initialized for the default case. 明确说明如何针对默认情况初始化属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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