10

チェックしましたが、クラスをバイト配列に直接シリアライズし、その後、Marc Gravell の protobuf-net 実装を使用してバイト配列からデシリアライズする方法を確認できないようです。

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

更新された質問:ジェネリックを処理する必要がなく、代わりにコンストラクターを通過するときにリフレクションを通じてプロパティ「MessageBody」の型を推測する方法はありますか? オブジェクト型をシリアル化できないと思いますよね?現在のソリューションは、新しい Message をインスタンス化するたびに 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;
    }   
}

私がやりたいのは、次のようなものです。

Message newMsg = new Message("Producer", "Consumer", Foo); byte[] byteArray = newMsg.Serialize();

および Message msg = Message.Deserialize(byteArray);

(ここで、Deserialize は静的メソッドであり、常に Message 型のオブジェクトに逆シリアル化され、メッセージ本文を逆シリアル化する型を知る必要があるだけです)。

4

2 に答える 2

10

ここにはいくつかの異なる質問があるので、私が見ることができるものに答えます。

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

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

type: yes を含める必要があるという問題については、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; }
}

これは、コントラクト型のインスタンスを保持する任意の値に対して機能します (ただし、プリミティブではなく、理想的には継承を含まない)。

于 2012-04-14T13:06:59.047 に答える
5

OPが投稿したコードは私にとってはうまくいきません。以下は、Marc Gravellの提案をもう少し取り入れたわずかな適応です。メッセージからの継承は、「循環継承は許可されていません」を防ぐために必要であり、以下のコード コメントに記載されているように、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;
        }
      }

于 2014-01-28T20:56:24.703 に答える