Merge branch 'json-view/11-view-access' into json-view/12-view-values

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-29 22:42:18 +02:00
8 changed files with 66 additions and 21 deletions

View File

@@ -15,7 +15,7 @@ int main()
const auto values = measurements.root();
const int sum = std::accumulate(values.cbegin(), values.cend(), 0,
[](int total, const json_view & v)
[](int total, const json_view & v)
{
return total + v.materialize().get<int>();
});

View File

@@ -15,7 +15,7 @@ int main()
const auto records = batch.root();
const bool all_objects = std::all_of(records.cbegin(), records.cend(),
[](const json_view & v)
[](const json_view & v)
{
return v.is_object();
});

View File

@@ -13,7 +13,10 @@ int main()
json_document bad = json_document::parse("[1, 2, 3]");
json_document failed = json_document::parse("not json", /* allow_exceptions */ false);
for (const json_view v : { good.root(), bad.root(), failed.root() })
for (const json_view v :
{
good.root(), bad.root(), failed.root()
})
{
if (v.is_object())
{

View File

@@ -418,6 +418,14 @@ class builder
return w;
}
/// a compile-time option as a runtime condition: testing the template
/// argument directly makes a condition like `TrailingCommas && c == ']'`
/// constant when the option is off, which MSVC reports as C4127
static NLOHMANN_VIEW_ALWAYS_INLINE bool enabled(bool option) noexcept
{
return option;
}
/// The parse state and the parser proper. The cursor is a local object of
/// run() whose address never escapes (everything it calls out of line is a
/// member of the builder and gets the positions it needs), so that the
@@ -547,7 +555,7 @@ arr_next:
{
return false;
}
if (TrailingCommas && cur() == ']')
if (enabled(TrailingCommas) && cur() == ']')
{
++p;
goto close_container;
@@ -614,7 +622,7 @@ obj_next:
{
return false;
}
if (TrailingCommas && cur() == '}')
if (enabled(TrailingCommas) && cur() == '}')
{
++p;
goto close_container;
@@ -756,7 +764,7 @@ indent_done:
{
++p;
}
if (Comments && cur() == '/')
if (enabled(Comments) && cur() == '/')
{
const unsigned char* const q = cold.comment(p);
if (q == nullptr)

View File

@@ -959,6 +959,14 @@ class builder
return w;
}
/// a compile-time option as a runtime condition: testing the template
/// argument directly makes a condition like `TrailingCommas && c == ']'`
/// constant when the option is off, which MSVC reports as C4127
static NLOHMANN_VIEW_ALWAYS_INLINE bool enabled(bool option) noexcept
{
return option;
}
/// The parse state and the parser proper. The cursor is a local object of
/// run() whose address never escapes (everything it calls out of line is a
/// member of the builder and gets the positions it needs), so that the
@@ -1088,7 +1096,7 @@ arr_next:
{
return false;
}
if (TrailingCommas && cur() == ']')
if (enabled(TrailingCommas) && cur() == ']')
{
++p;
goto close_container;
@@ -1155,7 +1163,7 @@ obj_next:
{
return false;
}
if (TrailingCommas && cur() == '}')
if (enabled(TrailingCommas) && cur() == '}')
{
++p;
goto close_container;
@@ -1297,7 +1305,7 @@ indent_done:
{
++p;
}
if (Comments && cur() == '/')
if (enabled(Comments) && cur() == '/')
{
const unsigned char* const q = cold.comment(p);
if (q == nullptr)

View File

@@ -1465,7 +1465,8 @@ TEST_CASE("Eisel-Lemire float conversion")
// than the distance to the rounding boundary, so it must not change
std::string longer = token;
const std::size_t e = longer.find('e');
const std::string extra = longer.find('.') == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
const std::size_t dot = longer.find('.');
const std::string extra = dot == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
longer.insert(e == std::string::npos ? longer.size() : e, extra);
CAPTURE(longer);
if (eisel_lemire(longer, out))
@@ -1488,7 +1489,8 @@ TEST_CASE("Eisel-Lemire float conversion")
CHECK(bits_of(json::parse("-65.613616999999977").get<double>()) == bits_of(-65.613616999999977));
CHECK(bits_of(json::parse("2.2250738585072011e-308").get<double>()) == 0x000FFFFFFFFFFFFFu);
CHECK(bits_of(json::parse("4.9406564584124654e-324").get<double>()) == 1u);
CHECK_THROWS_WITH_AS(json::parse("1.7976931348623159e308"),
json _;
CHECK_THROWS_WITH_AS(_ = json::parse("1.7976931348623159e308"),
"[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&);
}
}
@@ -1555,16 +1557,23 @@ TEST_CASE("string scanning kernels")
state ^= state << 17u;
return state;
};
// the upper half as a 32-bit value: converts to std::size_t implicitly on
// every platform (a cast of std::uint64_t is useless where both are the
// same type, and required where std::size_t is 32 bits wide)
const auto next_small = [&next]()
{
return static_cast<std::uint32_t>(next() >> 32u);
};
for (int round = 0; round < 100000; ++round)
{
// mostly ordinary text, so that runs span several words
std::string text(static_cast<std::size_t>(next() % 8), '.');
const auto count = static_cast<std::size_t>(next() % 12);
std::string text(next_small() % 8u, '.');
const std::size_t count = next_small() % 12u;
for (std::size_t k = 0; k < count; ++k)
{
const std::size_t p = (next() % 4 == 0) ? static_cast<std::size_t>(next() % pieces.size()) : 0;
const std::size_t p = (next() % 4 == 0) ? next_small() % pieces.size() : 0;
text += pieces[p];
text += std::string(static_cast<std::size_t>(next() % 10), 'x');
text += std::string(next_small() % 10u, 'x');
}
const auto* data = reinterpret_cast<const unsigned char*>(text.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
for (std::size_t offset = 0; offset < 3 && offset <= text.size(); ++offset)

View File

@@ -38,6 +38,7 @@ using nlohmann::ordered_json_view;
namespace
{
#if !defined(JSON_NOEXCEPTION)
// the exception parse() throws for a text, or "" if it accepts it
std::string parse_exception(const std::string& text, bool comments = false, bool trailing_commas = false)
{
@@ -66,6 +67,7 @@ std::string view_exception(const std::string& text, bool comments = false, bool
}
return "";
}
#endif
// a small deterministic generator of documents
struct generator
@@ -155,7 +157,7 @@ TEST_CASE("json_view")
CHECK(v.materialize() == j);
}
const json_view invalid;
const json_view invalid{};
CHECK(invalid.is_discarded());
CHECK(!static_cast<bool>(invalid));
CHECK(invalid.type() == json::value_t::discarded);
@@ -200,9 +202,11 @@ TEST_CASE("json_view")
})
{
CAPTURE(text);
#if !defined(JSON_NOEXCEPTION)
const std::string expected = parse_exception(text);
REQUIRE(!expected.empty());
CHECK(view_exception(text) == expected);
#endif
CHECK(!json_document::accept(text));
const json_document d = json_document::parse(text, false);
CHECK(d.is_discarded());
@@ -210,8 +214,9 @@ TEST_CASE("json_view")
CHECK(d.node_count() == 0);
}
// the exception types
CHECK_THROWS_AS(json_document::parse("[1,"), json::parse_error&);
CHECK_THROWS_AS(json_document::parse("1e400"), json::out_of_range&);
json_document _;
CHECK_THROWS_AS(_ = json_document::parse("[1,"), json::parse_error&);
CHECK_THROWS_AS(_ = json_document::parse("1e400"), json::out_of_range&);
}
SECTION("parse options")
@@ -226,7 +231,9 @@ TEST_CASE("json_view")
const bool comments = (options & 1) != 0;
const bool trailing_commas = (options & 2) != 0;
CHECK(json_document::accept(text, comments, trailing_commas) == json::accept(text, comments, trailing_commas));
#if !defined(JSON_NOEXCEPTION)
CHECK(view_exception(text, comments, trailing_commas) == parse_exception(text, comments, trailing_commas));
#endif
}
}
}
@@ -238,7 +245,9 @@ TEST_CASE("json_view")
const std::string nul_in_comment("[1, // c\0\n2]", 12);
CHECK(json_document::accept(nul_in_comment, true) == json::accept(nul_in_comment, true));
CHECK(json_document::parse("\xEF\xBB\xBF[1]").root().materialize() == json::parse("\xEF\xBB\xBF[1]"));
#if !defined(JSON_NOEXCEPTION)
CHECK(view_exception("\xEF\xBB") == parse_exception("\xEF\xBB"));
#endif
}
SECTION("inputs")
@@ -384,6 +393,7 @@ TEST_CASE("json_view")
namespace
{
#if !defined(JSON_NOEXCEPTION)
// the exception a call throws, or "" if it throws none
template<typename F>
std::string exception_of(F f)
@@ -398,6 +408,7 @@ std::string exception_of(F f)
}
return "";
}
#endif
// compares a view with the ordered_json value materialize() gives for it:
// types, sizes, elements and members (by index, key, and iteration), in
@@ -566,6 +577,7 @@ TEST_CASE("json_view element access and iteration")
const json_document d = json_document::parse(text);
const json_view v = d.root();
const json j = v.materialize();
#if !defined(JSON_NOEXCEPTION)
if (!j.is_object())
{
CHECK(exception_of([&] { static_cast<void>(v["a"]); }) == exception_of([&] { static_cast<void>(j["a"]); }));
@@ -587,6 +599,7 @@ TEST_CASE("json_view element access and iteration")
CHECK(exception_of([&] { static_cast<void>(v.front()); }) == exception_of([&] { static_cast<void>(j.front()); }));
CHECK(exception_of([&] { static_cast<void>(v.back()); }) == exception_of([&] { static_cast<void>(j.back()); }));
}
#endif
CHECK(v.contains("a") == j.contains("a"));
CHECK(v.count("a") == j.count("a"));
CHECK((v.find("a") == v.end()) == (j.find("a") == j.end())); // NOLINT(readability-container-contains): find() is what is tested
@@ -598,7 +611,7 @@ TEST_CASE("json_view element access and iteration")
CHECK(!d.root()["a"][0]);
CHECK_THROWS_WITH_AS(d.root()["a"].front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
CHECK_THROWS_WITH_AS(d.root()["a"].back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
const json_view invalid;
const json_view invalid{};
CHECK(invalid.begin() == invalid.end());
CHECK(std::string(invalid.type_name()) == "discarded");
CHECK_THROWS_WITH_AS(invalid["a"], "[json.exception.type_error.305] cannot use operator[] with a string argument with discarded", json::type_error&);

View File

@@ -9,8 +9,12 @@
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
#include <nlohmann/detail/view/builder.hpp>
#include <nlohmann/detail/view/string_ref.hpp>
#if JSON_TEST_USING_MULTIPLE_HEADERS
#include <nlohmann/detail/view/builder.hpp>
#include <nlohmann/detail/view/string_ref.hpp>
#else
#include <nlohmann/json_view.hpp> // the single header contains the internal headers
#endif
using nlohmann::json;
#include <cstdint>