C++ – JSON を扱うライブラリ RapidJSON の使い方

目次

概要

高速に動作する C++ の JSON の解析、生成ライブラリである RapidJSON について紹介します。

rapidjson の特徴

  • 仕様書の DOM や SAX の API をすべてサポートしている。
  • 非常に高速。C++ の Json ライブラリの比較結果を記載した C/C++ JSON parser/generator benchmark によると、2番目に早い。
  • ヘッダーオンリーライブラリなので、導入が容易。
  • 省メモリ。
  • UTF-8、UTF-16、UTF-32 をサポートしており、日本語を含む JSON も問題なく扱える。

インストール

ヘッダーオンリーライブラリなのでビルドは不要で、Tencent/rapidjson をクローンして、include ディレクトリを include パスに追加するだけです。 CMake でプロジェクト管理をしている場合、FetchContent モジュールを使って導入できます。

cmake_minimum_required(VERSION 3.1.1)
project(rapidjson VERSION 0.1.0)

# Fetch rapidjson
include(FetchContent)
FetchContent_Declare(
    rapidjson
    GIT_REPOSITORY https://github.com/Tencent/rapidjson.git
    GIT_TAG        v1.1.0
)
FetchContent_GetProperties(rapidjson)
if(NOT rapidjson_POPULATED)
  FetchContent_Populate(rapidjson)
endif()
include_directories(${rapidjson_SOURCE_DIR}/include)

add_executable(rapidjson main.cpp)

JSON の解析

JSON 文字列を解析する

JSON を表す const char*std::string を解析します。

#include <iostream>

#include "rapidjson/document.h"

using namespace rapidjson;

int main(int, char **)
{
    std::string json_str = "{\"hello\": \"world\", "
                           "\"t\": true, "
                           "\"f\": false, "
                           "\"n\": null, "
                           "\"i\": 123, "
                           "\"pi\": 3.1416, "
                           "\"a\": [1, 2, 3, 4]}";

    Document doc;
    doc.Parse(json_str.c_str());
}

JSON ファイルを解析する

JSON 形式のファイルを解析します。

#include <fstream>
#include <iostream>

#include "rapidjson/document.h"
#include "rapidjson/istreamwrapper.h"

using namespace rapidjson;

int main(int, char **)
{
    std::ifstream ifs("sample.json");
    IStreamWrapper isw(ifs);

    Document doc;
    doc.ParseStream(isw);
}

JSON の値の取得

JSON は Document オブジェクトによって表されます。JSON の各値は Value オブジェクトに格納されます。

値の型の判定

Value オブジェクトに格納されている値の判定は、Is から始まる名前のメソッド名で行います。

#include <iostream>

#include "rapidjson/document.h"

using namespace rapidjson;

int main(int, char **)
{
    std::string json_str = "{\"hello\": \"world\", "
                           "\"true\": true, "
                           "\"false\": false, "
                           "\"null\": null, "
                           "\"int\": -123, "
                           "\"uint\": 123, "
                           "\"int64\": -870744036720833075, "
                           "\"uint64\": 870744036720833075, "
                           "\"double\": 3.1416, "
                           "\"object\": {\"hoge\": 1}, "
                           "\"array\": [1, 2, 3, 4]}";

    Document doc;
    doc.Parse(json_str.c_str());

    // 型の判定
    std::cout << "IsString: " << doc["hello"].IsString() << std::endl; // 文字列かどうか
    std::cout << "IsNull: " << doc["null"].IsNull() << std::endl;      // null かどうか
    std::cout << "IsFalse: " << doc["false"].IsFalse() << std::endl;   // false かどうか
    std::cout << "IsTrue: " << doc["true"].IsTrue() << std::endl;      // true かどうか
    std::cout << "IsBool: " << doc["true"].IsBool() << std::endl;      // 真偽値かどうか
    std::cout << "IsObject: " << doc["object"].IsObject() << std::endl; // オブジェクトかどうか
    std::cout << "IsArray: " << doc["array"].IsArray() << std::endl; // 配列かどうか
    std::cout << "IsNumber: " << doc["int"].IsNumber() << std::endl; // 数値かどうか
    std::cout << "IsInt: " << doc["int"].IsInt() << std::endl; // int で表せる数値かどうか
    std::cout << "IsUint: " << doc["uint"].IsUint() << std::endl; // uint で表せる数値かどうか
    std::cout << "IsInt64: " << doc["int64"].IsInt64() << std::endl; // int64 で表せる数値かどうか
    std::cout << "IsUint64: " << doc["uint64"].IsUint64()
              << std::endl; // uint64 で表せる数値かどうか
    std::cout << "IsDouble: " << doc["double"].IsDouble() << std::endl; // 小数かどうか
}

値の取得

値の取得は Get から始まる名前のメソッド名で行います。

#include <iostream>

#include "rapidjson/document.h"

using namespace rapidjson;

int main(int, char **)
{
    std::string json_str = "{\"hello\": \"world\", "
                           "\"true\": true, "
                           "\"false\": false, "
                           "\"null\": null, "
                           "\"int\": -123, "
                           "\"uint\": 123, "
                           "\"int64\": -870744036720833075, "
                           "\"uint64\": 870744036720833075, "
                           "\"double\": 3.1416, "
                           "\"object\": {\"hoge\": 1}, "
                           "\"array\": [1, 2, 3, 4]}";

    Document doc;
    doc.Parse(json_str.c_str());

    // 真偽値
    std::cout << "GetBool: " << doc["true"].GetBool() << std::endl;

    // 文字列
    std::cout << "GetString: " << doc["hello"].GetString() << std::endl;

    // 数値
    std::cout << "GetInt: " << doc["int"].GetInt() << std::endl;
    std::cout << "GetUint: " << doc["uint"].GetUint() << std::endl;
    std::cout << "GetInt64: " << doc["int64"].GetInt64() << std::endl;
    std::cout << "GetUint64: " << doc["uint64"].GetUint64() << std::endl;
    std::cout << "GetDouble: " << doc["double"].GetDouble() << std::endl;

    // オブジェクト
    std::cout << "Object: " << doc["object"]["hoge"].IsInt() << std::endl;

    // 配列
    for (auto itr = doc["array"].Begin(); itr != doc["array"].End(); ++itr)
        std::cout << itr->GetInt() << " ";
    std::cout << std::endl;

    for (size_t i = 0; i < doc["array"].Size(); ++i)
        std::cout << doc["array"][i].GetInt() << " ";
    std::cout << std::endl;
}

値の設定

既存の値の変更 (真偽値、数値、文字列)

既存の Value オブジェクトの値を変更する場合は、Set から始まる名前のメソッド名で行います。

#include <iostream>

#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"

using namespace rapidjson;

int main(int, char **)
{
    std::string json_str = "{\"hello\": \"world\", "
                           "\"bool\": false, "
                           "\"int\": -123, "
                           "\"uint\": 123, "
                           "\"int64\": -870744036720833075, "
                           "\"uint64\": 870744036720833075, "
                           "\"double\": 3.1416}";

    Document doc;
    doc.Parse(json_str.c_str());

    // 真偽値
    doc["bool"].SetBool(true);

    // 文字列
    doc["hello"].SetString("Japan");

    // 数値
    doc["int"].SetInt(-7);
    doc["uint"].SetUint(7);
    doc["int64"].SetInt64(-570744036720833075);
    doc["uint64"].SetUint64(570744036720833075);
    doc["double"].SetDouble(1.57);

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);
    doc.Accept(writer);
    std::cout << buffer.GetString() << std::endl;
}

オブジェクトの編集

オブジェクト型の Value オブジェクトには AddMember() で子となる Value オブジェクトを追加できます。

#include <iostream>

#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"

using namespace rapidjson;

int main(int, char **)
{
    std::string json_str = "{\"hello\": \"world\"}";

    Document doc;
    doc.Parse(json_str.c_str());

    // オブジェクトに値を追加する。
    doc.AddMember("pi", 3.14, doc.GetAllocator());

    // 新規にオブジェクト型の Value を作成する。
    Value child(kObjectType);
    child.AddMember("hoge", 1, doc.GetAllocator());
    child.AddMember("fuga", 2, doc.GetAllocator());
    doc.AddMember("child", child, doc.GetAllocator());

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);
    doc.Accept(writer);
    std::cout << buffer.GetString() << std::endl;
    // {"hello":"world","pi":3.14,"child":{"hoge":1,"fuga":2}}
}

配列の編集

配列型の Value オブジェクトには PushBack() で値を追加できます。

#include <iostream>

#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"

using namespace rapidjson;

int main(int, char **)
{
    std::string json_str = "{\"array\": [1, 2]}";

    Document doc;
    doc.Parse(json_str.c_str());

    // オブジェクトに値を追加する。
    doc["array"].PushBack(3, doc.GetAllocator());
    doc["array"].PushBack(4, doc.GetAllocator());
    doc["array"].PushBack(5, doc.GetAllocator());

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);
    doc.Accept(writer);
    std::cout << buffer.GetString() << std::endl;
    // {"array":[1,2,3,4,5]}
}

JSON の出力

JSON の出力は、出力先を指定した Writer オブジェクトを作成し、Document.Accept() で Document オブジェクトに紐付けます。 buffer.GetString() で JSON の出力が必要となったタイミングで、紐付けられた Writer オブジェクトで実際に出力が生成されます。

文字列として出力する

文字列として出力する場合、Writer の出力先に StringBuffer を指定します。

#include <iostream>

#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"

using namespace rapidjson;

int main(int, char **)
{
    Document doc(kObjectType);
    doc.AddMember("pi", 3.14, doc.GetAllocator());
    doc.AddMember("hello", "world", doc.GetAllocator());

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::cout << buffer.GetString() << std::endl;
    // {"pi":3.14,"hello":"world"}
}

ファイルに出力する

ファイルに出力する場合、Writer の出力先に OStreamWrapper を指定します。

#include <fstream>

#include "rapidjson/document.h"
#include "rapidjson/ostreamwrapper.h"
#include "rapidjson/writer.h"

using namespace rapidjson;

int main(int, char **)
{
    Document doc(kObjectType);
    doc.AddMember("pi", 3.14, doc.GetAllocator());
    doc.AddMember("hello", "world", doc.GetAllocator());

    std::ofstream ofs("output.json");
    OStreamWrapper osw(ofs);

    Writer<OStreamWrapper> writer(osw);
    doc.Accept(writer);
}

コメント

コメントする

目次