web-dev-qa-db-ja.com

Protobuf-netのシリアル化/逆シリアル化

確認しましたが、クラスをバイト配列に直接シリアル化し、その後、MarcGravellのprotobuf-net実装を使用してバイト配列から逆シリアル化する方法がわからないようです。

編集:ストリームを経由せずにbyte []に​​シリアル化する方法の元の質問は確かに些細なものだったので、質問を変更してコードを提供しました。謝罪いたします。

更新された質問:ジェネリックスを処理する必要がなく、代わりにコンストラクターを通過するときにリフレクションを通じてプロパティ「MessageBody」のタイプを推測する方法はありますか?オブジェクトタイプをシリアル化できないと思いますよね?現在のソリューションは、新しいメッセージをインスタンス化するたびにMessageBodyのタイプを渡す必要があるという点で、非常に面倒に見えます。これに対するより洗練された解決策はありますか?

私は次のことを思いついた:

class Program
{
    static void Main(string[] args)
    {
        Message<string> msg = new Message<string>("Producer", "Consumer", "Test Message");

        byte[] byteArray = msg.Serialize();
        Message<string> message = Message<string>.Deserialize(byteArray);

        Console.WriteLine("Output");
        Console.WriteLine(message.From);
        Console.WriteLine(message.To);
        Console.WriteLine(message.MessageBody);

        Console.ReadLine();

    }
}

[ProtoContract]
public class Message<T>
{
    [ProtoMember(1)]
    public string From { get; private set; }
    [ProtoMember(2)]
    public string To { get; private set; }
    [ProtoMember(3)]
    public T MessageBody { get; private set; }

    public Message()
    {

    }

    public Message(string from, string to, T messageBody)
    {
        this.From = from;
        this.To = to;
        this.MessageBody = messageBody;
    }

    public byte[] Serialize()
    {
        byte[] msgOut;

        using (var stream = new MemoryStream())
        {
            Serializer.Serialize(stream, this);
            msgOut = stream.GetBuffer();
        }

        return msgOut;
    }

    public static Message<T> Deserialize(byte[] message)
    {
        Message<T> msgOut;

        using (var stream = new MemoryStream(message))
        {
            msgOut = Serializer.Deserialize<Message<T>>(stream);
        }

        return msgOut;
    }   
}

私が行きたいのは次のようなものです:

メッセージnewMsg = new Message( "Producer"、 "Consumer"、Foo); byte [] byteArray = newMsg.Serialize();

およびメッセージmsg = Message.Deserialize(byteArray);

(ここで、Deserializeは静的メソッドであり、常にメッセージタイプのオブジェクトにデシリアライズされ、メッセージ本文をデシリアライズするタイプを知る必要があるだけです)。

10
Matt

ここにはいくつかの異なる質問があるので、私が見ることができるものに答えます:私が何かを逃したならば、ただ私に知らせてください。

まず、前述のように、MemoryStreamはbyte []に​​到達するための最も一般的な方法です。これは、ほとんどのシリアライザーと一致しています。たとえば、XmlSerializer、BinaryFormatter、DataContractSerializeralsoには、「as a byte [] overload」はありませんが、 MemoryStreamを受け入れます。

ジェネリック:ジェネリックを使用する必要はありません。 v1にはSerializer.NonGenericがあり、これをラップします。 v2では、「コア」は非汎用であり、RuntimeTypeModel.Defaultを介してアクセスできます。もちろん、SerializerとSerializer.NonGenericは引き続き機能します。

タイプを含める必要があるという問題について:はい、protobuf仕様は、受信者が与えられているデータのタイプを知っていることを前提としています。ここでの簡単なオプションは、データの複数の型指定されたプロパティ(そのうちの1つだけがnull以外)を持つ単純なラッパーオブジェクトを「ルート」オブジェクトとして使用することです。別のオプションは、ProtoIncludeを介した組み込みの継承サポートから生まれる可能性があります(注:実装の詳細として、これら2つのアプローチは同じです)。

あなたの特定の例では、おそらく次のことを考慮してください。

[ProtoContract]
[ProtoInclude(1, typeof(Message<Foo>))]
.... More as needed
[ProtoInclude(8, typeof(Message<Bar>))]
public abstract class Message
{   }
[ProtoContract]
public class Message<T> : Message
{
    ...
}

次に、<Message>でシリアル化します。APIは適切なタイプを自動的に作成します。

最近のビルドでは、タイプデータを含むDynamicTypeオプションもあります。次に例を示します。

[ProtoContract]
public class MyRoot {
    [ProtoMember(1, DynamicType=true)]
    public object Value { get; set; }
}

これは、コントラクトタイプのインスタンスを保持するすべての値に対して機能します(ただし、プリミティブに対しては機能せず、理想的には継承を含みません)。

9
Marc Gravell

OPが投稿したコードは、私にはまったく機能しません。以下は、MarcGravellの提案をもう少し取り入れたわずかな適応です。 「循環継承は許可されていません」を防ぐためにメッセージからの継承が必要であり、以下のコードコメントに記載されているように、GetBufferも機能していませんでした。

それが他の誰かに役立つことを願って、それをすべて機能させるのに私は良い数時間かかりました...



      [ProtoContract]
      public abstract class Message
      {
        public byte[] Serialize()
        {
          byte[] result;
          using (var stream = new MemoryStream())
          {
            Serializer.Serialize(stream, this);
            result = stream.ToArray(); //GetBuffer was giving me a Protobuf.ProtoException of "Invalid field in source data: 0" when deserializing
          }
          return result;
        }
      }

      [ProtoContract]
      public class Message : Message
      {
        [ProtoMember(1)]
        public string From { get; private set; }
        [ProtoMember(2)]
        public string To { get; private set; }
        [ProtoMember(3)]
        public T MessageBody { get; private set; }

        public Message()
        { }

        public Message(string from, string to, T messageBody)
        {
          this.From = from;
          this.To = to;
          this.MessageBody = messageBody;
        }

        public static Message Deserialize(byte[] message)
        {
          Message result;
          using (var stream = new MemoryStream(message))
          {
            result = Serializer.Deserialize>(stream);
          }
          return result;
        }
      }

4
9swampy