4

I have a class that I serialize, then deserialize using Protobuf-net version r431 (probably a year old or so). The class contains an enum _type and a string called _band. In the constructor, _type is set to StationType.Other and _band is set to an empty string.

As you can see I create an object with data, serialize, then deserialize. The value of the enum _type (StationType.Streaming) is lost, while the _band (FM) is retained.

I feel this is an error, since the behavior is inconsistent. However, if I start the enum from the value of 1 instead of 0, everything works as expected (e.g. all values are retained).

Am I missing something here? See the code and the output below:

using System;
using System.IO;
using ProtoBuf;

namespace ProtoBufsWithEnums
{
    class Program
    {
        static void Main(string[] args)
        {
            stn1 = new Station{Type = StationType.Streaming, Band = "FM"};

            var ms1 = new MemoryStream();
            Serializer.Serialize(ms1, stn1); // serialize
            byte[] bytes = ms1.ToArray();

            var ms2 = new MemoryStream(bytes);
            Station stn2 = Serializer.Deserialize<Station>(ms2); // deserialize

            Console.WriteLine("Type - Original {0}, New {1}", stn1.Type, stn2.Type);
            Console.WriteLine("Band - Original {0}, New {1}", stn1.Band, stn2.Band);
        }
    }

    [ProtoContract]
    public enum StationType
    {
        [ProtoEnum(Name = "Streaming", Value = 0)]
        Streaming = 0,
        [ProtoEnum(Name = "Terrestrial", Value = 1)]
        Terrestrial = 1,
        [ProtoEnum(Name = "Other", Value = 2)]
        Other = 2,
        [ProtoEnum(Name = "Group", Value = 3)]
        Group = 3
    }

    [ProtoContract]
    public class Station 
    {
        [ProtoMember(9)]
        private StationType _type;
        [ProtoMember(10)]
        private string _band;

        public Station()
        {
            _type = StationType.Other;
            _band = "";
        }

        public StationType Type
        {
            get { return _type; }
            set { _type = value; }
        }

        public string Band
        {
            get { return _band; }
            set { _band = value; }
        }
    }
}

Output:

Type - Original Streaming, New Terrestrial
Type - Original FM, New FM

EDIT: Found issue 251 on the protobuf-net site, not sure if I am looking at the same issue or not. Not quite sure whether it has been fixed or not.

rene
  • 41,474
  • 78
  • 114
  • 152
AngryHacker
  • 59,598
  • 102
  • 325
  • 594

2 Answers2

10

Here's the glitch:

public Station()
{
    _type = StationType.Other;
    _band = "";
}

protobuf-net makes some implicit assumptions about zero defaults. This works nicely in a lot of cases, but not all. The easiest way to help it here is to tell it that you are using a non-zero default:

[ProtoMember(9), DefaultValue(StationType.Other)]
private StationType _type;

Another option would be to tell it always to serialize it:

[ProtoMember(9, IsRequired = true)]
private StationType _type;

If you have lots of this, another option is to disable the "zero-default" behaviour, which can be done on a custom model:

var model = TypeModel.Create();
model.UseImplicitZeroDefaults = false;

then use model.Serialize(...) etc - but note that you should store and re-use a model like this (don't recreate it every time you need to serialize something), since that contains all the cached reflection/meta-programming output.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

Please mark the Enum with [EnumMember]

e.g.:

 [DataContract]
 [ProtoContract]
    public enum StationType
    {
        [EnumMember]
        [ProtoEnum(Name = "Streaming", Value = 0)]
        Streaming = 0,

        [EnumMember]
        [ProtoEnum(Name = "Terrestrial", Value = 1)]
        Terrestrial = 1,

        [EnumMember]
        [ProtoEnum(Name = "Other", Value = 2)]
        Other = 2,

        [EnumMember]
        [ProtoEnum(Name = "Group", Value = 3)]
        Group = 3
    }
HatSoft
  • 11,077
  • 3
  • 28
  • 43