mirror of
https://github.com/nlohmann/json.git
synced 2026-07-12 13:35:13 +00:00
Document a duplicate-object-key rejection recipe (#5259)
* 📡 Document a duplicate-object-key rejection recipe RFC 8259 leaves handling of duplicate object keys to the implementation; this library silently keeps only the last value for a repeated key. Discussion #5085 asked for an opt-in rejection mode. Decision: don't change library behavior, but document the existing parser-callback workaround instead. Adds a "Recipe: rejecting duplicate object keys" section to parser_callbacks.md, adapted from a community-contributed workaround. Fixed an off-by-one bug in the original snippet: object_start reports the depth of the object's parent, while key events inside that object report depth+1, so indexing the per-depth key set with the same depth in both places caused an out-of-bounds access on nested objects. Verified the published snippet compiles and behaves correctly for flat duplicates, nested duplicates, sibling objects sharing key names, and arrays of objects. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Cross-link the duplicate-key recipe with the existing object_t behavior docs object_t.md and features/types/index.md already document that duplicate object keys resolve to an unspecified value (RFC 8259 leaves this to the implementation). The new recipe's intro overstated this as a guaranteed "last value wins" rule, which isn't true in general -- parsing text keeps the last value, but constructing from an initializer list keeps the first. Reworded the recipe to point at object_t's "unspecified" behavior instead of asserting a specific rule, and added cross-links from both existing pages to the new recipe. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Turn the duplicate-key recipe into a standalone, compiled example Replace the inline code fence in the "rejecting duplicate object keys" recipe with a proper docs/mkdocs/docs/examples/*.cpp + .output pair, included via --8<-- like every other example on the site. The .output file was generated by running it through the project's actual example build (docs/Makefile: single_include, -std=c++11, -DJSON_USE_GLOBAL_UDLS=0) and cross-checked with `make check_output`, and the source passes the pinned astyle 3.4.13 formatting unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
json parse_strict(const std::string& input)
|
||||
{
|
||||
// one key set per nesting depth, reused across sibling objects
|
||||
std::vector<std::unordered_set<std::string>> keys;
|
||||
|
||||
auto reject_duplicate_keys = [&](int depth, json::parse_event_t event, json & parsed)
|
||||
{
|
||||
if (event == json::parse_event_t::object_start)
|
||||
{
|
||||
// keys of this object are reported at depth+1 (see the event table above)
|
||||
const auto child_depth = static_cast<std::size_t>(depth) + 1;
|
||||
if (keys.size() <= child_depth)
|
||||
{
|
||||
keys.resize(child_depth + 1);
|
||||
}
|
||||
keys[child_depth].clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == json::parse_event_t::key)
|
||||
{
|
||||
auto& seen = keys[static_cast<std::size_t>(depth)];
|
||||
const auto& key = parsed.get_ref<const std::string&>();
|
||||
if (!seen.insert(key).second)
|
||||
{
|
||||
throw std::runtime_error("duplicate JSON object key: " + key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return json::parse(input, reject_duplicate_keys);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// parsing succeeds when all keys are unique
|
||||
json j = parse_strict(R"({"one": 1, "two": 2})");
|
||||
std::cout << j << '\n';
|
||||
|
||||
// parsing throws when a key is repeated
|
||||
try
|
||||
{
|
||||
parse_strict(R"({"one": 1, "one": 2})");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
{"one":1,"two":2}
|
||||
duplicate JSON object key: one
|
||||
Reference in New Issue
Block a user