4
5

More than 3 years have passed since last update.

C# でMessagePackを使ってみた

Last updated at Posted at 2021-05-18

まえがき

MessagePack-CSharp(neuecc氏作)を使ってみたときに「はまったこと」「わかったこと」を記述していく

環境

IDE:VisualStudio2019
フレームワーク:.NET 5.0

基本的な使い方

using MessagePack;
using System;

namespace MessagePackTest
{
    public class Program
    {
        // シリアル化させるオブジェクトには、MessagePackObject属性を付ける。
        // 引数をtrueにするとプロパティの名前がそのまま使用される。
        [MessagePackObject(true)]
        public class MyClass
        {
            public string FirstName { get; set; } = "Hoge";
            public string LastName { get; set; } = "Piyo";
            // シリアル化させたくない場合には、[IgnoreMember]を付ける。
            [IgnoreMember]
            public string FullName { get { return FirstName + LastName; } }
        }

        static void Main()
        {
            var myClass1 = new MyClass();
            // MessagePack形式のバイト配列に変換
            byte[] bytes = MessagePackSerializer.Serialize(myClass1);
            // 復元
            MyClass myClass2 = MessagePackSerializer.Deserialize<MyClass>(bytes);

            // JSONにも変換できる
            var json = MessagePackSerializer.ConvertToJson(bytes);
            Console.WriteLine(json);
        }
    }
}

出力結果

{"FirstName":"Hoge","LastName":"Piyo"}

基本的な使い方2

[Key(0)]のように数値を渡すと結果がArray形式になる

~略~
        // シリアル化させるオブジェクトには、MessagePackObject属性を付ける。
        // 引数をfalseにすると[Key()]を各プロパティに付ける必要がある。
        [MessagePackObject(false)]
        public class MyClass
        {
            [Key(0)]
            public string FirstName { get; set; } = "Hoge";
            [Key(1)]
            public string LastName { get; set; } = "Piyo";
            // シリアル化させたくない場合には、[IgnoreMember]を付ける。
            [IgnoreMember]
            public string FullName { get { return FirstName + LastName; } }
        }
~略~

出力結果

["Hoge","Piyo"]

基本的な使い方3

[Key("FN")]のように文字列を渡すとMap形式になる

~略~
        // シリアル化させるオブジェクトには、MessagePackObject属性を付ける。
        // 引数をfalseにすると[Key()]を各プロパティに付ける必要がある。
        [MessagePackObject(false)]
        public class MyClass
        {
            [Key("FN")]
            public string FirstName { get; set; } = "Hoge";
            [Key("LN")]
            public string LastName { get; set; } = "Piyo";
            // シリアル化させたくない場合には、[IgnoreMember]を付ける。
            [IgnoreMember]
            public string FullName { get { return FirstName + LastName; } }
        }
~略~
{"FN":"Hoge","LN":"Piyo"}

エラー例1

using MessagePack;
using System;

namespace MessagePackTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var mc = new MyClass
            {
                Age = 99,
                FirstName = "hoge",
                LastName = "huga",
            };
            byte[] bytes = MessagePackSerializer.Serialize(mc);
            MyClass mc2 = MessagePackSerializer.Deserialize<MyClass>(bytes);

            var json = MessagePackSerializer.ConvertToJson(bytes);
            Console.WriteLine(json);
        }

        [MessagePackObject]
        public class MyClass
        {
            [Key(0)]
            public int Age { get; set; }
            [Key(1)]
            public string FirstName { get; set; }
            [Key(2)]
            public string LastName { get; set; }
            [IgnoreMember]
            public string FullName { get { return FirstName + LastName; } }
        }
    }
}

上記コードを実行すると、最初の Serialize 関数で以下の例外が発生する。

~略~
内部例外 1:
TypeInitializationException: The type initializer for 'FormatterCache`1' threw an exception.
内部例外 2:
TypeLoadException: Type 'MessagePack.Formatters.MessagePackTest_Program\+MyClassFormatter1' from assembly 'MessagePack.Resolvers.DynamicObjectResolver, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is attempting to implement an inaccessible interface.

「アクセスできないインターフェイスウンヌンカンヌン」とあるので
class Program が public じゃないのが悪かった。
MessagePack 自体の問題ではないけど、VisualStudioで自動生成された部分に間違いがあると思えずはまった。

継承したクラスの場合1

特に問題なく動作

using MessagePack;
using System;

namespace MessagePackTest
{
    public class Program
    {
        static void Main(string[] args)
        {
            // {"Hoge":1,"Foo":10}
            Console.WriteLine(MessagePackSerializer.SerializeToJson(new Sample4 { Foo = 10, Bar = 20, Hoge = 1 }));
        }

        [MessagePackObject(keyAsPropertyName: true)]
        public class Sample4 : Base
        {
            public int Foo { get; set; }
            [IgnoreMember]
            public int Bar { get; set; }
        }

        [MessagePackObject(keyAsPropertyName: true)]
        public class Base
        {
            public int Hoge { get; set; }
        }
    }
}

継承したクラスの場合2

  • 上記のBaseのkeyAsPropertyNameをfalseにした場合

keyAsPropertyNameがtrueのときと同じ結果となった

他の部分略
        [MessagePackObject(keyAsPropertyName: true)]
        public class Sample4 : Base
        {
            public int Foo { get; set; }
            [IgnoreMember]
            public int Bar { get; set; }
        }

        [MessagePackObject]
        public class Base
        {
            [Key(0)]
            public int Hoge { get; set; }
        }

継承したクラスの場合3

  • 継承したクラスの場合1のSample4 のkeyAsPropertyNameをfalseにした場合

ビルドが失敗する(エラー内容は以下の出力)
BaseはkeyAsPropertyName=falseのつもりでも、継承先が優先されるため、[Key()]か[IgnoreMember]をつけろと怒られる

  • Baseに[MessagePackObject]を付けなくても同様
他の部分略
        [MessagePackObject]
        public class Sample4 : Base
        {
            [Key(0)]
            public int Foo { get; set; }
            [IgnoreMember]
            public int Bar { get; set; }
        }

        [MessagePackObject(keyAsPropertyName: true)]
        public class Base
        {
            public int Hoge { get; set; }
        }

出力

ビルドを開始しました...
1>------ ビルド開始: プロジェクト: MessagePackTest, 構成: Debug Any CPU ------
1>D:\Develop\TestProjects\MessagePackTest\MessagePackTest\Program.cs(138,24,138,28): error MsgPack004: Public members of MessagePackObject-attributed types require either KeyAttribute or IgnoreMemberAttribute. Sample4.Hoge.
1>プロジェクト "MessagePackTest.csproj" のビルドが終了しました -- 失敗。
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========

継承したクラスの場合4

  • 継承したクラスの場合1のBaseにMessagePackObjectが無い場合

継承したクラスの場合1と同様

他の部分略
        [MessagePackObject(keyAsPropertyName:true)]
        public class Sample4 : Base
        {
            public int Foo { get; set; }
            [IgnoreMember]
            public int Bar { get; set; }
        }

        public class Base
        {
            public int Hoge { get; set; }
        }
4
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5