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:
Niels Lohmann
2026-07-10 19:43:05 +02:00
parent 6ba332c7df
commit 2269656bc6
18 changed files with 612 additions and 44 deletions
+134
View File
@@ -4099,6 +4099,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result;
}
/// @brief deserialize from an iterator+sentinel pair (C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/parse/
template<typename IteratorType, typename SentinelType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(IteratorType first,
SentinelType last,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false,
const bool ignore_trailing_commas = false,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
basic_json result;
parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
return result;
}
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
static basic_json parse(detail::span_input_adapter&& i,
@@ -4132,6 +4150,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @brief check if the input is valid JSON (sentinel version)
/// @sa https://json.nlohmann.me/api/basic_json/accept/
template<typename IteratorType, typename SentinelType>
static bool accept(IteratorType first, SentinelType last,
const bool ignore_comments = false,
const bool ignore_trailing_commas = false,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
static bool accept(detail::span_input_adapter&& i,
@@ -4173,6 +4203,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
/// @brief generate SAX events (sentinel version)
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
template<class IteratorType, class SentinelType, class SAX>
JSON_HEDLEY_NON_NULL(3)
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false,
const bool ignore_trailing_commas = false,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
auto ia = detail::input_adapter(std::move(first), std::move(last));
return format == input_format_t::json
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
/// @brief generate SAX events
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
/// @deprecated This function is deprecated since 3.8.0 and will be removed in
@@ -4477,6 +4525,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in CBOR format (sentinel version)
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
template<typename IteratorType, typename SentinelType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_cbor(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
}
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
@@ -4533,6 +4599,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in MessagePack format (sentinel version)
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
template<typename IteratorType, typename SentinelType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_msgpack(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
}
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
@@ -4587,6 +4670,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in UBJSON format (sentinel version)
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
template<typename IteratorType, typename SentinelType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_ubjson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
}
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
@@ -4641,6 +4741,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in BJData format (sentinel version)
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
template<typename IteratorType, typename SentinelType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bjdata(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in BSON format
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
template<typename InputType>
@@ -4671,6 +4788,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in BSON format (sentinel version)
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
template<typename IteratorType, typename SentinelType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
typename std::enable_if < !std::is_same<IteratorType, SentinelType>::value &&
detail::can_compare_ne<IteratorType, SentinelType>::value >::type* = nullptr)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
return res ? result : basic_json(value_t::discarded);
}
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))