Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force protobuf-net to serialize all default values

I have a complex model serialized/deserialized with protobuf-net, and we had several bugs with this "feature" of not serializing default values.

Example:

[DataContract]
class Foo{
  public Foo(){
    // Value forced by constructor
    this.Value = 1;
  }

  // Buggy, when Value is set to zero
  [DataMember(Order = 1)]
  public double Value {get; set}
}

When Value = 0, it is not serialized by protobuf-net, but during deserialization, the constructor forces Value to 1 (and protobuf-net do not change this).

In order to make it work, I need to force protobuf-net to serialize values, with:

  // Works fine
  [DataMember(Order = 1, IsRequired = true)]
  public double Value {get; set}

But, as we already got bugs because of this feature, we'd like to force protobuf-net for the whole model, instead of marking every property.

Is it possible?

like image 512
ghusse Avatar asked Sep 06 '12 08:09

ghusse


1 Answers

Yes, this feature is fully supported. Actually, if I was forced to admit to bad design decisions in v1, the implicit-zero-defaults would be one of them - but for backwards compatibility the behaviour is retained by default. What you are looking for is RuntimeTypeModel.UseImplicitZeroDefaults, which is true by default.

To avoid changing the behaviour of code relying on v1 behaviour (via Serilaizer.*), you cannot change this feature on the default model, so what you need to do is:

  1. define your own model/serializer-instance
  2. set UseImplicitZeroDefaults = false, before using it
  3. in your serialize/deserialize code, use this instance rather than Serializer.*

for example:

private static readonly RuntimeTypeModel serializer;
static MyType() { // your type-initializer for class MyType
    serializer = TypeModel.Create();
    serializer.UseImplicitZeroDefaults = false;
}
... then when needed:
serializer.Serialize(stream, obj);
...
ObjType obj = (ObjType)serializer.Deserialize(stream, null, typeof(ObjType));

Another approach I could perhaps consider in the future is allowing for assembly-level attributes; this would help in particular for anyone using "precompiler" (for example, targeting mobile devices or WinRT) - so (just thinking aloud):

// this feature ***does not currently exist***
[assembly:ProtoDefaults(UseImplicitZeroDefaults=false, SkipConstructor=true)]

which would then apply to all types in that assembly. Just a thought. Another obvious advantage is that it would work with code that uses the classic Serializer.* API.

like image 200
Marc Gravell Avatar answered Nov 03 '22 23:11

Marc Gravell