docs: document how discarded values are removed by the parser callback (#5354)

Follow-up to #5342, which fixed the parser callback leaving a discarded
member behind when an array or a value under an object key was rejected.
The documentation of parser_callback_t only stated that discarded values
in structured types are skipped, without saying that this covers object
parents and that the key is removed along with the value, so there was no
way to tell the fixed behavior from the buggy one.

Spell out the discarding rules, add an example that exercises the cases
the fix repaired, and correct the return value description: a discarded
top-level value is replaced by null, not by "an empty discarded object".

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-08-04 08:45:02 +02:00
committed by GitHub
parent 85889e8843
commit 1c63a120b6
3 changed files with 78 additions and 2 deletions
@@ -29,7 +29,14 @@ Discarding a value (i.e., returning `#!cpp false`) has different effects dependi
called:
- Discarded values in structured types are skipped. That is, the parser will behave as if the discarded value was never
read.
read. This holds for every value type and for both kinds of parent: a discarded element is removed from the
surrounding array, and a discarded member is removed from the surrounding object together with its key.
- Arrays and objects can be discarded either at their `parse_event_t::array_start`/`parse_event_t::object_start` event
or at their `parse_event_t::array_end`/`parse_event_t::object_end` event, and both remove the whole value. Discarding
it at the start event also means the callback is called neither for the content of the value nor for its matching end
event.
- Discarding a `parse_event_t::key` event discards the whole object member. The callback is still called for the
associated value, but its return value has no further effect.
- In case a value outside a structured type is skipped, it is replaced with `null`. This case happens if the top-level
element is skipped.
@@ -49,7 +56,7 @@ called:
## Return value
Whether the JSON value which called the function during parsing should be kept (`#!cpp true`) or not (`#!cpp false`). In
the latter case, it is either skipped completely or replaced by an empty discarded object.
the latter case, it is skipped completely, or replaced by `null` if it is the top-level value.
## Examples
@@ -68,6 +75,21 @@ the latter case, it is either skipped completely or replaced by an empty discard
--8<-- "examples/parse__string__parser_callback_t.output"
```
??? example
The example below shows where discarded values are removed. The array and the number are discarded in different
ways, but in each case the parse result contains neither the value nor its key.
```cpp
--8<-- "examples/parser_callback_t.cpp"
```
Output:
```json
--8<-- "examples/parser_callback_t.output"
```
## See also
- [parse](parse.md) deserialize from a compatible input
@@ -76,3 +98,5 @@ the latter case, it is either skipped completely or replaced by an empty discard
## Version history
- Added in version 1.0.0.
- Fixed in version 3.13.0 to also remove discarded values from a parent object; before, discarding an array or a value
stored under an object key left a discarded member behind, which made the parse result serialize to invalid JSON.
@@ -0,0 +1,47 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// a JSON text with an array and a number inside an object
auto text = R"({"IDs": [116, 943], "Width": 800})";
// discard the array when the parser reads its opening bracket
json j_array_start = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & /*parsed*/)
{
return event != json::parse_event_t::array_start;
});
// discard the same array when the parser reads its closing bracket
json j_array_end = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & /*parsed*/)
{
return event != json::parse_event_t::array_end;
});
// discard the number, but keep its key
json j_value = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & parsed)
{
return !(event == json::parse_event_t::value && parsed == json(800));
});
// discard the key of the number
json j_key = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & parsed)
{
return !(event == json::parse_event_t::key && parsed == json("Width"));
});
// discard the top-level object
json j_root = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & /*parsed*/)
{
return event != json::parse_event_t::object_end;
});
// in every case, the discarded value is removed together with its key
std::cout << j_array_start << '\n'
<< j_array_end << '\n'
<< j_value << '\n'
<< j_key << '\n'
<< j_root << '\n';
}
@@ -0,0 +1,5 @@
{"Width":800}
{"Width":800}
{"IDs":[116,943]}
{"IDs":[116,943]}
null