Reputation: 3837
Here is an example class provided by Marc Gravel in his introduction on how to use Protobuf.net:
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
[ProtoMember(3)]
public Address Address {get;set;}
}
[ProtoContract]
class Address {
[ProtoMember(1)]
public string Line1 {get;set;}
[ProtoMember(2)]
public string Line2 {get;set;}
}
I have some questions, which I could not find the answers for after searching the web:
If on day 1, I know that I don't need the name property [ProtoMember(2)], then if I omit the [ProtoMember(2)] attribute, will Protobut.net ignore that property and not include it in the output serialized data? If true, then when the data is deserialized on the other end - what is Name initialized to - null?
Lets say that all 3 properties are initially serialized as shown above. If in the future, it turns out that the name property [ProtoMember(2)] is no longer required, can the [ProtoMember(2)] attribute be safely omitted so that only the first and third property are serialized? If true, would it be OK to simply leave the attribute numbers as shown (i.e. 1 & 3)? Any caveats if this is the case?
If it is OK to omit a serialization attribute for a property in a class, then what happens if the class definition on the deserialization side is out of sync? For example, say that the deserialization class defines all 3 properties above, but the serialization code only defines 1 and 3? Likewise, if the deserialization code only expects to see properties 1 and 3, but the serialization code sends all 3, would that still work or will it generate an error?
Upvotes: 3
Views: 1890
Reputation: 3837
Since others may want to know the answer to these questions, I decided to post the question and share my findings.
These questions are actually quite easy to solve with a test program:
class Program
{
static void Main(string[] args)
{
var person = new Person1
{
Id = 12345,
Name = "Fred",
Address = new Address
{
Line1 = "Flat 1",
Line2 = "The Meadows"
}
};
//
byte[] arr = Serialize(person);
Person2 newPerson = Deserialize(arr);
/*
using (var file = File.Create("person.bin"))
{
Serializer.Serialize(file, person);
}
//
Person newPerson;
using (var file = File.OpenRead("person.bin"))
{
newPerson = Serializer.Deserialize<Person>(file);
}
*/
}
public static byte[] Serialize(Person1 person)
{
byte[] result;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, person);
result = stream.ToArray();
}
return result;
}
public static Person2 Deserialize(byte[] tData)
{
using (var ms = new MemoryStream(tData))
{
return Serializer.Deserialize<Person2>(ms);
}
}
}
[ProtoContract]
class Person1
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
[ProtoContract]
class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
}
[ProtoContract]
class Person2
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
So, to try out if Protobuf.net will ignore the Name property when serializing, first run the code in debug to see the total number of bytes with all 3 properties serialized. This can be done by setting a breakpoint at the Serialize(person) line and examining the size of arr.
Then, remove the [ProtoMember(2)] attribute from the Name property of the Person1 class. Running the code in debug shows that it leaves it out because the number of bytes is 6 less than before. When the object is then deserialized back to an object of Person2, it then shows that the Name property is initialized to null.
After replacing the [ProtoMember(2)] attribute for the Name property in the Person1 class, remove this same attribute for the Person2 class. After debugging through the code, it shows that after the Deserialize call, the Person2.Name property is set to null.
So, it looks like Protobuf.net was well designed to be quite flexible, efficient, and in some ways is backwards compatible in that it supports removing of obsolete properties.
Upvotes: 3
Reputation: 1062502
null
(note: there is also an option in which the constructor can be suppressed, in which case it would be null
even if your class had a constructor / initializer)IExtensible
(often by subclassing Extensible
) - if it does, the unexpected data is stored separately, so that it can still be queried manually, or (more common) "round tripped" (i.e. if you serialize it again, the extra data is persisted, even though you didn't expect it)public bool ShouldSerializeName() => Name != null && SomethingElse == 42;
Upvotes: 4