Хорошо в плане поддержки JSON живётся программистам на Javascript — по какому-то невероятному стечению обстоятельств там JSON входит в спецификацию самого языка: есть JSON — есть объект. Удобно. Неплохо дело обстоит и в языках, где JSON не входит в сам язык, но поддерживается стандартной библиотекой (Python, Ruby): импортируешь модуль — и готово.
Жизнь программистов на С++ никогда не была особо простой — поддержки JSON у нас нет ни на уровне языка, ни в стандартной библиотеке. И не будет, возможно, никогда. «Тоже мне проблему нашел!» — скажут мне опытные коллеги — «Её там и не должно быть, С++ поставляется без „батареек“. Для решения этой задачи мы…» и вот здесь они разделятся на два лагеря:
1. «Мы используем большой фреймворк (boost, Qt, POCO, другой), который применяется во всех наших проектах и умеет 150 000 разных вещей, в том числе и JSON.»
2. «Мы придерживаемся подхода в котором для каждой задачи применяется своя легковесная библиотека. В частности, для JSON мы уже 150 000 лет назад выбрали отличную библиотеку %JSON_LIB%, которая прекрасно работает.»
Да, всё так и есть. Вот только…
В общем, фреймворки навязывают нам своё виденье задачи, свой способ её решения и стремятся навсегда привязать нас к себе. Нет, если вы уверены, что нашли тот самый единственный и неповторимый фреймворк и будете с ним счастливы до конца жизни — воля ваша. Но я как-то не сторонник подобного фатализма.
Давайте немного помечтаем.
А что, если бы JSON вошел в стандартную библиотеку нового стандарта С++? Что, если бы он был написан в терминах С++11\14 и без требований обратной совместимости со старыми стандартами языка? Что, если бы синтаксис этого модуля попытались бы сделать максимально приближенным к родному для JSON использованию «а-ля Javascript», но в том же время сохранить дух С++ (эффективность, минимальное потребление памяти, совместимость с STL)? Что, если бы его можно было включить в проект одним инклюдом и не беспокоиться о его сборке и линковке? Как бы это всё выглядело и работало?
И у нас есть ответ на этот вопрос! Давайте посмотрим на JSON-библиотеку для С++ написанную в соответствии со всеми этими принципами, ну и вообще написанной людьми для людей, а не чужими для хищников, как это обычно бывает.
Итак, пусть у нам нужно создать вот такой JSON-объект:
{ "name": "Habrahabr", "nothing": nullptr, "answer": { "everything": 42 }, "companies": ["Infopulse", "TM"], "user": { "name": "tangro", "active": true } }
Давайте возьмём и создадим!
Качаем github.com/nlohmann/json/blob/master/src/json.hpp, включаем его инклюдом в cpp-файл.
#include "json.hpp" using json = nlohmann::json;
// создаём пустой JSON-объект json j;
// добавляем строку, которая будет храниться как std::string j["name"] = "Habrahabr";
// добавляем пустой вложенный объект j["nothing"] = nullptr;
// число внутри вложенного объекта j["answer"]["everything"] = 42;
// добавляем массив строк (будет храниться как std::vector<std::string>) // обратите внимание - используются списки инициализации j["companies"] = { "Infopulse", "TM" };
// добавляем ещё один объект - на этот раз используем список инилиализации с парами "ключ"-"значение" j["user"] = { {"name", "tangro"}, {"active", true} };
Кстати, используя последнюю фичу, вместо всего этого можно было бы написать создания объекта очень похожее на сам JSON-формат:
json j2 = { {"name", "Habrahabr"}, {"nothing", nullptr}, {"answer", { {"everything", 42} }}, {"companies", {"Infopulse", "TM"}}, {"user", { {"name", "tangro"}, {"active", true} }} }
Так, а где же все эти модные фишки С++11?
А вот они.
// создание объекта из строкового литерала json j = "{ \"active\": true, \"pi\": 3.141 }"_json;
// использование raw string json j = R"( { "active": true, "pi": 3.141 } )"_json;
// использование auto auto j = json::parse("{ \"active\": true, \"pi\": 3.141 }");
// использование range-based for for (auto element : j) { std::cout << element << '\n'; }
Так, ладно, я приверженец «классического» стиля, покажите мне совместимость с STL
// создание массива, синтаксис "а-ля std::vector" json j; j.push_back("foo"); j.push_back(1); j.push_back(true);
// создание объекта, синтаксис "а-ля std::map" json o; o["foo"] = 23; o["bar"] = false;
// поиск элемента if (o.find("foo") != o.end()) { // не найдено }
// итерация по массиву for (json::iterator it = j.begin(); it != j.end(); ++it) { std::cout << *it << '\n'; }
// перегруженный оператор [] и метод at(), поддержка std::string const std::string tmp = j[0]; j[1] = 42; bool foo = j.at(2);
// знакомые методы работы с контейнерами j.size(); // 3 элемента j.empty(); // true или false j.type(); // json::value_t::array j.clear(); // очищаем
// дамп в строку std::string s = j.dump();
Ну а ещё есть поддержка создания JSON из любых STL-контейнеров (std::array, std::vector, std::deque, std::forward_list, std::list, std::set, std::multiset, std::unordered_set, std::unordered_multiset). Для линейно-упорядоченных контейнеров порядом элементов будет сохранён, для ассоциативных, понятное дело — нет.
Так, почти убедил
А ещё есть 100% покрытие библиотеки тестами и гарантия отстутствия утечек памяти от Valgrind.
Вот и всё.
В общем, я понимаю, что это «всего-лишь ещё одна библиотека для поддержки JSON» и по сути ничем она от миллиона других не отличается. Но как же всё-таки с ней удобно работать!
ссылка на оригинал статьи http://habrahabr.ru/post/254075/
Добавить комментарий