From a84370c61fd55caff0de09df54e8acbe5bccffa0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 24 Jul 2020 14:04:58 +0200 Subject: [PATCH 1/8] :memo: add more documentation --- doc/mkdocs/docs/features/binary_values.md | 7 +- doc/mkdocs/docs/features/comments.md | 83 +++++++++++++++++++++++ doc/mkdocs/docs/features/object_order.md | 67 ++++++++++++++++++ doc/mkdocs/docs/home/faq.md | 37 ---------- doc/mkdocs/mkdocs.yml | 2 + 5 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 doc/mkdocs/docs/features/comments.md create mode 100644 doc/mkdocs/docs/features/object_order.md diff --git a/doc/mkdocs/docs/features/binary_values.md b/doc/mkdocs/docs/features/binary_values.md index e268a5b5..14cc65b1 100644 --- a/doc/mkdocs/docs/features/binary_values.md +++ b/doc/mkdocs/docs/features/binary_values.md @@ -165,7 +165,7 @@ JSON does not have a binary type, and this library does not introduce a new type Code: ```cpp - // create a binary value of subtype 42 (will be ignored by CBOR) + // create a binary value of subtype 42 json j; j["binary"] = json::binary({0xCA, 0xFE, 0xBA, 0xBE}, 42); @@ -173,17 +173,18 @@ JSON does not have a binary type, and this library does not introduce a new type auto v = json::to_cbor(j); ``` - `v` is a `std::vector` with the following 13 elements: + `v` is a `std::vector` with the following 15 elements: ```c 0xA1 // map(1) 0x66 // text(6) 0x62 0x69 0x6E 0x61 0x72 0x79 // "binary" + 0xD8 0x2A // tag(42) 0x44 // bytes(4) 0xCA 0xFE 0xBA 0xBE // content ``` - Note the subtype (42) is **not** serialized, and deserializing `v` would yield the following value: + Note that the subtype is serialized as tag. However, parsing tagged values yield a parse error unless `json::cbor_tag_handler_t::ignore` is passed to `json::from_cbor`. ```json { diff --git a/doc/mkdocs/docs/features/comments.md b/doc/mkdocs/docs/features/comments.md new file mode 100644 index 00000000..f82029ee --- /dev/null +++ b/doc/mkdocs/docs/features/comments.md @@ -0,0 +1,83 @@ +# Comments + +This library does not support comments *by default*. It does so for three reasons: + +1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. +2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: + + > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. + + > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. + +3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this. + +However, you can pass set parameter `ignore_comments` to `#!c true` in the parse function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. + +!!! example + + Consider the following JSON with comments. + + ```json + { + // update in 2006: removed Pluto + "planets": ["Mercury", "Venus", "Earth", "Mars", + "Jupiter", "Uranus", "Neptune" /*, "Pluto" */] + } + ``` + + When calling `parse` without additional argument, a parse error exception is thrown. If `skip_comments` is set to `#! true`, the comments are skipped during parsing: + + ```cpp + #include + #include "json.hpp" + + using json = nlohmann::json; + + int main() + { + std::string s = R"( + { + // update in 2006: removed Pluto + "planets": ["Mercury", "Venus", "Earth", "Mars", + "Jupiter", "Uranus", "Neptune" /*, "Pluto" */] + } + )"; + + try + { + json j = json::parse(s); + } + catch (json::exception &e) + { + std::cout << e.what() << std::endl; + } + + json j = json::parse(s, + /* callback */ nullptr, + /* allow exceptions */ true, + /* skip_comments */ true); + std::cout << j.dump(2) << '\n'; + } + ``` + + Output: + + ``` + [json.exception.parse_error.101] parse error at line 3, column 9: + syntax error while parsing object key - invalid literal; + last read: ' { /'; expected string literal + ``` + + ```json + { + "planets": [ + "Mercury", + "Venus", + "Earth", + "Mars", + "Jupiter", + "Uranus", + "Neptune" + ] + } + ``` \ No newline at end of file diff --git a/doc/mkdocs/docs/features/object_order.md b/doc/mkdocs/docs/features/object_order.md new file mode 100644 index 00000000..86bb253b --- /dev/null +++ b/doc/mkdocs/docs/features/object_order.md @@ -0,0 +1,67 @@ +# Object Order + +The [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs". As such, an implementation does not need to preserve any specific order of object keys. + +The default type `nlohmann::json` uses a `std::map` to store JSON objects, and thus stores object keys **sorted alphabetically**. + +??? example + + ```cpp + #include + #include "json.hpp" + + using json = nlohmann::json; + + int main() + { + json j; + j["one"] = 1; + j["two"] = 2; + j["three"] = 3; + + std::cout << j.dump(2) << '\n'; + } + ``` + + Output: + + ```json + { + "one": 1, + "three": 3, + "two": 2 + } + ``` + +If you do want to preserve the **insertion order**, you can try the type [`nlohmann::ordered_json`](https://github.com/nlohmann/json/issues/2179). + +??? example + + ```cpp + #include + #include + + using ordered_json = nlohmann::ordered_json; + + int main() + { + ordered_json j; + j["one"] = 1; + j["two"] = 2; + j["three"] = 3; + + std::cout << j.dump(2) << '\n'; + } + ``` + + Output: + + ```json + { + "one": 1, + "two": 2, + "three": 3 + } + ``` + +Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)). diff --git a/doc/mkdocs/docs/home/faq.md b/doc/mkdocs/docs/home/faq.md index a9b5af08..af63cfb6 100644 --- a/doc/mkdocs/docs/home/faq.md +++ b/doc/mkdocs/docs/home/faq.md @@ -2,31 +2,6 @@ ## Limitations -### Comments - -!!! question "Questions" - - - Why does the library not support comments? - - Can you add support for JSON5/JSONC/HOCON so that comments are supported? - -This library does not support comments. It does so for three reasons: - -1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. -2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: - - > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. - - > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. - -3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this. - -This library will not support comments in the future. If you wish to use comments, I see three options: - -1. Strip comments before using this library. -2. Use a different JSON library with comment support. -3. Use a format that natively supports comments (e.g., YAML or JSON5). - - ### Relaxed parsing !!! question @@ -69,18 +44,6 @@ No, this is not possible. See for ## Serialization issues -### Order of object keys - -!!! question "Questions" - - - Why are object keys sorted? - - Why is the insertion order of object keys not preserved? - -By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs". - -If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)). - - ### Number precision !!! question diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 9169e4f1..815038a1 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -44,10 +44,12 @@ nav: - features/binary_formats/messagepack.md - features/binary_formats/ubjson.md - features/binary_values.md + - features/comments.md - features/iterators.md - features/json_pointer.md - features/json_patch.md - features/merge_patch.md + - features/object_order.md - features/enum_conversion.md - features/macros.md - Parsing: From d8d499ce9b88dd83000c94efc73ce68bd1034715 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 24 Jul 2020 14:35:52 +0200 Subject: [PATCH 2/8] :memo: add more documentation --- doc/mkdocs/docs/features/binary_formats/cbor.md | 11 ++++++++--- doc/mkdocs/docs/features/binary_values.md | 2 +- doc/mkdocs/docs/features/parsing/sax_interface.md | 3 +++ doc/mkdocs/docs/home/license.md | 2 +- include/nlohmann/json.hpp | 2 -- single_include/nlohmann/json.hpp | 2 -- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/doc/mkdocs/docs/features/binary_formats/cbor.md b/doc/mkdocs/docs/features/binary_formats/cbor.md index daea3a51..daa29be8 100644 --- a/doc/mkdocs/docs/features/binary_formats/cbor.md +++ b/doc/mkdocs/docs/features/binary_formats/cbor.md @@ -64,7 +64,6 @@ binary | *size*: 4294967296..18446744073709551615 | byte string (8 by If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the normal JSON serialization which serializes NaN or Infinity to `null`. - !!! info "Unused CBOR types" The following CBOR types are not used in the conversion: @@ -77,13 +76,16 @@ binary | *size*: 4294967296..18446744073709551615 | byte string (8 by - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - half-precision floats (0xF9) - break (0xFF) +!!! info "Tagged items" + + Binary subtypes will be serialized as tagged items. See [binary values](../binary_values.md#cbor) for an example. + ??? example ```cpp @@ -150,7 +152,6 @@ Double-Precision Float | number_float | 0xFB - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) @@ -159,6 +160,10 @@ Double-Precision Float | number_float | 0xFB CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected. +!!! warning "Tagged items" + + Tagged items will throw a parse error by default. However, they can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`. + ??? example ```cpp diff --git a/doc/mkdocs/docs/features/binary_values.md b/doc/mkdocs/docs/features/binary_values.md index 14cc65b1..4716aac7 100644 --- a/doc/mkdocs/docs/features/binary_values.md +++ b/doc/mkdocs/docs/features/binary_values.md @@ -158,7 +158,7 @@ JSON does not have a binary type, and this library does not introduce a new type ### CBOR -[CBOR](binary_formats/cbor.md) supports binary values, but no subtypes. Any binary value will be serialized as byte strings. The library will choose the smallest representation using the length of the byte array. +[CBOR](binary_formats/cbor.md) supports binary values, but no subtypes. Subtypes will be serialized as tags. Any binary value will be serialized as byte strings. The library will choose the smallest representation using the length of the byte array. ??? example diff --git a/doc/mkdocs/docs/features/parsing/sax_interface.md b/doc/mkdocs/docs/features/parsing/sax_interface.md index ef83a532..73153482 100644 --- a/doc/mkdocs/docs/features/parsing/sax_interface.md +++ b/doc/mkdocs/docs/features/parsing/sax_interface.md @@ -14,6 +14,7 @@ interface json::sax_t { + {abstract} bool number_float(number_float_t val, const string_t& s) + {abstract} bool string(string_t& val) + + {abstract} bool binary(binary_t& val) + {abstract} bool start_object(std::size_t elements) + {abstract} bool end_object() @@ -41,6 +42,8 @@ bool number_float(number_float_t val, const string_t& s); // called when a string is parsed; value is passed and can be safely moved away bool string(string_t& val); +// called when a binary value is parsed; value is passed and can be safely moved away +bool binary(binary& val); // called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known) bool start_object(std::size_t elements); diff --git a/doc/mkdocs/docs/home/license.md b/doc/mkdocs/docs/home/license.md index 9211eddd..f7d0aa82 100644 --- a/doc/mkdocs/docs/home/license.md +++ b/doc/mkdocs/docs/home/license.md @@ -4,7 +4,7 @@ The class is licensed under the [MIT License](http://opensource.org/licenses/MIT): -Copyright © 2013-2019 [Niels Lohmann](http://nlohmann.me) +Copyright © 2013-2020 [Niels Lohmann](http://nlohmann.me) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 753dac35..b4da4887 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -7036,7 +7036,6 @@ class basic_json - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) @@ -7423,7 +7422,6 @@ class basic_json - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0b7d2411..53c174a3 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -23339,7 +23339,6 @@ class basic_json - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) @@ -23726,7 +23725,6 @@ class basic_json - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) From 58167173a29f453626584bed6d9cac8eef02dd58 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 24 Jul 2020 15:25:53 +0200 Subject: [PATCH 3/8] :memo: add more documentation --- doc/mkdocs/docs/features/element_access.md | 108 +++++++++++++++++++++ doc/mkdocs/docs/features/macros.md | 4 + doc/mkdocs/mkdocs.yml | 1 + 3 files changed, 113 insertions(+) create mode 100644 doc/mkdocs/docs/features/element_access.md diff --git a/doc/mkdocs/docs/features/element_access.md b/doc/mkdocs/docs/features/element_access.md new file mode 100644 index 00000000..b6976970 --- /dev/null +++ b/doc/mkdocs/docs/features/element_access.md @@ -0,0 +1,108 @@ +# Element Access + +There are many ways elements in a JSON value can be accessed: + +- unchecked access via `operator[]` +- checked access via `at` +- checked access with default value via `value` +- iterators +- JSON pointers + +## Unchecked access via `operator[]` + +### Overview + +Elements in a JSON object and a JSON array can be accessed via `#!cpp operator[]` similar to a `#!cpp std::map` and a `#!cpp std::vector`, respectively. + +??? example + + Consider the following JSON value: + + ```json + { + "name": "Mary Smith", + "age": 42, + "hobbies": ["hiking", "reading"] + } + ``` + + Assume the value is parsed to a `json` variable `j`. + + | expression | value | + | ---------- | ----- | + | `#!cpp j` | `#!json {"name": "Mary Smith", "age": 42, "hobbies": ["hiking", "reading"]}` | + | `#!cpp j["name"]` | `#!json "Mary Smith"` | + | `#!cpp j["age"]` | `#!json 42` | + | `#!cpp j["hobbies"]` | `#!json ["hiking", "reading"]` | + | `#!cpp j["hobbies"][0]` | `#!json "hiking"` | + | `#!cpp j["hobbies"][1]` | `#!json "reading"` | + +The return value is a reference, so it can be modify the original value. In case the passed object key is non-existing, a `#!json null` value is inserted which can be immediately be overwritten. + +??? example + + ```cpp + j["name"] = "John Smith"; + j["maidenName"] = "Jones"; + ``` + + This code produces the following JSON value: + + ```json + { + "name": "John Smith", + "maidenName": "Jones", + "age": 42, + "hobbies": ["hiking", "reading"] + } + ``` + +When accessing an invalid index (i.e., and index greater than or equal to the array size), the JSON array is resized such that the passed index is the new maximal index. Intermediate values are filled with `#!json null`. + +??? example + + ```cpp + j["hobbies"][0] = "running"; + j["hobbies"][3] = "cooking"; + ``` + + This code produces the following JSON value: + + ```json + { + "name": "John Smith", + "maidenName": "Jones", + "age": 42, + "hobbies": ["running", "reading", null, "cooking"] + } + ``` + +### Notes + +!!! info "Design rationale" + + The library behaves differently to `#!cpp std::vector` and `#!cpp std::map`: + + - `#!cpp std::vector::operator[]` never inserts a new element. + - `#!cpp std::map::operator[]` is not available for const values. + + The type `#!cpp json` wraps all JSON value types. It would be impossible to remove `operator[]` for const objects. At the same time, inserting elements for non-const objects is really convenient as it avoids awkward `insert` calls. To this end, we decided to have an inserting non-const behavior for both arrays and objects. + +!!! info + + The access is unchecked. In case the passed object key does not exist or the passed array index is invalid, no exception is thrown. + +!!! danger + + - It is **undefined behavior** to access a const object with a non-existing key. + - It is **undefined behavior** to access a const array with an invalid index. + - In debug mode, an **assertion** will fire in both cases. You can disable assertions by defining the preprocessor symbol `#!cpp NDEBUG` or redefine the macro [`JSON_ASSERT(x)`](macros.md#json_assertx). + +### Summary + +| scenario | non-const value | const value | +| -------- | ------------- | ----------- | +| access to existing object key | reference to existing value is returned | const reference to existing value is returned | +| access to valid array index | reference to existing value is returned | const reference to existing value is returned | +| access to non-existing object key | reference to newly inserted `#!json null` value is returned | **undefined behavior**; assertion in debug mode | +| access to invalid array index | reference to newly inserted `#!json null` value is returned; any index between previous maximal index and passed index are filled with `#!json null` | **undefined behavior**; assertion in debug mode | diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index 7147be7e..48c71adf 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -2,6 +2,10 @@ Some aspects of the library can be configured by defining preprocessor macros before including the `json.hpp` header. +## `JSON_ASSERT(x)` + +The default value is `#!cpp assert(x)`. + ## `JSON_CATCH_USER(exception)` This macro overrides `#!cpp catch` calls inside the library. The argument is the type of the exception to catch. As of version 3.8.0, the library only catches `std::out_of_range` exceptions internally to rethrow them as [`json::out_of_range`](../home/exceptions.md#out-of-range) exceptions. The macro is always followed by a scope. diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 815038a1..962f5fd5 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -45,6 +45,7 @@ nav: - features/binary_formats/ubjson.md - features/binary_values.md - features/comments.md + - features/element_access.md - features/iterators.md - features/json_pointer.md - features/json_patch.md From 6e5be17b624f2ef4054dcdd057110882990ad3a6 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Jul 2020 14:40:50 +0200 Subject: [PATCH 4/8] :memo: add more documentation --- doc/mkdocs/docs/api/basic_json/dump.md | 72 +++++++++++++++++ doc/mkdocs/docs/api/basic_json/index.md | 11 +++ doc/mkdocs/docs/api/basic_json/meta.md | 45 +++++++++++ .../features/element_access/checked_access.md | 77 +++++++++++++++++++ .../features/element_access/default_value.md | 32 ++++++++ .../docs/features/element_access/index.md | 9 +++ .../unchecked_access.md} | 24 +++--- doc/mkdocs/mkdocs.yml | 22 ++++-- 8 files changed, 271 insertions(+), 21 deletions(-) create mode 100644 doc/mkdocs/docs/api/basic_json/dump.md create mode 100644 doc/mkdocs/docs/api/basic_json/index.md create mode 100644 doc/mkdocs/docs/api/basic_json/meta.md create mode 100644 doc/mkdocs/docs/features/element_access/checked_access.md create mode 100644 doc/mkdocs/docs/features/element_access/default_value.md create mode 100644 doc/mkdocs/docs/features/element_access/index.md rename doc/mkdocs/docs/features/{element_access.md => element_access/unchecked_access.md} (90%) diff --git a/doc/mkdocs/docs/api/basic_json/dump.md b/doc/mkdocs/docs/api/basic_json/dump.md new file mode 100644 index 00000000..73d6b428 --- /dev/null +++ b/doc/mkdocs/docs/api/basic_json/dump.md @@ -0,0 +1,72 @@ +# basic_json::dump + +```cpp +string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const +``` + +Serialization function for JSON values. The function tries to mimic +Python's `json.dumps()` function, and currently supports its `indent` +and `ensure_ascii` parameters. + +## Parameters + +`indent` (in) +: If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + +`indent_char` (in) +: The character to use for indentation if `indent` is + greater than `0`. The default is ` ` (space). + +`ensure_ascii` (in) +: If `ensure_ascii` is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + +`error_handler` (in) +: how to react on decoding errors; there are three + possible values: `strict` (throws and exception in case a decoding error + occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). + +## Return value + +string containing the serialization of the JSON value + +## Exception safety + +Strong guarantee: if an exception is thrown, there are no +changes to any JSON value. + +## Complexity + +Linear. + +## Notes + +Binary values are serialized as object containing two keys: + +- "bytes": an array of bytes as integers +- "subtype": the subtype as integer or `#!json null` if the binary has no subtype + +## Example + +The following example shows the effect of different `indent`, + `indent_char`, and `ensure_ascii` parameters to the result of the + serialization. + +```cpp +--8<-- "examples/dump.cpp" +``` + +Output: + +```json +--8<-- "examples/dump.output" +``` diff --git a/doc/mkdocs/docs/api/basic_json/index.md b/doc/mkdocs/docs/api/basic_json/index.md new file mode 100644 index 00000000..f34c20f9 --- /dev/null +++ b/doc/mkdocs/docs/api/basic_json/index.md @@ -0,0 +1,11 @@ +# Overview + +## Member functions + +### Object inspection + +- [dump](dump.md) - serialization + +## Static functions + +- [meta](meta.md) - returns version information on the library diff --git a/doc/mkdocs/docs/api/basic_json/meta.md b/doc/mkdocs/docs/api/basic_json/meta.md new file mode 100644 index 00000000..fd577545 --- /dev/null +++ b/doc/mkdocs/docs/api/basic_json/meta.md @@ -0,0 +1,45 @@ +# basic_json::meta + +```cpp +static basic_json meta(); +``` + +This function returns a JSON object with information about the library, +including the version number and information on the platform and compiler. + +## Return value + +JSON object holding version information + +key | description +----------- | --------------- +`compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). +`copyright` | The copyright line for the library as string. +`name` | The name of the library as string. +`platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. +`url` | The URL of the project as string. +`version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + +## Exception safety + +Strong guarantee: if an exception is thrown, there are no +changes to any JSON value. + +## Complexity + +Constant. + +## Example + +The following code shows an example output of the `meta()` +function. + +```cpp +--8<-- "examples/meta.cpp" +``` + +Output: + +```json +--8<-- "examples/meta.output" +``` diff --git a/doc/mkdocs/docs/features/element_access/checked_access.md b/doc/mkdocs/docs/features/element_access/checked_access.md new file mode 100644 index 00000000..095dff2d --- /dev/null +++ b/doc/mkdocs/docs/features/element_access/checked_access.md @@ -0,0 +1,77 @@ +# Checked access: at + +## Overview + +The `#!cpp at()` member function performs checked access; that is, it returns a reference to the desired value if it exists and throws a [`basic_json::out_of_range` exception](../../home/exceptions.md#out-of-range) otherwise. + +??? example + + Consider the following JSON value: + + ```json + { + "name": "Mary Smith", + "age": 42, + "hobbies": ["hiking", "reading"] + } + ``` + + Assume the value is parsed to a `json` variable `j`. + + | expression | value | + | ---------- | ----- | + | `#!cpp j` | `#!json {"name": "Mary Smith", "age": 42, "hobbies": ["hiking", "reading"]}` | + | `#!cpp j.at("name")` | `#!json "Mary Smith"` | + | `#!cpp j.at("age")` | `#!json 42` | + | `#!cpp j.at("hobbies")` | `#!json ["hiking", "reading"]` | + | `#!cpp j.at("hobbies").at(0)` | `#!json "hiking"` | + | `#!cpp j.at("hobbies").at(1)` | `#!json "reading"` | + +The return value is a reference, so it can be modify the original value. + +??? example + + ```cpp + j.at("name") = "John Smith"; + ``` + + This code produces the following JSON value: + + ```json + { + "name": "John Smith", + "age": 42, + "hobbies": ["hiking", "reading"] + } + ``` + +When accessing an invalid index (i.e., and index greater than or equal to the array size) or the passed object key is non-existing, an exception is thrown. + +??? example + + ```cpp + j.at("hobbies").at(3) = "cooking"; + ``` + + This code produces the following exception: + + ``` + [json.exception.out_of_range.401] array index 3 is out of range + ``` + +## Notes + + +!!! failure "Exceptions" + + - `at` can only be used with objects (with a string argument) or with arrays (with a numeric argument). For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error304) is thrown. + - [`basic_json::out_of_range` exception](../../home/exceptions.md#out-of-range) exceptions are thrown if the provided key is not found in an object or the provided index is invalid. + +## Summary + +| scenario | non-const value | const value | +| -------- | ------------- | ----------- | +| access to existing object key | reference to existing value is returned | const reference to existing value is returned | +| access to valid array index | reference to existing value is returned | const reference to existing value is returned | +| access to non-existing object key | `basic_json::out_of_range` exception is thrown | `basic_json::out_of_range` exception is thrown | +| access to invalid array index | `basic_json::out_of_range` exception is thrown | `basic_json::out_of_range` exception is thrown | diff --git a/doc/mkdocs/docs/features/element_access/default_value.md b/doc/mkdocs/docs/features/element_access/default_value.md new file mode 100644 index 00000000..02b4fea3 --- /dev/null +++ b/doc/mkdocs/docs/features/element_access/default_value.md @@ -0,0 +1,32 @@ +# Access with default value: value + +## Overview + +In many situations such as configuration files, missing values are not exceptional, but may be treated as if a default value was present. + +??? example + + Consider the following JSON value: + + ```json + { + "logOutput": "result.log", + "append": true + } + ``` + + Assume the value is parsed to a `json` variable `j`. + + | expression | value | + | ---------- | ----- | + | `#!cpp j` | `#!json {"logOutput": "result.log", "append": true}` | + | `#!cpp j.value("logOutput", "logfile.log")` | `#!json "result.log"` | + | `#!cpp j.value("append", true)` | `#!json true` | + | `#!cpp j.value("append", false)` | `#!json true` | + | `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` | + +## Note + +!!! failure "Exceptions" + + - `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown. diff --git a/doc/mkdocs/docs/features/element_access/index.md b/doc/mkdocs/docs/features/element_access/index.md new file mode 100644 index 00000000..1755fe8c --- /dev/null +++ b/doc/mkdocs/docs/features/element_access/index.md @@ -0,0 +1,9 @@ +# Overview + +There are many ways elements in a JSON value can be accessed: + +- unchecked access via [`operator[]`](unchecked_access.md) +- checked access via [`at`](checked_access.md) +- access with default value via [`value`](default_value.md) +- iterators +- JSON pointers diff --git a/doc/mkdocs/docs/features/element_access.md b/doc/mkdocs/docs/features/element_access/unchecked_access.md similarity index 90% rename from doc/mkdocs/docs/features/element_access.md rename to doc/mkdocs/docs/features/element_access/unchecked_access.md index b6976970..f8667c81 100644 --- a/doc/mkdocs/docs/features/element_access.md +++ b/doc/mkdocs/docs/features/element_access/unchecked_access.md @@ -1,16 +1,6 @@ -# Element Access +# Unchecked access: operator[] -There are many ways elements in a JSON value can be accessed: - -- unchecked access via `operator[]` -- checked access via `at` -- checked access with default value via `value` -- iterators -- JSON pointers - -## Unchecked access via `operator[]` - -### Overview +## Overview Elements in a JSON object and a JSON array can be accessed via `#!cpp operator[]` similar to a `#!cpp std::map` and a `#!cpp std::vector`, respectively. @@ -77,7 +67,7 @@ When accessing an invalid index (i.e., and index greater than or equal to the ar } ``` -### Notes +## Notes !!! info "Design rationale" @@ -96,9 +86,13 @@ When accessing an invalid index (i.e., and index greater than or equal to the ar - It is **undefined behavior** to access a const object with a non-existing key. - It is **undefined behavior** to access a const array with an invalid index. - - In debug mode, an **assertion** will fire in both cases. You can disable assertions by defining the preprocessor symbol `#!cpp NDEBUG` or redefine the macro [`JSON_ASSERT(x)`](macros.md#json_assertx). + - In debug mode, an **assertion** will fire in both cases. You can disable assertions by defining the preprocessor symbol `#!cpp NDEBUG` or redefine the macro [`JSON_ASSERT(x)`](../macros.md#json_assertx). -### Summary +!!! failure "Exceptions" + + `operator[]` can only be used with objects (with a string argument) or with arrays (with a numeric argument). For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error305) is thrown. + +## Summary | scenario | non-const value | const value | | -------- | ------------- | ----------- | diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 962f5fd5..d3799bdc 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -45,19 +45,23 @@ nav: - features/binary_formats/ubjson.md - features/binary_values.md - features/comments.md - - features/element_access.md + - Element Access: + - features/element_access/index.md + - features/element_access/unchecked_access.md + - features/element_access/checked_access.md + - features/element_access/default_value.md - features/iterators.md - features/json_pointer.md - features/json_patch.md - features/merge_patch.md - features/object_order.md + - Parsing: + - features/parsing/index.md + - features/parsing/parse_exceptions.md + - features/parsing/parser_callbacks.md + - features/parsing/sax_interface.md - features/enum_conversion.md - features/macros.md - - Parsing: - - features/parsing/index.md - - features/parsing/parse_exceptions.md - - features/parsing/parser_callbacks.md - - features/parsing/sax_interface.md - features/types.md - Integration: - integration/index.md @@ -65,6 +69,11 @@ nav: - integration/package_managers.md - Doxygen: - doxygen/index.html + - API: + - basic_json: + - api/basic_json/index.md + - api/basic_json/dump.md + - api/basic_json/meta.md # Extras extra: @@ -83,6 +92,7 @@ extra: # Extensions markdown_extensions: - admonition + - def_list - codehilite: guess_lang: false - toc: From fad14aabe7b69c860cfca541253926ff46c85ff8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Jul 2020 14:41:06 +0200 Subject: [PATCH 5/8] :memo: update output of meta function --- doc/examples/meta.output | 4 ++-- include/nlohmann/json.hpp | 2 +- single_include/nlohmann/json.hpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/examples/meta.output b/doc/examples/meta.output index aa47599b..eb66a35f 100644 --- a/doc/examples/meta.output +++ b/doc/examples/meta.output @@ -2,9 +2,9 @@ "compiler": { "c++": "201103", "family": "clang", - "version": "11.0.3 (clang-1103.0.32.62)" + "version": "12.0.0 (clang-1200.0.22.19)" }, - "copyright": "(C) 2013-2017 Niels Lohmann", + "copyright": "(C) 2013-2020 Niels Lohmann", "name": "JSON for Modern C++", "platform": "apple", "url": "https://github.com/nlohmann/json", diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index b4da4887..96526d17 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -344,7 +344,7 @@ class basic_json { basic_json result; - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["copyright"] = "(C) 2013-2020 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f08967ce..3212fd4c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -16501,7 +16501,7 @@ class basic_json detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false - ) + ) { return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions, ignore_comments); @@ -16647,7 +16647,7 @@ class basic_json { basic_json result; - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["copyright"] = "(C) 2013-2020 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = @@ -25041,7 +25041,7 @@ template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value&& is_nothrow_move_assignable::value -) + ) { j1.swap(j2); } From 7bd6242f049b49aa9959652168e6f92d90e2c8e3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Jul 2020 22:00:28 +0200 Subject: [PATCH 6/8] :memo: add more documentation --- doc/mkdocs/docs/api/basic_json/dump.md | 2 +- doc/mkdocs/docs/api/basic_json/index.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/api/basic_json/dump.md b/doc/mkdocs/docs/api/basic_json/dump.md index 73d6b428..7fe57f8c 100644 --- a/doc/mkdocs/docs/api/basic_json/dump.md +++ b/doc/mkdocs/docs/api/basic_json/dump.md @@ -14,7 +14,7 @@ and `ensure_ascii` parameters. ## Parameters `indent` (in) -: If indent is nonnegative, then array elements and object +: If `indent` is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of `0` will only insert newlines. `-1` (the default) selects the most compact representation. diff --git a/doc/mkdocs/docs/api/basic_json/index.md b/doc/mkdocs/docs/api/basic_json/index.md index f34c20f9..e60b621a 100644 --- a/doc/mkdocs/docs/api/basic_json/index.md +++ b/doc/mkdocs/docs/api/basic_json/index.md @@ -1,5 +1,9 @@ # Overview +!!! note + + This page is under construction. + ## Member functions ### Object inspection From 9c75c7eacecf96b6ba978f3917cdeb7d936f2834 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Jul 2020 22:04:42 +0200 Subject: [PATCH 7/8] :wrench: clean generated XML files --- doc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Makefile b/doc/Makefile index 07e417c1..35e8b5aa 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -43,7 +43,7 @@ check_output: $(EXAMPLES:.cpp=.test) clean: - rm -fr me.nlohmann.json.docset html $(EXAMPLES:.cpp=) + rm -fr me.nlohmann.json.docset html xml $(EXAMPLES:.cpp=) ########################################################################## From a34070d36a052f9323fb0d4b70ba2ff7bbea6ac0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Jul 2020 22:28:14 +0200 Subject: [PATCH 8/8] :white_check_mark: fix test for meta() function --- test/src/unit-meta.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/unit-meta.cpp b/test/src/unit-meta.cpp index 243de046..081f4b00 100644 --- a/test/src/unit-meta.cpp +++ b/test/src/unit-meta.cpp @@ -39,7 +39,7 @@ TEST_CASE("version information") json j = json::meta(); CHECK(j["name"] == "JSON for Modern C++"); - CHECK(j["copyright"] == "(C) 2013-2017 Niels Lohmann"); + CHECK(j["copyright"] == "(C) 2013-2020 Niels Lohmann"); CHECK(j["url"] == "https://github.com/nlohmann/json"); CHECK(j["version"] == json( {