From 29a647a08bcf05d877362d72e844850fec12425c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 29 Sep 2026 19:50:03 +0200 Subject: [PATCH] Correct documentation errors found while hunting for bugs - patch/patch_inplace: list the JSON pointer errors parse_error.106-109 and out_of_range.402/404, and quote the actual parse_error.105 message. - unflatten: list parse_error.106/107/108 and out_of_range.404. - to_bson: list out_of_range.415 (binary subtype above 255) and note that 412 and 415 are new in 3.13.0. - to_string: state that string_t must be convertible to std::string, also in the StringType requirements table. - JSON Lines: a `while (input >> j)` loop also throws after the last value for concatenated JSON values; show a loop that works for both. - BON8: a string gets 0xFF only if nothing follows it in the message; a string at the end of an array or object is ended by 0xFE. - custom_string_type.hpp: add operator+=(char), which the "Always required" list asks for (json_pointer::to_string, flatten, unflatten, and diff did not compile), and an ADL int_to_string for diff and items. Signed-off-by: Niels Lohmann --- docs/mkdocs/docs/api/basic_json/patch.md | 16 ++++++++++++++- .../docs/api/basic_json/patch_inplace.md | 16 ++++++++++++++- docs/mkdocs/docs/api/basic_json/to_bson.md | 4 ++++ docs/mkdocs/docs/api/basic_json/to_string.md | 3 ++- docs/mkdocs/docs/api/basic_json/unflatten.md | 9 +++++++++ .../docs/examples/custom_string_type.hpp | 20 +++++++++++++++---- .../docs/features/binary_formats/bon8.md | 7 ++++--- .../docs/features/parsing/json_lines.md | 20 +++++++++++++++---- .../features/types/template_parameters.md | 1 + 9 files changed, 82 insertions(+), 14 deletions(-) diff --git a/docs/mkdocs/docs/api/basic_json/patch.md b/docs/mkdocs/docs/api/basic_json/patch.md index ab0232691..8c2d7f231 100644 --- a/docs/mkdocs/docs/api/basic_json/patch.md +++ b/docs/mkdocs/docs/api/basic_json/patch.md @@ -26,10 +26,24 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va - Throws [`parse_error.104`](../../home/exceptions.md#jsonexceptionparse_error104) if the JSON patch does not consist of an array of objects. - Throws [`parse_error.105`](../../home/exceptions.md#jsonexceptionparse_error105) if the JSON patch is malformed (e.g., - mandatory attributes are missing); example: `"operation add must have member path"`. + mandatory attributes are missing); example: `"operation 'add' must have member 'path'"`. - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if an array index is out of range. +- Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in a "path" or + "from" member begins with '0'; example: `"array index '01' must not begin with '0'"`. +- Throws [`parse_error.107`](../../home/exceptions.md#jsonexceptionparse_error107) if a "path" or "from" member is not + empty and does not begin with a slash (`/`); example: `"JSON pointer must be empty or begin with '/' - was: 'a'"`. +- Throws [`parse_error.108`](../../home/exceptions.md#jsonexceptionparse_error108) if a tilde (`~`) in a "path" or + "from" member is not followed by `0` or `1`; example: `"escape character '~' must be followed with '0' or '1'"`. +- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a "path" or + "from" member is not a number; example: `"array index 'foo' is not a number"`. +- Throws [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if the array index `-` is used + where an existing element is required (the "path" of "replace", the "from" of "move" and "copy"); example: + `"array index '-' (3) is out of range"`. - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a JSON pointer inside the patch could not be resolved successfully in the current JSON value; example: `"key baz not found"`. +- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token of a JSON + pointer inside the patch cannot be resolved, e.g., `-` in a "remove" operation or `1a` for an array; example: + `"unresolved reference token '-'"`. - Throws [`out_of_range.405`](../../home/exceptions.md#jsonexceptionout_of_range405) if JSON pointer has no parent ("add", "remove", "move") - Throws [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) if an "add" operation's target diff --git a/docs/mkdocs/docs/api/basic_json/patch_inplace.md b/docs/mkdocs/docs/api/basic_json/patch_inplace.md index 0b6605314..2d46a2cc8 100644 --- a/docs/mkdocs/docs/api/basic_json/patch_inplace.md +++ b/docs/mkdocs/docs/api/basic_json/patch_inplace.md @@ -22,10 +22,24 @@ No guarantees, value may be corrupted by an unsuccessful patch operation. - Throws [`parse_error.104`](../../home/exceptions.md#jsonexceptionparse_error104) if the JSON patch does not consist of an array of objects. - Throws [`parse_error.105`](../../home/exceptions.md#jsonexceptionparse_error105) if the JSON patch is malformed (e.g., - mandatory attributes are missing); example: `"operation add must have member path"`. + mandatory attributes are missing); example: `"operation 'add' must have member 'path'"`. - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if an array index is out of range. +- Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in a "path" or + "from" member begins with '0'; example: `"array index '01' must not begin with '0'"`. +- Throws [`parse_error.107`](../../home/exceptions.md#jsonexceptionparse_error107) if a "path" or "from" member is not + empty and does not begin with a slash (`/`); example: `"JSON pointer must be empty or begin with '/' - was: 'a'"`. +- Throws [`parse_error.108`](../../home/exceptions.md#jsonexceptionparse_error108) if a tilde (`~`) in a "path" or + "from" member is not followed by `0` or `1`; example: `"escape character '~' must be followed with '0' or '1'"`. +- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a "path" or + "from" member is not a number; example: `"array index 'foo' is not a number"`. +- Throws [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if the array index `-` is used + where an existing element is required (the "path" of "replace", the "from" of "move" and "copy"); example: + `"array index '-' (3) is out of range"`. - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a JSON pointer inside the patch could not be resolved successfully in the current JSON value; example: `"key baz not found"`. +- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token of a JSON + pointer inside the patch cannot be resolved, e.g., `-` in a "remove" operation or `1a` for an array; example: + `"unresolved reference token '-'"`. - Throws [`out_of_range.405`](../../home/exceptions.md#jsonexceptionout_of_range405) if JSON pointer has no parent ("add", "remove", "move") - Throws [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) if an "add" operation's target diff --git a/docs/mkdocs/docs/api/basic_json/to_bson.md b/docs/mkdocs/docs/api/basic_json/to_bson.md index 370afa654..72ea54c38 100644 --- a/docs/mkdocs/docs/api/basic_json/to_bson.md +++ b/docs/mkdocs/docs/api/basic_json/to_bson.md @@ -43,6 +43,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va - Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array, string, or binary value exceeds the range of the 32-bit BSON length field; example: `"BSON length 2147483661 exceeds maximum of 2147483647"` +- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value + exceeds 255, the maximum of the BSON binary subtype; example: + `"subtype 300 is too large for the BSON binary subtype (max 255)"` ## Complexity @@ -92,4 +95,5 @@ pass before anything is written. ## Version history - Added in version 3.4.0. +- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0. - Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0. diff --git a/docs/mkdocs/docs/api/basic_json/to_string.md b/docs/mkdocs/docs/api/basic_json/to_string.md index 2df16c132..8c75db84c 100644 --- a/docs/mkdocs/docs/api/basic_json/to_string.md +++ b/docs/mkdocs/docs/api/basic_json/to_string.md @@ -10,7 +10,8 @@ This function implements a user-defined to_string for JSON objects. ## Template parameters `BasicJsonType` -: a specialization of [`basic_json`](index.md) +: a specialization of [`basic_json`](index.md) whose [`string_t`](string_t.md) is convertible to `#!cpp std::string`; + for other string types, use [`dump`](dump.md), which returns a `string_t` ## Return value diff --git a/docs/mkdocs/docs/api/basic_json/unflatten.md b/docs/mkdocs/docs/api/basic_json/unflatten.md index ac88cd73e..a8f0a60b0 100644 --- a/docs/mkdocs/docs/api/basic_json/unflatten.md +++ b/docs/mkdocs/docs/api/basic_json/unflatten.md @@ -27,8 +27,17 @@ The function can throw the following exceptions: - Throws [`type_error.315`](../../home/exceptions.md#jsonexceptiontype_error315) if object values are not primitive - Throws [`type_error.313`](../../home/exceptions.md#jsonexceptiontype_error313) if a key (JSON pointer) leads to a conflicting nesting; example: `"invalid value to unflatten"` +- Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in a key begins + with '0'; example: `"array index '01' must not begin with '0'"` +- Throws [`parse_error.107`](../../home/exceptions.md#jsonexceptionparse_error107) if a key is not empty and does not + begin with a slash (`/`); example: `"JSON pointer must be empty or begin with '/' - was: 'a'"` +- Throws [`parse_error.108`](../../home/exceptions.md#jsonexceptionparse_error108) if a tilde (`~`) in a key is not + followed by `0` or `1`; example: `"escape character '~' must be followed with '0' or '1'"` - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a number; example: `"array index 'one' is not a number"` +- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array + (because one of its keys is `0`) and another key at that level cannot be an array index; example: + `"unresolved reference token 'x'"` ## Complexity diff --git a/docs/mkdocs/docs/examples/custom_string_type.hpp b/docs/mkdocs/docs/examples/custom_string_type.hpp index ec48501fc..f09fe0f98 100644 --- a/docs/mkdocs/docs/examples/custom_string_type.hpp +++ b/docs/mkdocs/docs/examples/custom_string_type.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -8,10 +9,10 @@ // and nothing more of std::string's interface. // // Covers the "Always required" members, the extras needed for the binary -// formats, and the extras needed for JSON Pointer / flatten / unflatten / -// diff. Extending it further (e.g. for std::hash or to_bson) is -// a matter of adding the extra members listed in the "Required for other -// functionality" table. +// formats, JSON Pointer / flatten / unflatten, and the int_to_string overload +// needed for diff and items. Extending it further (e.g. for +// std::hash or to_bson) is a matter of adding the extra members +// listed in the "Required for other functionality" table. // // See https://json.nlohmann.me/features/types/template_parameters/#stringtype class custom_string_type @@ -93,6 +94,11 @@ class custom_string_type data_.append(other.data_); return *this; } + custom_string_type& operator+=(char c) + { + data_.push_back(c); + return *this; + } size_type find_first_of(char c, size_type pos = 0) const { @@ -116,6 +122,12 @@ class custom_string_type return data_.end(); } + // found by ADL; converts array indices to keys in diff and items + friend void int_to_string(custom_string_type& target, std::size_t value) + { + target.data_ = std::to_string(value); + } + friend bool operator==(const custom_string_type& lhs, const custom_string_type& rhs) { return lhs.data_ == rhs.data_; diff --git a/docs/mkdocs/docs/features/binary_formats/bon8.md b/docs/mkdocs/docs/features/binary_formats/bon8.md index ff5d6d8a7..d2d20da19 100644 --- a/docs/mkdocs/docs/features/binary_formats/bon8.md +++ b/docs/mkdocs/docs/features/binary_formats/bon8.md @@ -53,8 +53,9 @@ The library uses the following mapping from JSON values types to BON8 types acco An integer that takes 2 to 4 bytes starts with a UTF-8 lead byte (0xC2..0xF7) that is followed by a byte that cannot continue a UTF-8 character: 0x00..0x7F for positive and 0xC0..0xFF for negative integers. A string is terminated by -0xFF only if it is empty, if another string follows it, or if it is the last value of the message; otherwise, the first -byte of the next value ends it. +0xFF only if it is empty, if another string follows it, or if nothing follows it in the message; otherwise, the byte +after it ends it: the first byte of the next value, or the 0xFE that ends an array or object. For example, `["e"]` is +serialized as 0x81 0x65 0xFF, but `[1,2,3,4,"e"]` as 0x85 0x91 0x92 0x93 0x94 0x65 0xFE. !!! success "Complete mapping" @@ -140,7 +141,7 @@ Non-negative integers are read as number_unsigned, negative integers as number_i arrays and objects with up to four elements that are terminated by 0xFE, unsorted object keys, or a 0xFF after a string that would also end without it, are accepted. A second 0xFF is not a terminator but an empty string. - Strings must be valid UTF-8, and the last string of a message must be terminated by 0xFF. + Strings must be valid UTF-8, and a string at the very end of a message must be terminated by 0xFF. !!! info diff --git a/docs/mkdocs/docs/features/parsing/json_lines.md b/docs/mkdocs/docs/features/parsing/json_lines.md index fb1481819..5ecf9d4af 100644 --- a/docs/mkdocs/docs/features/parsing/json_lines.md +++ b/docs/mkdocs/docs/features/parsing/json_lines.md @@ -46,8 +46,20 @@ JSON Lines input with more than one value is treated as invalid JSON by the [`pa } ``` - with a JSON Lines input does not work, because the parser will try to parse one value after the last one. + with a JSON Lines input does not work, because the parser will try to parse one value after the last one and throw + a [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) exception. The same happens for a + stream of *concatenated* (non-newline-delimited) JSON values: `operator>>` reads them one at a time, but the loop + above throws after the last value. To read either format with `operator>>`, check for the end of the stream before + each read: - This is different from parsing a stream of *concatenated* (non-newline-delimited) JSON values, for which - `operator>>` does work, provided that a value that is a number is followed by whitespace -- see its - [notes](../../api/operator_gtgt.md#notes) for details. + ```cpp + json j; + while (input >> std::ws && input.peek() != std::char_traits::eof()) + { + input >> j; + std::cout << j << std::endl; + } + ``` + + A value that is a number must be followed by whitespace -- see the [notes](../../api/operator_gtgt.md#notes) of + `operator>>` for details. diff --git a/docs/mkdocs/docs/features/types/template_parameters.md b/docs/mkdocs/docs/features/types/template_parameters.md index e6bfd1ca8..6bee1d5bb 100644 --- a/docs/mkdocs/docs/features/types/template_parameters.md +++ b/docs/mkdocs/docs/features/types/template_parameters.md @@ -394,6 +394,7 @@ using array_t = ArrayType>; | [`to_bson`](../../api/basic_json/to_bson.md) | `find(value_type)` and `npos` | | [`parse`](../../api/basic_json/parse.md) from a `string_t` | the input adapters must accept it; otherwise pass a character range | | `#!cpp operator<<(std::ostream&, const json_pointer&)` | streamability to `#!cpp std::ostream` | +| [`to_string`](../../api/basic_json/to_string.md) | conversion of `StringType` to `#!cpp std::string` (the function returns a `#!cpp std::string`) | | exception messages | `data()` and `size()`, or `begin()` and `end()` | ### Compatible types