diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 161568ded..741793bdd 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -411,8 +411,9 @@ class json_sax_dom_parser JSON_ASSERT(!ref_stack.empty()); JSON_ASSERT(ref_stack.back()->is_object()); - // add null at the given key and store the reference for later - object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val)); + // add null at the given key and store the reference for later; the + // key is moved, as the lexer starts the next token with reset() + object_element = &(ref_stack.back()->m_data.m_value.object->operator[](std::move(val))); return true; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ccd99cfb2..d69ea7a8e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -14600,8 +14600,9 @@ class json_sax_dom_parser JSON_ASSERT(!ref_stack.empty()); JSON_ASSERT(ref_stack.back()->is_object()); - // add null at the given key and store the reference for later - object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val)); + // add null at the given key and store the reference for later; the + // key is moved, as the lexer starts the next token with reset() + object_element = &(ref_stack.back()->m_data.m_value.object->operator[](std::move(val))); return true; } diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp index c3793f220..1d1b5f40b 100644 --- a/tests/src/unit-class_parser.cpp +++ b/tests/src/unit-class_parser.cpp @@ -2452,6 +2452,36 @@ std::string parse_error_message_range(IteratorType first, IteratorType last) } } // namespace +TEST_CASE("object keys are moved into the value") +{ + // json_sax_dom_parser::key() moves the key out of the lexer's buffer into + // the object, and the lexer starts the next token with an empty buffer. + // Keys of every length (inline and allocated strings), keys with escapes, + // and duplicate keys must give the same objects as reading the input one + // character at a time, and as ordered_json, whose map copies the key. + const std::string long_key(100, 'k'); + const std::string doc = R"({"a": 1, ")" + long_key + R"(": {"b": [2, 3], "sixteen_chars_xx": 4}, )" + R"("esc\"apedé": 5, "a": 6, ")" + long_key + R"(x": 7, "": 8, "dup": 9, "dup": 10})"; + + const json j = json::parse(doc); + const std::list chars(doc.begin(), doc.end()); + CHECK(j == json::parse(chars.begin(), chars.end())); + + CHECK(j.size() == 6); + CHECK(j["a"] == 6); + CHECK(j[long_key]["b"] == json({2, 3})); + CHECK(j[long_key]["sixteen_chars_xx"] == 4); + CHECK(j["esc\"aped\xc3\xa9"] == 5); + CHECK(j[long_key + "x"] == 7); + CHECK(j[""] == 8); + CHECK(j["dup"] == 10); + + const auto o = nlohmann::ordered_json::parse(doc); + CHECK(o.size() == 6); + CHECK(o[long_key + "x"] == 7); + CHECK(json(o) == j); +} + TEST_CASE("text of float tokens") { // For contiguous input, the lexer copies the text of a float token into