mirror of
https://github.com/nlohmann/json.git
synced 2026-10-12 10:13:13 +00:00
* Use and extend the detail helpers to remove duplicated code Library: - binary_reader: format bytes with hex_byte() instead of snprintf - add throw_type_must_be() for the 20 copies of type_error.302 - binary_reader: add last_byte_error()/unexpected_byte() for the 32 "parse error at the last read byte" sites (replaces bon8_error) - json_sax: add check_container_size() for out_of_range.408 and diagnostic_positions::set_container_start/_end() - json_pointer: add throw_no_parent() (405) and throw_unresolved() (404) Tests: - unit-class_parser uses the shared utils::SaxCountdown - move SaxEventLogger (and its ExitAfter* variants) from unit-class_parser and unit-deserialization into the new tests/src/test_sax.hpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Factor out more repeated error paths and test boilerplate Library: - add throw_cannot_use_with() for the 34 copies of type_error.304-312 "cannot use X with Y" - iter_impl: add throw_cannot_get_value() (invalid_iterator.214) - parser: add syntax_error() for the 12 parse_error.101 sites - ordered_map: share the four at() bodies via at_impl() - json_sax_dom_callback_parser: add pop_container() for end_object() and end_array() - binary_reader: build the two UBJSON/BJData length-type messages with concat() and last_byte_error() Tests: - move same_value(), the NDEBUG guard, and step 0 (parse without exceptions) of the seven fuzzer drivers into tests/src/fuzzer_common.hpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Name the exception ids and address review comments Add detail::exception_id, a scoped enum with one named enumerator per documented exception id, and use it for every id in the library. The create() functions get an overload for it; the int overloads stay for user code. Following the review of #5783: add binary_reader::invalid_byte() and length_type_error(), basic_json::throw_subscript_wrong_type(), move the fuzzer includes and the using-declaration into fuzzer_common.hpp, and rename test_sax.hpp to sax_event_loggers.hpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence -Wweak-vtables for the SAX event loggers The loggers moved from anonymous namespaces in the test files into sax_event_loggers.hpp, so clang now warns that their vtables are emitted in every translation unit. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence MSVC 2015 C4100 in parser::syntax_error for static SAX::parse_error Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Inline invalid_byte() into its call sites The wrapper only fixed the message string of unexpected_byte(), which is the same kind of per-argument helper that was declined for throw_type_must_be(). Call unexpected_byte("invalid byte", ...) directly. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
186 lines
4.8 KiB
C++
186 lines
4.8 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <cstddef> // size_t
|
|
#include <cstdint> // uint8_t (via json::binary_t)
|
|
#include <limits> // numeric_limits
|
|
#include <string> // string, to_string
|
|
#include <vector> // vector
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
// the loggers are defined in this header only, so their vtables are emitted in
|
|
// every test that includes it
|
|
#if defined(__clang__)
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wweak-vtables"
|
|
#endif
|
|
|
|
namespace utils
|
|
{
|
|
/// a SAX event consumer that records every event it receives as a
|
|
/// human-readable string, used by the deserialization tests to check the
|
|
/// exact sequence of SAX events a parse run produces
|
|
struct SaxEventLogger : public nlohmann::json_sax<nlohmann::json>
|
|
{
|
|
using json = nlohmann::json;
|
|
|
|
bool null() override
|
|
{
|
|
events.emplace_back("null()");
|
|
return true;
|
|
}
|
|
|
|
bool boolean(bool val) override
|
|
{
|
|
events.emplace_back(val ? "boolean(true)" : "boolean(false)");
|
|
return true;
|
|
}
|
|
|
|
bool number_integer(json::number_integer_t val) override
|
|
{
|
|
events.push_back("number_integer(" + std::to_string(val) + ")");
|
|
return true;
|
|
}
|
|
|
|
bool number_unsigned(json::number_unsigned_t val) override
|
|
{
|
|
events.push_back("number_unsigned(" + std::to_string(val) + ")");
|
|
return true;
|
|
}
|
|
|
|
bool number_float(json::number_float_t /*val*/, const std::string& s) override
|
|
{
|
|
events.push_back("number_float(" + s + ")");
|
|
return true;
|
|
}
|
|
|
|
bool string(std::string& val) override
|
|
{
|
|
events.push_back("string(" + val + ")");
|
|
return true;
|
|
}
|
|
|
|
bool binary(json::binary_t& val) override
|
|
{
|
|
std::string binary_contents = "binary(";
|
|
std::string comma_space;
|
|
for (const auto b : val)
|
|
{
|
|
binary_contents.append(comma_space);
|
|
binary_contents.append(std::to_string(static_cast<int>(b)));
|
|
comma_space = ", ";
|
|
}
|
|
binary_contents.append(")");
|
|
events.push_back(binary_contents);
|
|
return true;
|
|
}
|
|
|
|
bool start_object(std::size_t elements) override
|
|
{
|
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
{
|
|
events.emplace_back("start_object()");
|
|
}
|
|
else
|
|
{
|
|
events.push_back("start_object(" + std::to_string(elements) + ")");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool key(std::string& val) override
|
|
{
|
|
events.push_back("key(" + val + ")");
|
|
return true;
|
|
}
|
|
|
|
bool end_object() override
|
|
{
|
|
events.emplace_back("end_object()");
|
|
return true;
|
|
}
|
|
|
|
bool start_array(std::size_t elements) override
|
|
{
|
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
{
|
|
events.emplace_back("start_array()");
|
|
}
|
|
else
|
|
{
|
|
events.push_back("start_array(" + std::to_string(elements) + ")");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool end_array() override
|
|
{
|
|
events.emplace_back("end_array()");
|
|
return true;
|
|
}
|
|
|
|
bool parse_error(std::size_t position, const std::string& /*last_token*/, const json::exception& /*ex*/) override
|
|
{
|
|
errored = true;
|
|
events.push_back("parse_error(" + std::to_string(position) + ")");
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
|
|
bool errored = false;
|
|
};
|
|
|
|
struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger
|
|
{
|
|
bool start_object(std::size_t elements) override
|
|
{
|
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
{
|
|
events.emplace_back("start_object()");
|
|
}
|
|
else
|
|
{
|
|
events.push_back("start_object(" + std::to_string(elements) + ")");
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
struct SaxEventLoggerExitAfterKey : public SaxEventLogger
|
|
{
|
|
bool key(std::string& val) override
|
|
{
|
|
events.push_back("key(" + val + ")");
|
|
return false;
|
|
}
|
|
};
|
|
|
|
struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger
|
|
{
|
|
bool start_array(std::size_t elements) override
|
|
{
|
|
if (elements == (std::numeric_limits<std::size_t>::max)())
|
|
{
|
|
events.emplace_back("start_array()");
|
|
}
|
|
else
|
|
{
|
|
events.push_back("start_array(" + std::to_string(elements) + ")");
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
} // namespace utils
|
|
|
|
#if defined(__clang__)
|
|
#pragma clang diagnostic pop
|
|
#endif
|