From 9862cf25f68bd705a77ef4e8575109a4f8417af8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 30 Oct 2026 22:37:32 +0100 Subject: [PATCH] Test escaped object keys decoded through NLOHMANN_DEFINE_TYPE from a json_view A json_view or json_document decodes a macro-defined struct directly and looks the members up by their decoded keys. Only plain keys were covered so far. The new section decodes structs defined with NLOHMANN_DEFINE_TYPE_INTRUSIVE, ..._WITH_DEFAULT and ..._WITH_NAMES from documents whose keys contain escape sequences, and compares the result with the conversion of the materialized value. It also pins the difference for a duplicate key written once plain and once escaped (the view takes the first member, the materialized value keeps the last one), and that an escaped backslash is part of the decoded key. Signed-off-by: Niels Lohmann --- tests/src/unit-json_view_from_json.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/src/unit-json_view_from_json.cpp b/tests/src/unit-json_view_from_json.cpp index 7e84a9081..a1d94d586 100644 --- a/tests/src/unit-json_view_from_json.cpp +++ b/tests/src/unit-json_view_from_json.cpp @@ -443,6 +443,30 @@ TEST_CASE("json_view from_json") CHECK(d.root().materialize().get().x == 3); } + SECTION("escaped keys: members are found by their decoded names") + { + // keys are decoded before they are compared, as by parse(); a lookup + // that compares the raw bytes of the source would miss these members + const json_document d = json_document::parse(R"([{"\u0078": 1, "y": 2}, {"V\u0061lue": 3, "Te\u0078t": "t"}, {"\u0062": "escaped"}, {"x": 1, "\u0078": 3, "y": 2}])"); + const json j = d.root().materialize(); + CHECK(d.root()[0].get().x == 1); + CHECK(d.root()[0].get().y == 2); + CHECK((d.root()[0].get() == j[0].get())); + CHECK(d.root()[1].get().value == 3); + CHECK(d.root()[1].get().text == "t"); + CHECK(d.root()[2].get().b == "escaped"); + CHECK(d.root()[2].get().a == 1); + // a duplicate key written once plain and once escaped: still the first member + CHECK(d.root()[3].get().x == 1); + CHECK(j[3].get().x == 3); +#if !defined(JSON_NOEXCEPTION) && !JSON_DIAGNOSTICS && !JSON_DIAGNOSTIC_POSITIONS + // an escaped backslash is part of the decoded key: "x\\" is not "x" + const json_document e = json_document::parse(R"({"x\\": 1, "y": 2})"); + CHECK(exception_of_call([&] { static_cast(e.root().get()); }) == exception_of_call([&] { static_cast(e.root().materialize().get()); })); + CHECK(exception_of_call([&] { static_cast(e.root().get()); }) == "[json.exception.out_of_range.403] key 'x' not found"); +#endif + } + SECTION("editable documents and ordered_json") { json_editable_document e = json_editable_document::parse(R"({"x": 1, "y": 2})");