LoginSignup
7
7

More than 5 years have passed since last update.

JSONの代わりにMessagePackを使いたい

Posted at

今までCLI/SV間のデータのやり取りにはJSONを利用してきましたが、調べてみるとMessagePackの方が利点が多そうなので動作の確認をしました

データ構造の定義について

あらかじめstructを全て定義しておく必要があるようです
structの入れ子構造にも対応しています
MSGPACK_DEFINEというマクロでシリアライズしたいフィールドを定義します

struct sFoo
{
    std::string name;
    int secret;
    MSGPACK_DEFINE( name );
};

変換も簡単

serialize

auto origin = std::make_unique<sFoo>();
sTest->name = "foo";
msgpack::sbuffer sbuf;
msgpack::pack( sbuf, *( origin.get() ) );

deserialize

std::string msg = // 受け取ったconst char*
msgpack::object_handle oh = msgpack::unpack( msg.c_str(), msg.size() );
msgpack::object deserialized = oh.get();

auto clone = std::make_unique<sFoo>();
deserialized.convert( *( clone.get() ) );

unique_ptrもしっかりコピーしてくれるし、nullptr(nullableなフィールドも定義可能)にも対応してます

データの中身を参照する

std::stringstream ss;
ss << deserialized;
// 出力は [["child1"],[4,[2,4,6,8]],null] のようになる

公式ではstd::coutに渡していましたが、Stream write(const char*, size_t s)を持つ型ならなんでもいいそう
JSONのpritty formatに比べるとインデント/改行やキーがないのでデータが大きくなると結構見づらい

変換の成否判定

try
{
    auto failed = std::make_unique<sBar>();
    deserialized.convert( *( failed.get() ) );
}
catch ( const std::bad_cast& e )
{
    // FAILED!!
}

変換できなかったときstd::bad_castエラーを吐きます
JSONと違ってメンバーチェック等は必要ないし、Import/Export関数も書かなくてよい

確認したコード

msgpack-cのUsageに沿って確認

msgpacktest.cpp
#include <msgpack.hpp>

struct sChild1
{
    std::string name;
    MSGPACK_DEFINE( name );
};

struct sChild2
{
    int num;
    std::vector<int> nums;
    MSGPACK_DEFINE( num, nums );
};

struct sParent
{
    std::unique_ptr<sChild1> child1;
    std::unique_ptr<sChild2> child2;
    std::unique_ptr<sChild2> nullable;
    MSGPACK_DEFINE( child1, child2, nullable );
};

int main()
{
    auto origin = std::make_unique<sParent>();
    origin->child1 = std::make_unique<sChild1>();
    origin->child2 = std::make_unique<sChild2>();
    origin->nullable = nullptr;

    origin->child1->name = "child1";
    origin->child2->num = 4;
    origin->child2->nums = { 2, 4, 6, 8 };

    // serialize
    msgpack::sbuffer sbuf;
    msgpack::pack( sbuf, *( origin.get() ) );

    // 元のEntityを削除してみる
    origin = nullptr;

    // deserialize
    msgpack::object_handle oh = msgpack::unpack( sbuf.data(), sbuf.size() );
    msgpack::object deserialized = oh.get();

    auto clone = std::make_unique<sParent>();
    deserialized.convert( *( clone.get() ) );

    if ( clone->nullable == nullptr )
    {
        Log::Console( "nullable is null." );
    }
    Log::Console( "child1 %s, child2 num %u", clone->child1->name.c_str(), clone->child2->num );

    std::stringstream ss;
    ss << deserialized;

    Log::Console( ss.str().c_str() );

    try
    {
        auto failed = std::make_unique<sChild1>();
        deserialized.convert( *( failed.get() ) );
    }
    catch ( const std::bad_cast& e )
    {
        Log::Console( e.what() );
    }

    return 0;
}
nullable is null.
child1 child1, child2 num 4
[["child1"],[4,[2,4,6,8]],null]
std::bad_cast
7
7
0

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
7
7