protobuf-net: Issues deserializing DataMember fields in lieu of read-only property
Posted
by Paul Smith
on Stack Overflow
See other posts from Stack Overflow
or by Paul Smith
Published on 2010-04-10T05:28:48Z
Indexed on
2010/04/10
5:33 UTC
Read the original article
Hit count: 1152
c#
|protobuf-net
I'm having issues deserializing certain properties of ORM-generated entities using protobuf-net. I suspect something in the way the ORM manages serialization attributes on read-only properties (uses public backing fields with DataMember attributes & [de]serializes) those instead of the corresponding read-only property, which has an IgnoreDataMember attribute). Guid properties might have issues of their own, but the field vs. property thing is my working theory now.
Here's a simplified example of the code. Say I have a class, Account
with an AccountID
read-only guid, and an AccountName
read-write string. I serialize & immediately deserialize a clone. In this scenario I get one of two results (depending on the entity, haven't isolated the specific commonality yet).
The deserialized clone either:
- ...has a different AccountID from the original, or
- ...throws an
Incorrect wire-type deserializing Guid
exception while deserializing.
Here's example usage...
Account acct = new Account() { AccountName = "Bob's Checking" };
Debug.WriteLine(acct.AccountID.ToString());
using (MemoryStream ms = new MemoryStream())
{
ProtoBuf.Serializer.Serialize<Account>(ms, acct);
Debug.WriteLine(Encoding.UTF8.GetString(ms.GetBuffer()));
ms.Position = 0;
Account clone = ProtoBuf.Serializer.Deserialize<Account>(ms);
Debug.WriteLine(clone.AccountID.ToString());
}
And here's an example ORM'd class (simplified; hopefully haven't removed the cause of the issue in the process). Uses a shell game to deserialize read-only properties by exposing the backing field ("can't write" essentially becomes "shouldn't write," but we can scan code for instances of assigning to these fields, so the hack works for our purposes):
[DataContract()]
[Serializable()]
public partial class Account
{
public Account()
{
_accountID = Guid.NewGuid();
}
[XmlAttribute("AccountID")]
[DataMember(Name = "AccountID", Order = 0)]
public Guid _accountID;
/// <summary>
/// A read-only property; XML, JSON and DataContract serializers all seem
/// to correctly recognize the public backing field when deserializing:
/// </summary>
[IgnoreDataMember]
[XmlIgnore]
public Guid AccountID
{
get { return this._accountID; }
}
[IgnoreDataMember]
protected string _accountName;
[DataMember(Name = "AccountName", Order = 1)]
[XmlAttribute]
public string AccountName
{
get { return this._accountName; }
set { this._accountName = value; }
}
}
XML, JSON and DataContract serializers all seem to serialize / deserialize matching object graphs here, so this attribute arrangement apparently causes those serializers to correctly assign to the public backing field when deserializing. I've tried protobuf-net with lists vs. single instances, different prefix styles, etc., but always either get the 'incorrect wire type ... Guid' exception, or the Guid property (field) not deserializing correctly.
So the specific questions are,
- is there a quick workaround for this, and/or
- is there an explanation for both of outcomes 1 & 2 above, and/or
- can protobuf-net somehow be corralled into behaving like WCF in cases like this (i.e. follow the same DataMember/IgnoreDataMember semantics)?
We hope not to have to create a protobuf dependency directly in the entity layer; if that's the case, we'll probably create proxy DTO entities with all public properties having protobuf attributes. (This is a subjective issue I have with all declarative serialization models; it's a ubiquitous pattern, but IMO, "normal" should be to have objects and serialization contracts decoupled.)
Thanks!
© Stack Overflow or respective owner