Работа с JSON в C++: парсинг и сериализация на практике
JSON (JavaScript Object Notation) — один из самых популярных форматов обмена данными в современных приложениях. Несмотря на то, что C++ не имеет встроенной поддержки JSON, существует множество библиотек, упрощающих работу с этим форматом. В этой статье мы рассмотрим практические аспекты парсинга и сериализации JSON в C++ с использованием библиотеки nlohmann/json — одной из самых популярных и удобных в использовании.
Почему стоит выбрать nlohmann/json?
Библиотека nlohmann/json (часто называемая JSON for Modern C++) завоевала популярность благодаря своему интуитивно понятному API, напоминающему работу с контейнерами STL. Она является header-only (не требует сборки), поддерживает C++11 и выше, а также обеспечивает высокую производительность и безопасность типов.
Установка библиотеки проста: достаточно скачать один заголовочный файл json.hpp или использовать менеджер пакетов vcpkg, Conan или CMake FetchContent.
#include <nlohmann/json.hpp>
using json = nlohmann::json;
Основы парсинга JSON в C++
Парсинг JSON — это процесс преобразования строки в структурированные данные. Библиотека nlohmann/json предоставляет несколько способов парсинга.
Парсинг из строки
Самый распространённый способ — использование статического метода json::parse():
#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
int main() {
std::string json_string = R"({
"name": "Иван",
"age": 30,
"is_student": false,
"skills": ["C++", "Python", "JavaScript"],
"address": {
"city": "Москва",
"zip": "101000"
}
})";
try {
json data = json::parse(json_string);
// Доступ к данным
std::cout << "Имя: " << data["name"] << std::endl;
std::cout << "Возраст: " << data["age"] << std::endl;
std::cout << "Студент: " << (data["is_student"] ? "да" : "нет") << std::endl;
// Работа с массивами
for (auto& skill : data["skills"]) {
std::cout << "Навык: " << skill << std::endl;
}
// Работа с вложенными объектами
std::cout << "Город: " << data["address"]["city"] << std::endl;
} catch (json::parse_error& e) {
std::cerr << "Ошибка парсинга: " << e.what() << std::endl;
}
return 0;
}
Парсинг из файла
Для работы с файлами используйте потоки ввода-вывода C++:
#include <fstream>
#include <nlohmann/json.hpp>
int main() {
std::ifstream file("data.json");
if (!file.is_open()) {
std::cerr << "Не удалось открыть файл" << std::endl;
return 1;
}
try {
json data = json::parse(file);
std::cout << "Данные загружены: " << data.dump(2) << std::endl;
} catch (json::parse_error& e) {
std::cerr << "Ошибка: " << e.what() << std::endl;
}
return 0;
}
Сериализация JSON в C++
Сериализация — обратный процесс: преобразование данных C++ в JSON-строку. Библиотека предлагает метод dump() с возможностью форматирования.
Создание JSON-объекта
Вы можете создавать JSON-объекты напрямую, как обычные ассоциативные массивы:
#include <nlohmann/json.hpp>
#include <iostream>
int main() {
json person;
person["name"] = "Мария";
person["age"] = 25;
person["is_employed"] = true;
person["languages"] = {"Русский", "Английский", "Немецкий"};
// Вложенный объект
person["contact"]["email"] = "maria@example.com";
person["contact"]["phone"] = "+7-999-123-45-67";
// Сериализация с отступами (pretty print)
std::string pretty_json = person.dump(4);
std::cout << pretty_json << std::endl;
// Компактная сериализация
std::string compact_json = person.dump();
std::cout << "Компактный: " << compact_json << std::endl;
return 0;
}
Сериализация в файл
#include <fstream>
#include <nlohmann/json.hpp>
void save_to_file(const json& data, const std::string& filename) {
std::ofstream file(filename);
if (file.is_open()) {
file << data.dump(2);
std::cout << "Данные сохранены в " << filename << std::endl;
} else {
std::cerr << "Ошибка записи файла" << std::endl;
}
}
Практический пример: конфигурация приложения
Рассмотрим реальный сценарий — загрузка и сохранение конфигурации приложения в формате JSON.
#include <nlohmann/json.hpp>
#include <iostream>
#include <fstream>
#include <string>
class AppConfig {
private:
json config;
std::string filename;
public:
AppConfig(const std::string& file) : filename(file) {
load();
}
void load() {
std::ifstream file(filename);
if (file.is_open()) {
try {
file >> config;
std::cout << "Конфигурация загружена" << std::endl;
} catch (...) {
std::cerr << "Ошибка загрузки, используем значения по умолчанию" << std::endl;
set_defaults();
}
} else {
std::cout << "Файл не найден, создаём новый" <&