C#でMessagePackを使う
C++で書いたアプリとC#で書いたアプリの間でMessagePackを使ってデータをやりとりできないかな、とやってます。C++の方はなんとか使い方を覚えましたが、C#の方はC#自体ここ一ヶ月くらいしか経験がないせいもあって苦労してます。どこかに質問しようにも頭の中を整理しないと始まらないので、ここにメモします。
参考
- Format specification - MessagePack - Confluence ... シリアライズ結果のフォーマットが説明されています。
- 主に技術日記: MessagePack for CLI ... C#版MessagePackの作者による紹介です。
C++でMessagePackを使う
まずは勝手知ったるC++から。ドキュメントやソースコードを読みながら使い方を覚えました。
#include <string> #include <msgpack.hpp> struct Foo { std::string str; MSGPACK_DEFINE(str); }; int main() { Foo foo; foo.str = "Hello"; msgpack::sbuffer sbuf; msgpack::pack(sbuf, foo); // 91 a5 48 65 6c 6c 6f と変換される return 0; }
ユーザ定義クラスはMSGPACK_DEFINEマクロで指定した変数が並んだ配列としてシリアライズされます。Rubyでいうところの["Hello"]
と同等の状態ですね。MSGPACK_DEFINEマクロでpackとunpack両方の仕組みが自動的に定義されて便利です。
C#でMessagePackを使う(1)
最初はVisual Studio 2012のNuGet経由でインストールしたところ、これは二代目の実装だそうです。バージョンは0.1.0.2011042300。以下は説明のためのコードです。
using MsgPack; // 中略 class Foo { string str; } // 中略 Foo foo = new Foo { str = "Hello }; var packer = new CompiledPacker(true); byte[] msg = packer.Pack<Foo>(foo); // 81 a3 73 74 72 a5 48 65 6c 6c 6f と変換される
ユーザ定義クラスのオブジェクトはmapとしてシリアライズされています。これはC++でのシリアライズ結果と異なるため、両者に互換性がないことになります。
C#でMessagePackを使う(2)
次に三代目の実装(MessagePack for CLI)を使います。バージョンは0.3-beta2。こちらはデフォルトで配列としてシリアライズされるそうなのでC++での結果と互換性がありそうです。
using System.IO; using Msgpack.Serialization; // 中略 // (Fooクラスは上記を参照) MemoryStream stream = new MemoryStream; var serializer = MessagePackSerializer.Create<Foo>(); // ここで例外 System.TypeLoadException が発生する serializer.Pack(stream, foo); stream.Position = 0; byte[] msg = new byte[256]; stream.Read(buf, 0, (int)stream.Length);
一応上記のように実装はしてみましたが、例外System.TypeLoadExceptionが発生してしまいます。
というところで今日は時間切れです。