概要
高速に動作する 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);
}
コメント