# Trivial integration - single header file - no build system needed - vanilla C++11 - works with GCC 4.8+, Clang 3.4+ and MSVC 2015+ ```cpp #include // for convenience using json = nlohmann::json; int main() { // a JSON pretty-printer in 3 lines json j; std::cin >> j; std::cout << std::setw(4) << j << std::endl; } $ g++ pretty.cpp -std=c++11 -o pretty ``` # First-class datatype - write JSON like any other C++ literal - initializer lists become arrays and objects ```cpp json j = { {"pi", 3.141}, {"happy", true}, {"name", "Niels"}, {"nothing", nullptr}, {"answer", { {"everything", 42} }}, {"list", {1, 0, 2}}, {"object", { {"currency", "USD"}, {"value", 42.99} }} }; ``` # Array construction - arrays work the way you expect - adding to a null value makes it an array ```cpp // create null value json j; // add via member function j.push_back("Hello"); j.emplace_back("World"); // add via += operator j += false; // add via index j[2] = 42.3; // gaps are filled with null j[4] = "World"; ``` # Object construction - objects work the way you expect - adding to a null value makes it an object ```cpp // create null value json j; // add via member function j.emplace("key2", "value"); j.push_back({"key", "value"}); // add via += operator j += {"another key", false}; // add via key j["pi"] = 3.1415926; j["answer"]["everything"] = 42; ``` # Container operations - use JSON like an STL container - satisfies the ReversibleContainer requirements ```cpp // container empty? j.empty(); // current number of elements j.size(); // maximal number of elements j.max_size(); // swap elements j.swap(j2); std::swap(j, j2); // clear a container j.clear(); ``` # STL containers - switch between STL and JSON containers - conversion works in both directions ```cpp // some STL containers std::vector v; std::unordered_map m; // automatic transformation json j_array(v); json j_object(m); // works both ways auto l = j_array.get>(); auto u = j_object .get>(); ``` # Type conversion - convert your own types to and from JSON - one macro writes to_json and from_json - or write both functions for full control ```cpp // some user-defined type struct person { std::string name; int age; }; // defines to_json and from_json NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, age) // conversion in action person p {"Ned Flanders", 60}; json j = p; // {"age": 60, "name": "Ned Flanders"} auto p2 = j.get(); ``` # Type deduction - types are deduced automatically - member functions tell you the type ```cpp // type deduction json j1 = nullptr; json j2 = false; json j3 = 12.23; json j4 = {1, 2, 3}; json j5 = {{"key", "value"}}; json j6 = "Hello"; // quick checks j1.is_null(); j2.is_boolean(); j3.is_number(); j4.is_array(); j5.is_object(); j6.is_string(); ``` # Serialization - to a string or a stream - pretty-printing included ```cpp // default serialization auto s1 = j.dump(); // pretty-printed int indent = 4; std::string s2 = j.dump(indent); // to stream std::cout << j << std::endl; // pretty-printed std::cout << std::setw(indent) << j << std::endl; ``` # Deserialization - parse strings, streams and files - the _json literal puts plain JSON in your code - comments and trailing commas on request ```cpp // from string json j1 = json::parse("[1,null,false]"); // from file std::ifstream f("example.json"); json j2 = json::parse(f); // from string literal json j3 = "[3.14159]"_json; // from raw string literal json j4 = R"( {"one": 1, "two": 2, "three": [1, 2, 3]} )"_json; ``` # SAX parsing - the parser can emit SAX events - process input without building a JSON value ```cpp // define a SAX event processor struct sax_processor : json::json_sax_t { bool boolean(bool val) override { return true; } // implement other functions... }; // create object sax_processor my_processor; // use it to process input json::sax_parse(input, &my_processor); ``` # Zero-copy views {badge: NEW IN 3.13} - parse without building a tree: 16 bytes per value - strings and numbers stay in the source text - materialize a json value only where you need one ```cpp #include // parse into a flat index, not a tree std::ifstream f("big.json"); auto doc = nlohmann::json_document::parse(f); // read-only views with familiar access auto view = doc.root(); double pi = view["pi"].get(); for (auto v : view["list"]) std::cout << v.get() << '\n'; // build a json value only where needed json part = view["list"].materialize(); ``` # JSON Pointer - address any element with a JSON Pointer - flatten a value to pointer/value pairs, and back ```cpp json j = {"good", "bad", "ugly"}; // JSON pointer to the 3rd list element auto ptr = json::json_pointer("/2"); // use JSON pointer to access element j[ptr] = {{"it", "cattivo"}}; // user-defined string literal j["/2/en"_json_pointer] = "ugly"; // ["good","bad",{"en":"ugly","it":"cattivo"}] json flat = j.flatten(); // {"/0":"good","/1":"bad","/2/en":"ugly", // "/2/it":"cattivo"} json j2 = flat.unflatten(); // j == j2 ``` # JSON Patches - use JSON Patch to describe edit operations - create diffs from two JSON values - or use a JSON Merge Patch that looks like the document ```cpp json source = R"({"F": "Paris", "D": "Berlin"})"_json; // a JSON Patch (RFC 6902) json p1 = R"([ {"op": "add", "path": "/GB", "value": "London"} ])"_json; json target = source.patch(p1); // create a diff from two JSON values json p2 = json::diff(target, source); // [{"op": "remove", "path": "/GB"}] // a JSON Merge Patch (RFC 7396) target.merge_patch(R"({"F": null})"_json); // {"D": "Berlin", "GB": "London"} ``` # Binary formats - compact binary representations - BJData, BON8, BSON, CBOR, MessagePack and UBJSON ```cpp // a JSON value json j = R"({"compact": true, "schema": 0})"_json; // serialize to CBOR std::vector v = json::to_cbor(j); // 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61, // 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68, // 0x65, 0x6D, 0x61, 0x00 // roundtrip json j_from_cbor = json::from_cbor(v); // the same for the other formats auto b = json::to_bon8(j); auto m = json::to_msgpack(j); ``` # Algorithms - the standard algorithms just work - sort, find, count, merge and more ```cpp // sort JSON containers std::sort(j.begin(), j.end()); // find element auto pos = std::find(j.begin(), j.end(), json("needle")); // count all numbers auto n = std::count_if(j.begin(), j.end(), [](const json& v) { return v.is_number(); }); // merge two sorted containers std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); ``` # Iterators - iterators, const iterators, and reverse iterators - random access for arrays - items() gives you keys and values ```cpp json j = {1, 2, 3, 4, 5}; // range for for (auto& el : j) { // print all values std::cerr << el << '\n'; } // classical iterators for (auto it = j.begin(); it != j.end(); ++it) { // double all values *it = it->get() * 2; } ``` # Structured bindings - since C++17, unpack keys and values with structured bindings ```cpp // JSON object auto j = R"({"foo": 23, "bar": 42})"_json; // structured bindings for (auto& [key, val] : j.items()) { std::cerr << key << ':' << val << '\n'; } ```