RapidJSON
ライブラリ
RapidJSON
概要
RapidJSONは、C++用の高速なJSONパーサー/ジェネレーターライブラリです。SAX(Simple API for XML)とDOM(Document Object Model)の両方のAPIスタイルをサポートし、非常に高いパフォーマンスと最小限のメモリフットプリントを実現しています。ヘッダーオンリーライブラリとして提供され、外部依存関係がないため、プロジェクトへの統合が非常に簡単です。
詳細
RapidJSONは、その名前が示すとおり、極めて高速なJSON処理を目的として設計されています。SIMD(Single Instruction, Multiple Data)命令セットのサポートにより、SSE2、SSE4.2、ARM Neonなどのプロセッサ機能を活用して、パフォーマンスを最大化します。
主な特徴:
- 高速性: 業界トップクラスのパフォーマンスを誇り、多くのベンチマークで他のJSON ライブラリを上回る
- メモリ効率: 最小限のメモリアロケーションで動作し、In-situパーシングによりメモリ使用量をさらに削減可能
- 完全なJSON仕様準拠: RFC 7159に完全準拠し、UTF-8、UTF-16、UTF-32エンコーディングをサポート
- 柔軟なAPI: DOM APIによる使いやすさと、SAX APIによる高度な制御の両方を提供
- エラーハンドリング: 詳細なエラー情報とエラー位置の報告
- JSONポインター: RFC 6901準拠のJSONポインターによる効率的なデータアクセス
- スキーマ検証: JSON Schemaによるデータ検証機能
メリット・デメリット
メリット
- 業界最高水準のパフォーマンス: 多くのベンチマークで最速クラスの処理速度を記録
- ヘッダーオンリー: 複雑なビルド設定が不要で、プロジェクトへの統合が簡単
- 外部依存なし: 他のライブラリに依存せず、単独で動作
- 豊富な機能: DOM/SAX API、JSONポインター、スキーマ検証など包括的な機能セット
- プラットフォーム非依存: Windows、Linux、macOS、各種組み込みシステムで動作
- 充実したドキュメント: 詳細なドキュメントとサンプルコードが豊富
- アクティブな開発: Tencentがメンテナンスし、継続的に改善
デメリット
- C++11必須: 古いコンパイラでは使用できない(最低でもGCC 4.7.3、Clang 3.3、MSVC 2013が必要)
- JSONのみ: XMLやYAMLなど他のフォーマットはサポートしない
- 学習曲線: 高度な機能(SAX API、カスタムアロケータなど)の習得には時間が必要
- コンパイル時間: ヘッダーオンリーライブラリのため、大規模プロジェクトではコンパイル時間が増加する可能性
参考ページ
書き方の例
基本的なJSONパース(DOM API)
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
int main() {
// 1. JSON文字列をDOMにパース
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document d;
d.Parse(json);
// 2. DOMを使って値を変更
Value& s = d["stars"];
s.SetInt(s.GetInt() + 1);
// 3. DOMを文字列化
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
// 出力: {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl;
return 0;
}
JSONオブジェクトとarray配列の作成
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
int main() {
Document d;
d.SetObject();
Document::AllocatorType& allocator = d.GetAllocator();
// 文字列を追加
d.AddMember("name", "RapidJSON", allocator);
// 数値を追加
d.AddMember("version", 1.1, allocator);
// 配列を作成して追加
Value features(kArrayType);
features.PushBack("fast", allocator);
features.PushBack("efficient", allocator);
features.PushBack("header-only", allocator);
d.AddMember("features", features, allocator);
// JSON文字列に変換
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
std::cout << buffer.GetString() << std::endl;
return 0;
}
SAX APIを使った効率的なパース
#include "rapidjson/reader.h"
#include <iostream>
using namespace rapidjson;
// SAXハンドラーの実装
struct MyHandler : public BaseReaderHandler<UTF8<>, MyHandler> {
bool Null() { std::cout << "Null()" << std::endl; return true; }
bool Bool(bool b) { std::cout << "Bool(" << std::boolalpha << b << ")" << std::endl; return true; }
bool Int(int i) { std::cout << "Int(" << i << ")" << std::endl; return true; }
bool Uint(unsigned u) { std::cout << "Uint(" << u << ")" << std::endl; return true; }
bool Int64(int64_t i) { std::cout << "Int64(" << i << ")" << std::endl; return true; }
bool Uint64(uint64_t u) { std::cout << "Uint64(" << u << ")" << std::endl; return true; }
bool Double(double d) { std::cout << "Double(" << d << ")" << std::endl; return true; }
bool String(const char* str, SizeType length, bool copy) {
std::cout << "String(" << str << ", " << length << ", " << std::boolalpha << copy << ")" << std::endl;
return true;
}
bool StartObject() { std::cout << "StartObject()" << std::endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
std::cout << "Key(" << str << ", " << length << ", " << std::boolalpha << copy << ")" << std::endl;
return true;
}
bool EndObject(SizeType memberCount) { std::cout << "EndObject(" << memberCount << ")" << std::endl; return true; }
bool StartArray() { std::cout << "StartArray()" << std::endl; return true; }
bool EndArray(SizeType elementCount) { std::cout << "EndArray(" << elementCount << ")" << std::endl; return true; }
};
int main() {
const char* json = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}";
MyHandler handler;
Reader reader;
StringStream ss(json);
reader.Parse(ss, handler);
return 0;
}
ファイルからのJSON読み込み
#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h"
#include <cstdio>
using namespace rapidjson;
int main() {
FILE* fp = fopen("config.json", "r");
if (!fp) {
std::cerr << "Failed to open file" << std::endl;
return 1;
}
char readBuffer[65536];
FileReadStream is(fp, readBuffer, sizeof(readBuffer));
Document d;
d.ParseStream(is);
fclose(fp);
// JSONデータへのアクセス
if (d.HasMember("database")) {
const Value& db = d["database"];
if (db.HasMember("host") && db["host"].IsString()) {
std::cout << "Database host: " << db["host"].GetString() << std::endl;
}
}
return 0;
}
エラーハンドリング
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
int main() {
const char* json = "{\"hello\": world}"; // 不正なJSON(worldが引用符で囲まれていない)
Document d;
if (d.Parse(json).HasParseError()) {
fprintf(stderr, "\nError(offset %u): %s\n",
(unsigned)d.GetErrorOffset(),
GetParseError_En(d.GetParseError()));
return 1;
}
return 0;
}
JSONポインターの使用
#include "rapidjson/document.h"
#include "rapidjson/pointer.h"
int main() {
Document d;
d.Parse("{\"users\":[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]}");
// JSONポインターを使ってアクセス
Value* alice_age = Pointer("/users/0/age").Get(d);
if (alice_age && alice_age->IsInt()) {
std::cout << "Alice's age: " << alice_age->GetInt() << std::endl;
}
// JSONポインターを使って値を設定
Pointer("/users/1/age").Set(d, 26);
// 新しいパスを作成
Pointer("/users/2/name").Create(d).SetString("Charlie");
Pointer("/users/2/age").Create(d).SetInt(35);
return 0;
}