RapidJSON

ライブラリシリアライゼーションJSONC++高速

ライブラリ

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