in protobuf-net is it possible to partially deserialize a message based on a base type?
In my system I have an inheritance hierarchy where every message inherits from a MessageBase. The MessageBase has a uint MessageType. Ideally I only want to deserialize the MessageBase and check if it’s a MessageType I’m interested in then I can either throw away the message or make the decision to deserialize the actual message. This is to save the cost of deserialize ( I have a cpu cycle budget and lots of messages to process).
Example usage shown below.
Thanks a lot.
MessageBase msgBase = ..deserialize;
if(msgBase.MessageType = 1)//1 is the Tick msg type
{
Tick tick = ..deserialize actual msg;
//do something with tick
}
//throw away msgBase
[ProtoContract,Prot开发者_高级运维oInclude(1, typeof(Tick))]
public class MessageBase
{
protected uint _messageType;
[ProtoMember(1)]
public uint MessageType
{
get { return _messageType; }
set{ _messageType = value;}
}
}
[ProtoContract]
public public class Tick : MessageBase
{
private int _tickId;
private double _value;
public Tick()
{
_messageType = 1;
}
[ProtoMember(1)]
public int TickID
{
get { return _tickId; }
set { _tickId = value; }
}
[ProtoMember(2)]
public double Value
{
get { return _value; }
set { _value = value; }
}
}
If it is part of the message, then at the moment: no. Since it is field 1, I could potentially pre-screen them, but even that is a hack (there is no guarantee that field 1 is first - the spec makes it clear that you must allow for any ordering).
However!
there may be an option, if you are open to a little refactoring. If this is a linear heterogenous sequence of messages, then another way to encode it is to use the SerializeWithLengthPrefix
implementation, passing a different tag for each message type - then you have a sequence like (being a bit liberal with the representation)
1:[tick-body] 2:[some-other-body] 1:[tick body] etc
of course it depends a bit on the other end matching, but unless I am mistaken this ties in pretty well with the SAX-like processing discussed here (as a proposal), which incidentally is also entirely compatible with how the NonGeneric deserialize works. Here's an example, that only deserializes the Bar
objects, showing "2" and "4" on the console:
using System;
using System.IO;
using ProtoBuf;
[ProtoContract]
class Foo
{
[ProtoMember(1)]
public int A { get; set; }
}
[ProtoContract]
class Bar
{
[ProtoMember(1)]
public int B { get; set; }
}
static class Program
{
static void Main()
{
using (var ms = new MemoryStream())
{
Serializer.SerializeWithLengthPrefix(ms, new Foo { A = 1 }, PrefixStyle.Base128, 1);
Serializer.SerializeWithLengthPrefix(ms, new Bar { B = 2 }, PrefixStyle.Base128, 2);
Serializer.SerializeWithLengthPrefix(ms, new Foo { A = 3 }, PrefixStyle.Base128, 1);
Serializer.SerializeWithLengthPrefix(ms, new Bar { B = 4 }, PrefixStyle.Base128, 2);
ms.Position = 0;
// we want all the Bar - so we'll use a callback that says "Bar" for 2, else null (skip)
object obj;
while (Serializer.NonGeneric.TryDeserializeWithLengthPrefix(ms, PrefixStyle.Base128,
tag => tag == 2 ? typeof(Bar) : null, out obj))
{
Console.WriteLine(((Bar)obj).B);
}
}
}
}
On the wire, this is actually compatible with a parent object:
repeated foo foo = 1;
repeated bar bar = 2;
If the proposed option generate_visitors
gets implemented you should be able to consume the same type of heterogenous data stream from any client. The obvious mapping would be something like an optional property on [ProtoContract]
to help with this - but I don't want to add that until the new protobuf feature is clear, as so far it looks like an exact match to my implementation. Which is nice.
精彩评论