Введение в работу с JSON в C++
JSON (JavaScript Object Notation) — это легкий формат обмена данными, который широко используется в веб-разработке, конфигурационных файлах и API-интерфейсах. Несмотря на то, что C++ не имеет встроенной поддержки JSON, существует множество надежных библиотек, которые упрощают парсинг (разбор) и сериализацию (преобразование в строку) данных. В этой статье мы рассмотрим две популярные библиотеки: nlohmann/json (рекомендуется для новичков и современных проектов) и Boost.PropertyTree (для тех, кто уже использует Boost). Вы узнаете, как читать, создавать, изменять и выводить JSON-данные на C++.
Библиотека nlohmann/json: установка и базовые принципы
Библиотека nlohmann/json (часто называется "JSON for Modern C++") является одной из самых популярных благодаря своей простоте и интуитивно понятному API. Она поддерживает C++11 и выше, не требует внешних зависимостей и распространяется как один заголовочный файл json.hpp.
Установка
Скачайте файл json.hpp с GitHub-репозитория и поместите его в папку с вашим проектом. Затем подключите его в коде:
#include <iostream>
#include <fstream>
#include "json.hpp" // Подключение библиотеки
using json = nlohmann::json;
Парсинг JSON из строки и файла
Библиотека позволяет парсить JSON из строки, файла или потока ввода. Рассмотрим пример парсинга из строки:
#include <iostream>
#include "json.hpp"
using json = nlohmann::json;
int main() {
// Строка JSON
std::string jsonString = R"({
"name": "Alice",
"age": 30,
"isStudent": false,
"hobbies": ["reading", "coding"]
})";
// Парсинг
json data = json::parse(jsonString);
// Доступ к данным
std::cout << "Name: " << data["name"] << std::endl;
std::cout << "Age: " << data["age"] << std::endl;
std::cout << "Is student: " << (data["isStudent"] ? "true" : "false") << std::endl;
// Итерация по массиву
for (auto& hobby : data["hobbies"]) {
std::cout << "Hobby: " << hobby << std::endl;
}
return 0;
}
Для парсинга из файла используйте json::parse(std::ifstream("data.json"));.
Сериализация JSON
Создание JSON-объекта и его сериализация в строку выполняется естественным образом:
#include <iostream>
#include "json.hpp"
using json = nlohmann::json;
int main() {
json person;
person["name"] = "Bob";
person["age"] = 25;
person["isStudent"] = true;
person["hobbies"] = {"gaming", "swimming"};
// Вложенный объект
json address;
address["city"] = "Moscow";
address["street"] = "Tverskaya";
person["address"] = address;
// Сериализация в строку с отступами
std::string serialized = person.dump(4); // 4 пробела для форматирования
std::cout << serialized << std::endl;
// Сохранение в файл
std::ofstream file("output.json");
file << person.dump(2);
file.close();
return 0;
}
Метод dump() преобразует объект в строку. Параметр указывает количество пробелов для отступов (0 — компактный вывод).
Библиотека Boost.PropertyTree: альтернативный подход
Если в вашем проекте уже используется Boost, библиотека Boost.PropertyTree предоставляет удобный способ работы с JSON и другими форматами (XML, INI). Она менее специализирована для JSON, но хорошо интегрируется с экосистемой Boost.
Парсинг JSON с Boost.PropertyTree
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
int main() {
using boost::property_tree::ptree;
ptree pt;
// Чтение из файла
boost::property_tree::read_json("config.json", pt);
// Доступ к значениям
std::string name = pt.get<std::string>("name");
int age = pt.get<int>("age");
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
// Обход массива (в JSON представлен как объект с числовыми ключами)
for (auto& item : pt.get_child("hobbies")) {
std::cout << "Hobby: " << item.second.data() << std::endl;
}
return 0;
}
Обратите внимание: массивы в Boost.PropertyTree хранятся как объекты с ключами "0", "1", "2" и т.д. Это необходимо учитывать при работе с JSON-массивами.
Сериализация с Boost.PropertyTree
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
int main() {
using boost::property_tree::ptree;
ptree pt;
pt.put("name", "Charlie");
pt.put("age", 35);
// Создание массива
ptree hobbies;
hobbies.put("0", "running");
hobbies.put("1", "cycling");
pt.add_child("hobbies", hobbies);
// Запись в файл
boost::property_tree::write_json("output_boost.json", pt);
// Вывод в поток
std::ostringstream oss;
boost::property_tree::write_json(oss, pt);
std::cout << oss.str() << std::endl;
return 0;
}
Продвинутые техники: ошибки, производительность и типы данных
Обработка ошибок парсинга
При работе с внешними данными важно корректно обрабатывать ошибки. В nlohmann/json используется исключение json::parse_error:
try {
json data