json parsing C + + library nlohmann / json

Keywords: C++ JSON

Project address: nlohmann_json_cmake_fetchcontent/json.hpp at master · ArthurSonzogni/nlohmann_json_cmake_fetchcontent · GitHub

design goal

  • Intuitive syntax. In like Python In such a language, JSON is like a first-class data type. We used all the magic of modern C + + operators to achieve the same feeling in your code. Look at the following example and you'll see what I mean.

  • Trivial integration. Our entire code consists of a single header file, json.hpp. That's it. No libraries, no subprojects, no dependencies, no complex build systems. This class is written in ordinary C++11. In short, there is no need to adjust your compiler flags or project settings.

  • Rigorous testing. Our class has been 100% unit tested, including all abnormal behaviors. In addition, we confirmed with Valgrind and xxx that there was no memory leak. In addition, Google OSS fuzzy runs fuzzy tests on all parsers 24 / 7, and has efficiently performed billions of tests so far. In order to maintain high quality, the project follows (CII) best practices.

There are other aspects that are not important to us:

  • Memory efficiency. Each JSON object has the overhead of a pointer (the maximum size of the Union) and an enumeration element (1 byte). The default generalization uses the following C + + data types: std::string, corresponding string, int64_t,uint64_t or double corresponds to a number, std::map for an object, std::vector corresponds to an array, and bool corresponds to a Boolean value. However, you can template the generic class basic_json meets your needs.

  • Speed. There must be a faster JSON library outside. However, if your goal is to speed up development by adding JSON support with a single header file, this library is your choice.

Integration

json.hpp is the only file needed. Just add

#include <nlohmann/json.hpp>

// for convenience
using json = nlohmann::json;

You want to process json files and set it to support C + +.

You can further use the file include/nlohmann/json_fwd.hpp is used to forward declarations. json_ The installation of fwd.hpp (as part of the cmake installation steps) can be done by setting - djson_ MultipleHeaders =ON

example

In addition to the following examples, you may want to view the documentation, where each function contains a separate code example (for example, see empty()). All sample files can be compiled and executed by themselves (for example, the file empty. CPP).

JSON as a primary data type

Here are some examples to let you know how to use this class.

Suppose you want to create such a JSON object with this library:

{
  "pi": 3.141,
  "happy": true,
  "name": "Niels",
  "nothing": null,
  "answer": {
    "everything": 42
  },
  "list": [1, 0, 2],
  "object": {
    "currency": "USD",
    "value": 42.99
  }
}

It can be written as follows:

// create an empty structure (null)
json j;

// add a number that is stored as double (note the implicit conversion of j to an object)
j["pi"] = 3.141;

// add a Boolean that is stored as bool
j["happy"] = true;

// add a string that is stored as std::string
j["name"] = "Niels";

// add another null object by passing nullptr
j["nothing"] = nullptr;

// add an object inside the object
j["answer"]["everything"] = 42;

// add an array that is stored as std::vector (using an initializer list)
j["list"] = { 1, 0, 2 };

// add another object (using an initializer list of pairs)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };

// instead, you could also write (which looks very similar to the JSON above)
json j2 = {
  {"pi", 3.141},
  {"happy", true},
  {"name", "Niels"},
  {"nothing", nullptr},
  {"answer", {
    {"everything", 42}
  }},
  {"list", {1, 0, 2}},
  {"object", {
    {"currency", "USD"},
    {"value", 42.99}
  }}
};

Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to clarify or express some edge cases, function   json::array()   and   json::object() will help, as follows:

// a way to express the empty array []
json empty_array_explicit = json::array();

// ways to express the empty object {}
json empty_object_implicit = json({});
json empty_object_explicit = json::object();

// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });

Serialization / deserialization

To/from strings
You can add_ JSON to a string to create a JSON value (deserialization):

// create object from string literal
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;

// or even nicer with a raw string literal
auto j2 = R"(
  {
    "happy": true,
    "pi": 3.141
  }
)"_json;

Please note that if not attached_ JSON suffix, the passed string text will not be parsed, but only used as JSON string value. That is, JSON J = "{\" happy\": true, \"pi\": 3.141}" only stores strings   "{\" happy\": true, \"pi\": 3.141}", instead of parsing the actual object.

The above example can also use json::parse():

// parse explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");

serialize

You can also get the string expression corresponding to a json object (serialization):

// explicit conversion to string
std::string s = j.dump();    // {"happy":true,"pi":3.141}

// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump(4) << std::endl;
// {
//     "happy": true,
//     "pi": 3.141
// }

As shown above, adding the dump parameter can make the print result more beautiful.

Note the difference between serialization and assignment:

// store a string in a JSON value
json j_string = "this is a string";
// Store a string in the json class

// retrieve the string value
// Gets the value of the internal string
auto cpp_string = j_string.get<std::string>();
// retrieve the string value (alternative when an variable already exists)
std::string cpp_string2;
j_string.get_to(cpp_string2);
// Assign the value of the internal string to cpp_string2?

// retrieve the serialized value (explicit JSON serialization)
// Explicitly serialize json objects into std::string
std::string serialized_string = j_string.dump();

// output of original string
// Output: get the value of the internal string, the value assigned to the string through the json object, and get the value of the internal string directly from json
std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::string>() << '\n';

// output of serialized value
// Overloaded the cout operator and directly output the value of the json object
std::cout << j_string << " == " << serialized_string << std::endl;

The first is to store a string in the json class
The second is to get the value of the internal string.

The json object overloads the cout operator.

. dump() always returns a serialized value, and Get < STD:: String > () returns the originally stored string value.

be careful

The library only supports UTF-8. When you store strings of different encodings in the library, calling. dump() may throw an exception unless json::error_handler_t::replace or json::error_handler_t::ignore is used.

Note the library only supports UTF-8. When you store strings with
different encodings in the library, calling dump() may throw an
exception unless json::error_handler_t::replace or
json::error_handler_t::ignore are used as error handlers.

To/from streams (e.g. files, string streams)

You can also use stream to serialize and deserialize:

// deserialize from standard input
json j;
std::cin >> j;

// serialize to standard output
std::cout << j;

// the setw manipulator was overloaded to set the indentation for pretty printing
std::cout << std::setw(4) << j << std::endl;

The setw() operator has been overloaded and can be used to set indentation and better print format.

These operators apply to any subclass of std::istream or std::ostream. Here are some examples of files:

// read a JSON file
// Get json object through file stream
std::ifstream i("file.json");
json j;
i >> j;

// write prettified JSON to another file
// Write the nice of the json object to the file
std::ofstream o("pretty.json");
o << std::setw(4) << j << std::endl;

Note that if the exception bit is set to failbit, it is not suitable for this use case. This will cause the program to terminate because the noexcept specifier is in use.

Please note that setting the exception bit for failbit is inappropriate for this use case. It will result in program termination due to the noexcept specifier in use.

Read from iterator range

Resolved by a range iterator.
JSON can also be parsed from the iterator scope, that is, from any container accessible to the iterator, the value type is an integer type of 1, 2 or 4 bytes, which will be interpreted as UTF-8, UTF-16 and UTF-32 respectively. For example, an STD:: vector < STD:: uint8_ t> Or STD:: vector < STD:: uint16_ t>:

std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
json j = json::parse(v.begin(), v.end());

You may leave the iterators for the range [begin, end):

std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
json j = json::parse(v);

STL like access

We design JSON classes to behave like STL containers. In fact, it meets the requirements of reversible containers.
Add element:

// create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);

// also use emplace_back
j.emplace_back(1.78);

Iterator traversal:

// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
  std::cout << *it << '\n';
}

// range-based for
for (auto& element : j) {
  std::cout << element << '\n';
}

Get / set value

// getter/setter
const auto tmp = j[0].get<std::string>();
j[1] = 42;
bool foo = j.at(2);

Comparison:

// comparison
j == "[\"foo\", 42, true]"_json;  // true

Other interfaces:

// other stuff
j.size();     // 3 entries
j.empty();    // false
j.type();     // json::value_t::array
j.clear();    // the array is empty again

Type extraction:

// convenience type checkers
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();

Create object:

// create an object
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;

// also use emplace
o.emplace("weather", "sunny");

There are several other ways to access iterators:

// special iterator member functions for objects
for (json::iterator it = o.begin(); it != o.end(); ++it) {
  std::cout << it.key() << " : " << it.value() << "\n";
}

// the same code as range for
for (auto& el : o.items()) {
  std::cout << el.key() << " : " << el.value() << "\n";
}

// even easier with structured bindings (C++17)
for (auto& [key, value] : o.items()) {
  std::cout << key << " : " << value << "\n";
}

Find (exists):

// find an entry
if (o.contains("foo")) {
  // there is an entry with key "foo"
}

Return iterator lookup (return iterator):

// or via find and an iterator
if (o.find("foo") != o.end()) {
  // there is an entry with key "foo"
}

Find through count():

// or simpler using count()
int foo_present = o.count("foo"); // 1
int fob_present = o.count("fob"); // 0

Delete:

// delete an entry
o.erase("foo");

Convert from STL container

The values of any sequence container (std::array, std::vector, std::deque, std::forward_list, std::list) can be used to construct JSON values (for example, integers, floating-point numbers, Boolean values, string types, or STL containers described in this section), which can be used to create JSON arrays. Similarly, the same is true for associative containers (std::set, std::multiset, std::unordered_set, std::unordered_multiset), but in these cases, the order of array elements depends on the order of elements in their respective STL containers.

std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// [1, 2, 3, 4]

std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);
// [1.2, 2.3, 3.4, 5.6]

std::list<bool> c_list {true, true, false, true};
json j_list(c_list);
// [true, true, false, true]

std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]

std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
// [1, 2, 3, 4]

std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]

std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]

std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]

std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]

Similarly, any associated key value container (std::map, std::multimap, std::unordered_map, std::unordered_multimap), if its key can construct std::string   And its value can be used to construct JSON values (see the above example), it can be used to create JSON objects. Note that in the case of Multimap, only one key is used in the JSON object, and the value depends on the internal order of the STL container.

std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
json j_map(c_map);
// {"one": 1, "three": 3, "two": 2 }

std::unordered_map<const char*, double> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
json j_umap(c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}

std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_mmap(c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}

std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_ummap(c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}

JSON Pointer and JSON Patch

JSON PATCH usage_ mojianpo Mo jianpo CSDN blog   JSON PATCH usage_ mojianpo Mo jianpo CSDN blog

This library supports JSON pointers (RFC 6901) as an alternative to addressing structured value. Most importantly, JSON patch (RFC 6902) allows describing the difference between two JSON values - effectively allowing patch and difference operations known from Unix.

// a JSON value
json j_original = R"({
  "baz": ["one", "two", "three"],
  "foo": "bar"
})"_json;

// access members with a JSON pointer (RFC 6901)
j_original["/baz/1"_json_pointer];
// "two"

// a JSON patch (RFC 6902)
json j_patch = R"([
  { "op": "replace", "path": "/baz", "value": "boo" },
  { "op": "add", "path": "/hello", "value": ["world"] },
  { "op": "remove", "path": "/foo"}
])"_json;

// apply the patch
json j_result = j_original.patch(j_patch);
// {
//    "baz": "boo",
//    "hello": ["world"]
// }

// calculate a JSON patch from two JSON values
json::diff(j_result, j_original);
// [
//   { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
//   { "op": "remove","path": "/hello" },
//   { "op": "add", "path": "/foo", "value": "bar" }
// ]

Implicit conversion

Supported types can be implicitly converted to JSON values.

It is not recommended to use implicit conversion to convert values from JSON. You can turn OFF implicit conversion by defining JSON_USE_IMPLICIT_CONVERSIONS to 0 before including the json.hpp header file. If CMake is used, you can also do so by setting the option json_implicit conversions to OFF.

// strings
std::string s1 = "Hello, world!";
json js = s1;
auto s2 = js.get<std::string>();
// NOT RECOMMENDED
std::string s3 = js;
std::string s4;
s4 = js;

// Booleans
bool b1 = true;
json jb = b1;
auto b2 = jb.get<bool>();
// NOT RECOMMENDED
bool b3 = jb;
bool b4;
b4 = jb;

// numbers
int i = 42;
json jn = i;
auto f = jn.get<double>();
// NOT RECOMMENDED
double f2 = jb;
double f3;
f3 = jb;

// etc.

Note that char type is not automatically converted to JSON string, but to integer. Conversion to string must be explicitly specified:

char ch = 'A';                       // ASCII value 65
json j_default = ch;                 // stores integer number 65
json j_string = std::string(1, ch);  // stores string "A"

Arbitrary type conversion

Each type can be serialized in JSON, not just STL containers and scalar types. Usually, you will do something similar:

namespace ns {
    // a simple struct to model a person
    struct person {
        std::string name;
        std::string address;
        int age;
    };
}

ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

// convert to JSON: copy each value into the JSON object
json j;
j["name"] = p.name;
j["address"] = p.address;
j["age"] = p.age;

// ...

// convert from JSON: copy each value from the JSON object
ns::person p {
    j["name"].get<std::string>(),
    j["address"].get<std::string>(),
    j["age"].get<int>()
};

It works, but it's troublesome... There's a better way:

// create a person
ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};

// conversion: person -> json
json j = p;

std::cout << j << std::endl;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

// conversion: json -> person
auto p2 = j.get<ns::person>();

// that's it
assert(p == p2);

Basic Usage

To make this work with your type, you only need to provide two functions:

using nlohmann::json;

namespace ns {
    void to_json(json& j, const person& p) {
        j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
    }

    void from_json(const json& j, person& p) {
        j.at("name").get_to(p.name);
        j.at("address").get_to(p.address);
        j.at("age").get_to(p.age);
    }
} // namespace ns

That's it! When calling the JSON constructor and your type, the custom to_json method will be called automatically. Similarly, when calling get < your_type > () or get_to (your_type &), the from_json method will be called.

Posted by David03 on Mon, 01 Nov 2021 17:18:58 -0700