-
Notifications
You must be signed in to change notification settings - Fork 0
API
- JSON() - element with null value
- JSON::Array() - Json array
- JSON::Object() - Json Object
Extra constructors
- JSON::Int() - alias to long long
- JSON::Float() - alias to double
- JSON::Bool() - alias to bool
- JSON::String() - alias to std::string
- JSON::Null() - alias to nullptr
Extra constructors are unnecessary, you can simply use type directly or literature.
JSON can be also constructed from other variables or variable types, such as variable types are
- std::string
- nullptr
- int *
- double
- bool
* can be instantiated as long long
Or you could be using literals, such as
JSON json = "hello";
or you can use bracket-enclosed lists to create even very complex objects, with literals, this will work:
JSON json = {{{ "hello", "world" }, { "another", "entry" }}};
but this won't:
JSON j = {{{ "hello", "world" }}};
Reason is that compiler cannot decide, whether you are trying create object with key "hello" and value "world", or array with entries "hello" and "world".
These literals though, do allow creating even quite complex and long trees without issues, if there is luck that compiler can decide are you trying to add a object or array, or single element (at least in case of string). But it's better to use JSON::Array and JSON::Object, also because it makes constructor much easier to read. When using literals, especially with longer json's, it might become pretty difficult to read as there will be so many curly braces.
This would be a lot better approach:
JSON json = JSON::Object({
{ "obj1", "ovalue1" },
{ "obj2", JSON::Object({
{ "child1", "value1" },
{ "child2", "value2" },
{ "child3", JSON::Array({
"avalue1", "avalue2", 1, 10, 924.05, nullptr,
JSON::Object({
{ "arrobj1", "aoval1" },
{ "arrobj2", "aoval2" }
}),
JSON::Array({ "arrval", nullptr, 14 }),
"avalue3", 19
})
}
})
},
{ "obj3", "ovalue3" },
{ "obj4", "ovalue4" },
{ "obj5", JSON::Object({
{ "child5_1", "value5_1" },
{ "child5_2", "value5_2" }
}
)},
{ "obj6", "ovalue6" }
});
Following constructor will create tree like this:
{
"obj1": "ovalue1",
"obj2": {
"child1": "value1",
"child2": "value2",
"child3": [
"avalue1",
"avalue2",
1,
10,
924.050000,
null,
{
"arrobj1": "aoval1",
"arrobj2": "aoval2"
},
[
"arrval",
null,
14
],
"avalue3",
19
]
},
"obj3": "ovalue3",
"obj4": "ovalue4",
"obj5": {
"child5_1": "value5_1",
"child5_2": "value5_2"
},
"obj6": "ovalue6"
}
JSON object can be any of following:
- NULLPTR
- OBJECT
- ARRAY
- STRING
- FLOAT
- INT
- BOOL
NULLPTR would be normally NULL, but that keyword is already reserved in C++ for alias to nullptr (or vice versa), so that's why it is NULLPTR instead of NULL. JSON variable's type can be queried with type() member.
In JSON standard, certain escape codes must be used with string, but when handling JSON types, you have normal values accessible, unless you are exporting a .dump() - this means that if you have a string value that contains text "hello world" it is as that when handling it; but when dumped, it's shown like like this "hello world" - parser also will take care of these when creating JSON from json string.
- operator [] (subscript operator)
- operator =
- += operator (append operator)
- at() (same as operator [] but throws exception)
- clear() (clears object, array or string)
- erase() (removes element from object or array)
- insert() (add element to object or array)
- append() (same as above)
Assignment types do not need to match with previous type, here string type element is replaced with int type.
JSON json = "hello";
json = 12;
Subscript operator [] works for both, objects and arrays, difference is that you either use name of key, which is a string, or index number from array. If key name is used, and type is not object, it will become object and overwrite any previous value, also if index number is used, element becomes a json array.
JSON json = JSON::Object({{ "key1", "value1" }});
JSON obj = JSON::Object();
JSON arr = JSON::Array();
json["hello"] = "world";
json["number"] = 1;
arr[0] = "value1";
arr.append("value2");
json["obj"] = obj;
json["arr"] = arr;
In example below, is also shown how you can combine other json values, json root element "json" type is object, and it's keys "obj" and "arr" are holding other JSON type variables, this code will produce following json:
{
"arr": [
"value1",
"value2"
],
"hello": "world",
"key1": "value1",
"number": 1,
"obj": {}
}
Subscript operator[] can also be replaced with .at() member, difference is that it won't ever create a new member to json, as it throws is object type is incorrect (object or array, depending on argument being string or number); or if subscript is out of bounds when key or index does not exist. This can be catched. This issue when new members appear to json, is explained better in Comparators along with solutions to avoid it.
- operator ==(JSON::TYPE) tests if json element is type
- operator !=(JSON::TYPE) tests if json element is not type
- operator ==(VALUE) where value can be anything to compare, such as std::string, double, bool, etc..
JSON json = JSON::Object({{ "key1", "value1" }});
if ( json["key1"] == JSON::TYPE::STRING )
std::cout << "key1 type is string" << std::endl;
if ((std::string)json["key1"] == "value1" )
std::cout << "key1 = value1" << std::endl;
if ( std::string value = json["key1"]; value == "value1" )
std::cout << "key1 = value1" << std::endl;
if ( json["key1"] == "value1" )
std::cout << "key1 = value1" << std::endl;
if ( json["key2"] == "value2" )
std::cout << "key2 = value2" << std::endl;
All above is valid tests- now there is a caveat; after performing these tests, we have a new value in our json:
{
"key1": "value1",
"key2": null
}
key2 appeared there because we tested it, and to subscript operator able to give a reference to value, it had to create it.
There is a solution to avoid this, though it's a bit more of work; you could test against a const - a member function .as_const() that returns const provides a work-around for this issue, or you can also use std::as_const from utility header also.
const JSON c_json = json.as_const(); // or std::as_const(json) if you prefer that instead, result is same with both ways
if ( json.as_const()["key2"] == "value2" )
std::cout << "key2 = value2" << std::endl;
if ( std::as_const(json)["key2"] == "value2" )
std::cout << "key2 = value2" << std::endl;
if ( c_json["key2"] == "value2" )
std::cout << "key2 = value2" << std::endl;
this would ensure that our json does not change, but still we succeeded doing this tests. You could also have all tests inside a function that accepts const JSON as argument, forcing argument to be const inside function.
{
"key1": "value1",
}
You also can use .at() member, if it suites better your needs, though you might need to try and catch a exception, this method is better explained in assignment section.
- to_string()
- to_int() casts to long long
- to_float() despite the name, casts to double
- to_double()
- to_bool()
or you can use operator based casting, supported direct types are:
- std::to_string
- double
- long long
- int
- bool
and in-direct types ofcourse have int casted from long long for example.
You can always cast to string, whether what is type of element, even for objects and arrays. If you are casting a object or array, it is same as you would had used .dump()
Casting to numeric values, from objects and arrays will raise a exception, but it succeeds from strings, if string contains parseable numeric value or bool's true or false, if not exception is raised.
Casting to bool from some other types works as well; if string contains either true or false, or a numeric value (not float), these values can be casted to bool as well.
There is also assistant for casting bool is_convertible(const JSON::TYPE&) and bool convertible_to(const JSON::TYPE& type) which both are same function. They are giving out a solution to test if type is available for casting to selected type, without exception. Even though string type can be casted from any type, this returns false when tested against array and object types, and when testing against numeric types, this will return false for null, even when casting allows conversion from null, resulting as a 0. Obviously when trying to test against object or array type, this will return false for every type, as they are not types available for casting.
You can output your json data to string with .dump(bool minified = false) member.
*.dump() function outputs json data in human readable format or as minimised blob, in exportable format, meaning that it's valid JSON with necessary string escapes.
warning, in case of special characters being used, it might not be that much of a human readable format after all
- parse(const std::string&)
Static parse function creates JSON from string. If parsing fails, JSON::exception is throw'n with a message containing coordinates/position where in string the error occurred.
JSON is iteratable both with const and non-const, begin() and end() are available.
Iteration produces a wrapper that is described in its own section of wiki.
JSON uses it's own JSON::exception which inherits from std::runtime_error - it isn't a lot different, but has some extra details that can be valuable when debugging.
- code() JSON::ERROR_CODE
- pos() if error occurred while parsing, this contains position where error occurred
- row() if error occurred while parsing, this contains row where error occurred or 0 if no rows existed
- column() this contains column where error existed during parsing, if no rows were on JSON source, this is same as pos
- coords() row and column as std::pair<size_t, size_t>
- msg() if exception has customised error message, it's here
- describe() JSON::ERROR_CODE of exception described
- what()
.what() is standard in exceptions; with JSON::exception it returns custom message(.msg()) if it is set(not empty), or description of JSON::ERROR_CODE error.
This API has its own page for used error codes.
- JSON::escape(const std::string&)
- JSON::unescape(const std::string&)
- JSON::to_lower(const std::string&)
- JSON::trim_special_chars(const std::string&)
all utility functions are static and mainly used to manipulate strings, there are tools for escaping and unescaping strings, as well as turning string to lower case. In case that your string contains various special characters, such as unicode characters, tabs and line feeds, they can be trimmed with trim_special_chars, all functions return string modified from argument, without touching original string.
- bool empty()
- bool contains(const std::string&)
- as_const()
These functions can be tested if object or array is empty, or object contains element with key. .empty() works also on string type, to reveal if string is empty.
.as_const()'s usefulness was earlier mentioned in Comparators - it simply results as a JSON variable's constant version, same can be done with std::as_const which is utilised in this function as well.
Most types introduced are supported by ostream, including:
- JSON
- JSON::TYPE
- JSON::ERROR_CODE
- JSON::ERROR
- JSON::exception
directing JSON type to output stream is same as directing .dump() to output stream.