mirror of
https://github.com/nlohmann/json.git
synced 2026-07-11 21:15:10 +00:00
Add iterator+sentinel tests and docs for binary deserializers
This commit extends the C++20 ranges support (iterator+sentinel pairs) to the binary format deserializers from_cbor, from_msgpack, from_ubjson, from_bjdata, and from_bson, matching what was already done for parse(), accept(), and sax_parse(). Changes: - Add istreambuf_sentinel helper to test_utils.hpp for EOF detection in tests - Add 5 new test cases that read binary files directly via std::istreambuf_iterator<char> + sentinel, without pre-buffering - Update documentation for all 5 from_* functions to document overload (3) with SentinelType parameter - All tests pass; verified against existing test suite data - Fix potential buffer over-read warning in heterogeneous iterator test Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -30,4 +30,20 @@ inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
|
||||
return byte_vector;
|
||||
}
|
||||
|
||||
// sentinel for istreambuf_iterator; compares != true until EOF is reached
|
||||
// lets tests read a file directly via the new iterator+sentinel overloads
|
||||
// instead of buffering the whole file into a vector first
|
||||
struct istreambuf_sentinel
|
||||
{
|
||||
friend bool operator!=(const std::istreambuf_iterator<char>& it, const istreambuf_sentinel&) noexcept
|
||||
{
|
||||
return it != std::istreambuf_iterator<char>();
|
||||
}
|
||||
|
||||
friend bool operator!=(const istreambuf_sentinel&, const std::istreambuf_iterator<char>& it) noexcept
|
||||
{
|
||||
return it != std::istreambuf_iterator<char>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
@@ -3699,6 +3699,15 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Parse BJData directly from a file using iterator and sentinel")
|
||||
{
|
||||
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.bjdata";
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
std::istreambuf_iterator<char> first(file);
|
||||
const json parsed = json::from_bjdata(first, utils::istreambuf_sentinel{});
|
||||
CHECK((parsed.is_object() || parsed.is_array()));
|
||||
}
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
TEST_CASE("all BJData first bytes")
|
||||
{
|
||||
|
||||
@@ -1208,6 +1208,25 @@ TEST_CASE("BSON numerical data")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Parse BSON directly from a file using iterator and sentinel")
|
||||
{
|
||||
const json j = {{"key", "value"}, {"number", 42}};
|
||||
std::vector<std::uint8_t> bson_bytes;
|
||||
json::to_bson(j, bson_bytes);
|
||||
|
||||
{
|
||||
std::ofstream file("test_bson_sentinel.bson", std::ios::binary);
|
||||
file.write(reinterpret_cast<const char*>(bson_bytes.data()), static_cast<std::streamsize>(bson_bytes.size()));
|
||||
}
|
||||
|
||||
std::ifstream file("test_bson_sentinel.bson", std::ios::binary);
|
||||
std::istreambuf_iterator<char> first(file);
|
||||
const json parsed = json::from_bson(first, utils::istreambuf_sentinel{});
|
||||
CHECK(parsed == j);
|
||||
|
||||
static_cast<void>(std::remove("test_bson_sentinel.bson"));
|
||||
}
|
||||
|
||||
TEST_CASE("BSON roundtrips" * doctest::skip())
|
||||
{
|
||||
SECTION("reference files")
|
||||
|
||||
@@ -1921,6 +1921,15 @@ TEST_CASE("single CBOR roundtrip")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Parse CBOR directly from a file using iterator and sentinel")
|
||||
{
|
||||
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.cbor";
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
std::istreambuf_iterator<char> first(file);
|
||||
const json parsed = json::from_cbor(first, utils::istreambuf_sentinel{});
|
||||
CHECK((parsed.is_object() || parsed.is_array()));
|
||||
}
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
TEST_CASE("CBOR regressions")
|
||||
{
|
||||
|
||||
@@ -1641,6 +1641,15 @@ TEST_CASE("single MessagePack roundtrip")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Parse MessagePack directly from a file using iterator and sentinel")
|
||||
{
|
||||
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.msgpack";
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
std::istreambuf_iterator<char> first(file);
|
||||
const json parsed = json::from_msgpack(first, utils::istreambuf_sentinel{});
|
||||
CHECK((parsed.is_object() || parsed.is_array()));
|
||||
}
|
||||
|
||||
TEST_CASE("MessagePack roundtrips" * doctest::skip())
|
||||
{
|
||||
SECTION("input from msgpack-python")
|
||||
|
||||
@@ -2393,6 +2393,15 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Parse UBJSON directly from a file using iterator and sentinel")
|
||||
{
|
||||
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.ubjson";
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
std::istreambuf_iterator<char> first(file);
|
||||
const json parsed = json::from_ubjson(first, utils::istreambuf_sentinel{});
|
||||
CHECK((parsed.is_object() || parsed.is_array()));
|
||||
}
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
TEST_CASE("all UBJSON first bytes")
|
||||
{
|
||||
|
||||
@@ -168,4 +168,40 @@ TEST_CASE("Custom iterator")
|
||||
CHECK(as_json.at(3) == 4);
|
||||
}
|
||||
|
||||
// Custom sentinel type for testing heterogeneous iterator+sentinel support
|
||||
struct CustomSentinel
|
||||
{
|
||||
const char* end_ptr;
|
||||
|
||||
// Support both directions for != comparison
|
||||
friend bool operator!=(const char* it, const CustomSentinel& sentinel)
|
||||
{
|
||||
return it != sentinel.end_ptr;
|
||||
}
|
||||
|
||||
friend bool operator!=(const CustomSentinel& sentinel, const char* it)
|
||||
{
|
||||
return it != sentinel.end_ptr;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("Parse with heterogeneous iterator and sentinel types")
|
||||
{
|
||||
std::string json_str = R"({"key":"value"})";
|
||||
const char* end_ptr = json_str.data() + json_str.size();
|
||||
|
||||
// Parse using pointer and sentinel (different types)
|
||||
json j = json::parse(json_str.data(), CustomSentinel{end_ptr});
|
||||
CHECK(j["key"] == "value");
|
||||
|
||||
// Accept using pointer and sentinel
|
||||
CHECK(json::accept(json_str.data(), CustomSentinel{end_ptr}));
|
||||
|
||||
// Test that the same-type case still works
|
||||
std::string raw_data = R"([1,2,3])";
|
||||
std::list<char> data(raw_data.begin(), raw_data.end());
|
||||
json j2 = json::parse(data.begin(), data.end());
|
||||
CHECK(j2.at(0) == 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user