RapidJSON для C++: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

RapidJSON для C++: полное руководство с примерами

RapidJSON — это высокопроизводительная библиотека для парсинга и генерации JSON на языке C++. Разработанная Мило Ип (Milo Yip) и распространяемая под лицензией MIT, она завоевала популярность благодаря своей скорости, минимальному потреблению памяти и полному соответствию стандарту ECMA-404 (RFC 7159). В отличие от многих других библиотек, RapidJSON не требует внешних зависимостей и может работать в окружениях с ограниченными ресурсами, включая встраиваемые системы.

Зачем нужна эта библиотека? JSON (JavaScript Object Notation) стал де-факто стандартом для обмена данными между веб-сервисами, конфигурационными файлами и хранением структурированных данных. В C++, где производительность критична, использование медленного парсера может стать узким местом. RapidJSON решает эту проблему: он способен обрабатывать мегабайты JSON за миллисекунды, используя SAX-подобный (потоковый) API для минимизации аллокаций и DOM-API для удобной работы с деревом объектов. Это делает его идеальным выбором для высоконагруженных серверов, игровых движков и приложений реального времени.

Библиотека поддерживает как полный разбор JSON (с проверкой корректности), так и "нечувствительный" режим для ускорения. Она умеет работать с UTF-8, UTF-16 и UTF-32, а также предоставляет удобные итераторы и функции для поиска. RapidJSON активно используется в таких проектах, как MongoDB, Tencent и многих open-source решениях.

Установка

RapidJSON — это header-only библиотека (за исключением опциональных файлов для сборки). Это значит, что для её использования достаточно скопировать заголовочные файлы в ваш проект и подключить их. Однако для удобства мы рассмотрим три основных способа установки.

Способ 1: Клонирование репозитория (рекомендуется)

Склонируйте репозиторий с GitHub и скопируйте папку include/rapidjson в ваш проект.

git clone https://github.com/Tencent/rapidjson.git
# Затем скопируйте папку include/rapidjson в ваш проект

Способ 2: Через пакетный менеджер vcpkg

Если вы используете vcpkg, установка выполняется одной командой:

vcpkg install rapidjson

Способ 3: Через Conan

Для пользователей Conan (C/C++ package manager):

conan install rapidjson/1.1.0@

Подключение в коде

После установки добавьте в ваш исходный файл:

#include "rapidjson/document.h"     // для DOM API
#include "rapidjson/writer.h"       // для записи JSON
#include "rapidjson/stringbuffer.h" // для буферизации вывода
#include "rapidjson/prettywriter.h" // для форматированного вывода
#include "rapidjson/reader.h"       // для SAX API
#include "rapidjson/error/en.h"     // для обработки ошибок

Быстрый старт: минимальный рабочий пример

Давайте напишем простейшую программу, которая парсит JSON-строку, извлекает значение и выводит его на экран.

#include <iostream>
#include "rapidjson/document.h"

int main() {
    // 1. JSON-строка для парсинга
    const char* json = "{\"project\":\"RapidJSON\",\"version\":1.0}";

    // 2. Создаём DOM-документ
    rapidjson::Document doc;
    doc.Parse(json);

    // 3. Проверяем, успешен ли парсинг
    if (doc.HasParseError()) {
        std::cerr << "Ошибка парсинга: " 
                  << rapidjson::GetParseError_En(doc.GetParseError()) 
                  << std::endl;
        return 1;
    }

    // 4. Извлекаем значения по ключам
    const std::string& project = doc["project"].GetString();
    double version = doc["version"].GetDouble();

    // 5. Выводим результат
    std::cout << "Проект: " << project << std::endl;
    std::cout << "Версия: " << version << std::endl;

    return 0;
}

Этот пример показывает базовый цикл работы с RapidJSON: создание документа, парсинг, проверка ошибок и доступ к данным. Обратите внимание, что doc["key"] возвращает ссылку на Value, а методы GetString(), GetDouble() и т.д. извлекают типизированные данные.

Основные методы, функции и классы

RapidJSON предоставляет два основных API: DOM (Document Object Model) для удобной работы с деревом и SAX (Simple API for XML) для потоковой обработки. Ниже мы рассмотрим 8 ключевых компонентов.

1. Класс rapidjson::Document

Сигнатура: class Document : public Value { ... };

Что делает: Представляет корневой узел JSON-документа. Наследует все методы класса Value и добавляет методы для парсинга и управления памятью.

Пример:

rapidjson::Document doc;
doc.Parse(json_string);
if (doc.IsObject()) {
    // работаем с объектом
}

2. Метод Document::Parse()

Сигнатура: ParseResult Parse(const Ch* str);

Что делает: Парсит JSON-строку и строит DOM-дерево. Возвращает ParseResult, который можно проверить на ошибки.

Пример:

rapidjson::Document doc;
doc.Parse("[1, 2, 3]");
if (doc.HasParseError()) {
    std::cout << "Ошибка на позиции " << doc.GetErrorOffset() << std::endl;
} else {
    std::cout << "Массив размером: " << doc.Size() << std::endl;
}

3. Метод Value::GetString()

Сигнатура: const Ch* GetString() const;

Что делает: Возвращает строковое значение узла. Узел должен быть строкой (IsString() == true).

Пример:

rapidjson::Document doc;
doc.Parse("{\"name\": \"Alice\"}");
const char* name = doc["name"].GetString();
std::cout << "Name: " << name << std::endl; // Вывод: Name: Alice

4. Метод Value::GetInt() / GetDouble() / GetBool()

Сигнатура: int GetInt() const; / double GetDouble() const; / bool GetBool() const;

Что делает: Извлекают числовое или булево значение. Для чисел RapidJSON автоматически определяет тип (int, unsigned int, int64_t, double).

Пример:

rapidjson::Document doc;
doc.Parse("{\"age\": 30, \"pi\": 3.14, \"active\": true}");
int age = doc["age"].GetInt();
double pi = doc["pi"].GetDouble();
bool active = doc["active"].GetBool();
std::cout << age << " " << pi << " " << active << std::endl;

5. Метод Value::HasMember()

Сигнатура: bool HasMember(const Ch* key) const;

Что делает: Проверяет, существует ли ключ в объекте. Позволяет избежать ошибок доступа к несуществующим полям.

Пример:

rapidjson::Document doc;
doc.Parse("{\"name\": \"Bob\"}");
if (doc.HasMember("age")) {
    std::cout << "Age: " << doc["age"].GetInt();
} else {
    std::cout << "Поле age отсутствует";
}

6. Класс rapidjson::Writer

Сигнатура: template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, unsigned StackLevel> class Writer;

Что делает: Позволяет генерировать JSON-строку в потоковом режиме (SAX-стиль). Эффективнее, чем создание DOM и его сериализация.

Пример:

rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
writer.StartObject();
writer.Key("name");
writer.String("Charlie");
writer.Key("age");
writer.Int(25);
writer.EndObject();
std::cout << buffer.GetString() << std::endl; // {"name":"Charlie","age":25}

7. Класс rapidjson::PrettyWriter

Сигнатура: class PrettyWriter : public Writer { ... };

Что делает: Наследует Writer, но добавляет форматирование (отступы, переносы строк) для читаемого вывода.

Пример:

rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
writer.StartObject();
writer.Key("data");
writer.StartArray();
writer.Int(1);
writer.Int(2);
writer.EndArray();
writer.EndObject();
std::cout << buffer.GetString() << std::endl;
/* Вывод:
{
    "data": [
        1,
        2
    ]
}
*/

8. Класс rapidjson::Reader и rapidjson::BaseReaderHandler

Сигнатура: class Reader { ... }; template<typename Encoding, typename Derived> class BaseReaderHandler;

Что делает: Реализует SAX-подобный парсинг. Вы определяете обработчик (handler), который получает события (начало объекта, ключ, значение и т.д.). Это позволяет обрабатывать JSON без построения полного DOM-дерева, экономя память.

Пример (простой обработчик):

struct MyHandler : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, MyHandler> {
    bool Null() { std::cout << "null" << std::endl; return true; }
    bool Bool(bool b) { std::cout << (b ? "true" : "false") << std::endl; return true; }
    bool Int(int i) { std::cout << i << std::endl; return true; }
    bool String(const char* str, rapidjson::SizeType length, bool copy) {
        std::cout << "string: " << str << std::endl; return true;
    }
    bool StartObject() { std::cout << "{" << std::endl; return true; }
    bool Key(const char* str, rapidjson::SizeType length, bool copy) {
        std::cout << "key: " << str << std::endl; return true;
    }
    bool EndObject(rapidjson::SizeType memberCount) { std::cout << "}" << std::endl; return true; }
    bool StartArray() { std::cout << "[" << std::endl; return true; }
    bool EndArray(rapidjson::SizeType elementCount) { std::cout << "]" << std::endl; return true; }
};

rapidjson::Reader reader;
MyHandler handler;
const char* json = "{\"a\": 1, \"b\": [2, 3]}";
rapidjson::StringStream ss(json);
reader.Parse(ss, handler);

Реальный практический пример: парсинг конфигурационного файла и генерация отчёта

Представьте, что у нас есть JSON-файл config.json с настройками приложения и списком пользователей. Мы хотим прочитать его, проверить корректность, а затем сгенерировать отчёт в формате JSON.

#include <iostream>
#include <fstream>
#include <string>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/error/en.h"

// Функция для чтения файла в строку
std::string readFile(const std::string& filename) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        throw std::runtime_error("Не удалось открыть файл: " + filename);
    }
    std::string content((std::istreambuf_iterator<char>(file)),
                         std::istreambuf_iterator<char>());
    return content;
}

int main() {
    try {
        // 1. Читаем JSON-файл
        std::string jsonContent = readFile("config.json");
        
        // 2. Парсим JSON
        rapidjson::Document doc;
        doc.Parse(jsonContent.c_str());
        
        if (doc.HasParseError()) {
            std::cerr << "Ошибка парсинга JSON: " 
                      << rapidjson::GetParseError_En(doc.GetParseError())
                      << " (позиция: " << doc.GetErrorOffset() << ")" 
                      << std::endl;
            return 1;
        }
        
        // 3. Проверяем структуру документа
        if (!doc.Is                    

Рекомендаций