1
votes

I'm trying to replace our existing serialization with Proto-buf. Problem is we currently use ISerializable to check whether data has changed and only serialize the original value if the data has changed. This is done by using two nullable variables and only adding the original value in ISerializable.GetObjectData to the info object if the value has changed.

When deserializing, in the ISerializable constructor, I read the SerializationInfo to find which members were serialized and which ones were not. If an original value was not serialized, it's value is set to the current value. (therefore saving resources as it wasn't serialized).

Is there a way in Protobuf-net for me to find out what fields were deserialized? I am using the ShouldSerialize pattern to not send the original value as I explained above, but when I get to the other end I need to know what fields were serialized to be able to set the original value.

Edit: More Details, here is a sample class.

[Serializable()]
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class SomeClass : ISerializable
{
    internal int? _key;
    internal int? _originalKey;

    internal bool ShouldSerialize_key()
    {
        return _key.HasValue;
    }
    [NonSerialized]
    public bool _keySpecified;

    internal bool ShouldSerialize_originalKey()
    {
        return _key != _originalKey;
    }
    [NonSerialized]
    public bool _originalKeySpecified;
    [OnDeserialized()]
    internal void OnDeserializedMethod(StreamingContext context)
    {
        // Use this to set the _originalKey if it hasn't been specified.
        if (!_originalKeySpecified)
        {
            _originalKey = _key;
        }
    }
}

As you can see, _originalKey is not serialized if it has the same value as the _key. When the object is Deserialized, I want to know if _originalKey was Deserialized or not. I thought your answer of _originalKeySpecified would work, but in the class above, once I Deserialize, the _originalKeySpecified is always false. Does the Protobuf deserialization process set the value? (Note that I can't use the ShouldSerialize property to decide whether to set the _originalKey when deserialized as it may have been changed from null to another value, which I need to know when saving in the data store.

1
Re your edit: see the wording in my answer: "This is a simple bool property..." - _originalKeySpecified is not a bool property, so does not match the pattern it is looking for. Nor is _keySpecified a property, note. - Marc Gravell
Additionally, note it doesn't use both patterns: it looks for a *Specified property, then it looks for a ShouldSerialize* method, then it stops. So basically you would also move some of your other logic to *Specified. This order is because *Specified allows both get and set (separately); ShouldSerialize* is only effectively a get, so is a second choice. - Marc Gravell
Ah facepalm. It also has to be public as you said below. Why won't it work set as internal? - Colin Moore
hmmm... it should work as internal/private; ShouldSerialize* does (I've just tested it) - I'll take a look at why that isn't working later - Marc Gravell
Found and fixed; committed to source, but I haven't deployed NuGet etc - Marc Gravell

1 Answers

0
votes

It depends a bit on your specific case. For example, if you have nullable backing fields, then: the ones that were serialized are example the ones that have non-null values... i.e.

private int? id;
[ProtoMember(n)]
public int Id {
    get { return id.GetValueOrDefault(); }
    set { id = value; }
}
public bool ShouldSerializeId() { return id.HasValue; }

Here you could simply check each ShouldSerialize* method to see which values were deserialized; anything that wasn't will still have null (it doesn't call the set if it didn't have data to deserialize).

An alternative is the *Specified pattern, as used by some other serializers (XmlSerializer, IIRC - and possibly DataContractSerializer). This is a simple bool property, which gets set (by the serializer) to true if a value is assigned, but isn't otherwise. Likewise, the value of *Specified is used identically to ShouldSerialize* to determine whether a member should be processed during serialization.

If I've missed your intent, please clarify (preferably with an example), and I'll try to explain more.