How can I send multiple types of objects across Protobuf?

前端 未结 2 1698
轮回少年
轮回少年 2020-12-30 09:36

I\'m implementing a client-server application, and am looking into various ways to serialize and transmit data. I began working with Xml Serializers, which worked rather wel

2条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-30 10:28

    This functionality is actually built in, albeit not obviously.

    In this scenario, it is anticipated that you would designate a unique number per message type. The overload you are using passes them all in as "field 1", but there is an overload that lets you include this extra header information (it is still the job of the calling code to decide how to map numbers to types, though). You can then specify different types as different fields is the stream (note: this only works with the base-128 prefix style).

    I'll need to double check, but the intention is that something like the following should work:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using ProtoBuf;
    static class Program
    {
        static void Main()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                WriteNext(ms, 123);
                WriteNext(ms, new Person { Name = "Fred" });
                WriteNext(ms, "abc");
    
                ms.Position = 0;
    
                while (ReadNext(ms)) { }            
            }
        }
        // *** you need some mechanism to map types to fields
        static readonly IDictionary typeLookup = new Dictionary
        {
            {1, typeof(int)}, {2, typeof(Person)}, {3, typeof(string)}
        };
        static void WriteNext(Stream stream, object obj) {
            Type type = obj.GetType();
            int field = typeLookup.Single(pair => pair.Value == type).Key;
            Serializer.NonGeneric.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Base128, field);
        }
        static bool ReadNext(Stream stream)
        {
            object obj;
            if (Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128, field => typeLookup[field], out obj))
            {
                Console.WriteLine(obj);
                return true;
            }
            return false;
        }
    }
    [ProtoContract] class Person {
        [ProtoMember(1)]public string Name { get; set; }
        public override string ToString() { return "Person: " + Name; }
    }
    

    Note that this doesn't currently work in the v2 build (since the "WithLengthPrefix" code is incomplete), but I'll go and test it on v1. If it works, I'll all the above scenario to the test suite to ensure it does work in v2.

    Edit:

    yes, it does work fine on "v1", with output:

    123
    Person: Fred
    abc
    

提交回复
热议问题