// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // Copyright (c) 2013-2022 Niels Lohmann . // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" #include using nlohmann::json; #ifdef JSON_TEST_NO_GLOBAL_UDLS using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif #include #include #include #include namespace { struct SaxEventLogger : public nlohmann::json_sax { 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 (auto b : val) { binary_contents.append(comma_space); binary_contents.append(std::to_string(static_cast(b))); comma_space = ", "; } binary_contents.append(")"); events.push_back(binary_contents); return true; } bool start_object(std::size_t elements) override { if (elements == static_cast(-1)) { 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 == static_cast(-1)) { 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 { events.push_back("parse_error(" + std::to_string(position) + ")"); return false; } std::vector events {}; // NOLINT(readability-redundant-member-init) }; struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger { bool start_object(std::size_t elements) override { if (elements == static_cast(-1)) { 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 == static_cast(-1)) { events.emplace_back("start_array()"); } else { events.push_back("start_array(" + std::to_string(elements) + ")"); } return false; } }; template class proxy_iterator { public: using iterator = typename T::iterator; using value_type = typename std::iterator_traits::value_type; using reference = typename std::iterator_traits::reference; using pointer = typename std::iterator_traits::pointer; using difference_type = typename std::iterator_traits::difference_type; using iterator_category = std::input_iterator_tag; proxy_iterator() = default; explicit proxy_iterator(iterator& it) : m_it(std::addressof(it)) {} proxy_iterator& operator++() { ++*m_it; return *this; } proxy_iterator& operator--() { --*m_it; return *this; } bool operator==(const proxy_iterator& rhs) const { return (m_it && rhs.m_it) ? (*m_it == *rhs.m_it) : (m_it == rhs.m_it); } bool operator!=(const proxy_iterator& rhs) const { return !(*this == rhs); } reference operator*() const { return **m_it; } private: iterator* m_it = nullptr; }; } // namespace TEST_CASE("deserialization") { SECTION("successful deserialization") { SECTION("stream") { std::stringstream ss1; std::stringstream ss2; std::stringstream ss3; ss1 << R"(["foo",1,2,3,false,{"one":1}])"; ss2 << R"(["foo",1,2,3,false,{"one":1}])"; ss3 << R"(["foo",1,2,3,false,{"one":1}])"; json j = json::parse(ss1); CHECK(json::accept(ss2)); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); SaxEventLogger l; CHECK(json::sax_parse(ss3, &l)); CHECK(l.events.size() == 11); CHECK(l.events == std::vector( { "start_array()", "string(foo)", "number_unsigned(1)", "number_unsigned(2)", "number_unsigned(3)", "boolean(false)", "start_object()", "key(one)", "number_unsigned(1)", "end_object()", "end_array()" })); } SECTION("string literal") { const auto* s = R"(["foo",1,2,3,false,{"one":1}])"; json j = json::parse(s); CHECK(json::accept(s)); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); SaxEventLogger l; CHECK(json::sax_parse(s, &l)); CHECK(l.events.size() == 11); CHECK(l.events == std::vector( { "start_array()", "string(foo)", "number_unsigned(1)", "number_unsigned(2)", "number_unsigned(3)", "boolean(false)", "start_object()", "key(one)", "number_unsigned(1)", "end_object()", "end_array()" })); } SECTION("string_t") { json::string_t const s = R"(["foo",1,2,3,false,{"one":1}])"; json j = json::parse(s); CHECK(json::accept(s)); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); SaxEventLogger l; CHECK(json::sax_parse(s, &l)); CHECK(l.events.size() == 11); CHECK(l.events == std::vector( { "start_array()", "string(foo)", "number_unsigned(1)", "number_unsigned(2)", "number_unsigned(3)", "boolean(false)", "start_object()", "key(one)", "number_unsigned(1)", "end_object()", "end_array()" })); } SECTION("operator<<") { std::stringstream ss; ss << R"(["foo",1,2,3,false,{"one":1}])"; json j; j << ss; CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } SECTION("operator>>") { std::stringstream ss; ss << R"(["foo",1,2,3,false,{"one":1}])"; json j; ss >> j; CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } SECTION("user-defined string literal") { CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } } SECTION("unsuccessful deserialization") { SECTION("stream") { std::stringstream ss1; std::stringstream ss3; std::stringstream ss4; std::stringstream ss5; ss1 << R"(["foo",1,2,3,false,{"one":1})"; ss3 << R"(["foo",1,2,3,false,{"one":1})"; ss4 << R"(["foo",1,2,3,false,{"one":1})"; ss5 << R"(["foo",1,2,3,false,{"one":1})"; json _; CHECK_THROWS_WITH_AS(_ = json::parse(ss1), "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); CHECK(!json::accept(ss3)); json j_error; CHECK_NOTHROW(j_error = json::parse(ss4, nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(ss5, &l)); CHECK(l.events.size() == 11); CHECK(l.events == std::vector( { "start_array()", "string(foo)", "number_unsigned(1)", "number_unsigned(2)", "number_unsigned(3)", "boolean(false)", "start_object()", "key(one)", "number_unsigned(1)", "end_object()", "parse_error(29)" })); } SECTION("string") { json::string_t const s = R"(["foo",1,2,3,false,{"one":1})"; json _; CHECK_THROWS_WITH_AS(_ = json::parse(s), "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); CHECK(!json::accept(s)); json j_error; CHECK_NOTHROW(j_error = json::parse(s, nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(s, &l)); CHECK(l.events.size() == 11); CHECK(l.events == std::vector( { "start_array()", "string(foo)", "number_unsigned(1)", "number_unsigned(2)", "number_unsigned(3)", "boolean(false)", "start_object()", "key(one)", "number_unsigned(1)", "end_object()", "parse_error(29)" })); } SECTION("operator<<") { std::stringstream ss; ss << R"(["foo",1,2,3,false,{"one":1})"; json j; CHECK_THROWS_WITH_AS(j << ss, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); } SECTION("operator>>") { std::stringstream ss; ss << R"(["foo",1,2,3,false,{"one":1})"; json j; CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); } SECTION("user-defined string literal") { CHECK_THROWS_WITH_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); } } SECTION("contiguous containers") { SECTION("directly") { SECTION("from std::vector") { std::vector const v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from std::array") { std::array const v { {'t', 'r', 'u', 'e'} }; CHECK(json::parse(v) == json(true)); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from array") { uint8_t v[] = {'t', 'r', 'u', 'e'}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) CHECK(json::parse(v) == json(true)); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from chars") { auto* v = new uint8_t[5]; // NOLINT(cppcoreguidelines-owning-memory) v[0] = 't'; v[1] = 'r'; v[2] = 'u'; v[3] = 'e'; v[4] = '\0'; CHECK(json::parse(v) == json(true)); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); delete[] v; // NOLINT(cppcoreguidelines-owning-memory) } SECTION("from std::string") { std::string const v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from std::initializer_list") { std::initializer_list const v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("empty container") { std::vector const v; json _; CHECK_THROWS_AS(_ = json::parse(v), json::parse_error&); CHECK(!json::accept(v)); SaxEventLogger l; CHECK(!json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(1)"})); } } SECTION("via iterator range") { SECTION("from std::vector") { std::vector v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); CHECK(json::accept(std::begin(v), std::end(v))); SaxEventLogger l; CHECK(json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from std::array") { std::array v { {'t', 'r', 'u', 'e'} }; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); CHECK(json::accept(std::begin(v), std::end(v))); SaxEventLogger l; CHECK(json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from array") { uint8_t v[] = {'t', 'r', 'u', 'e'}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); CHECK(json::accept(std::begin(v), std::end(v))); SaxEventLogger l; CHECK(json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from std::string") { std::string v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); CHECK(json::accept(std::begin(v), std::end(v))); SaxEventLogger l; CHECK(json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from std::initializer_list") { std::initializer_list const v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); CHECK(json::accept(std::begin(v), std::end(v))); SaxEventLogger l; CHECK(json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("from std::valarray") { std::valarray v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); CHECK(json::accept(std::begin(v), std::end(v))); SaxEventLogger l; CHECK(json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } SECTION("with empty range") { std::vector v; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(1)"})); } SECTION("iterator_input_adapter advances iterators correctly") { using nlohmann::json; using nlohmann::detail::input_format_t; using nlohmann::detail::json_sax_dom_parser; using proxy = proxy_iterator; std::string str1 = "[1]"; std::string str2 = "[2]"; std::string str = str1 + str2; auto first = str.begin(); auto last = str.end(); json j; json_sax_dom_parser sax(j, true); CHECK(json::sax_parse(proxy(first), proxy(last), &sax, input_format_t::json, false)); CHECK(j.dump() == str1); CHECK(std::string(first, last) == str2); } } // these cases are required for 100% line coverage SECTION("error cases") { SECTION("case 1") { std::array v = {{'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u'}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(10)"})); } SECTION("case 2") { std::array v = {{'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(11)"})); } SECTION("case 3") { std::array v = {{'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(18)"})); } SECTION("case 4") { std::array v = {{'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(18)"})); } SECTION("case 5") { std::array v = {{'\"', 0x7F, 0xC1}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(3)"})); } SECTION("case 6") { std::array v = {{'\"', 0x7F, 0xDF, 0x7F}}; json _; CHECK_THROWS_WITH_AS(_ = json::parse(std::begin(v), std::end(v)), "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\x7f\xdf\x7f'", json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 7") { std::array v = {{'\"', 0x7F, 0xDF, 0xC0}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 8") { std::array v = {{'\"', 0x7F, 0xE0, 0x9F}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 9") { std::array v = {{'\"', 0x7F, 0xEF, 0xC0}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 10") { std::array v = {{'\"', 0x7F, 0xED, 0x7F}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 11") { std::array v = {{'\"', 0x7F, 0xF0, 0x8F}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 12") { std::array v = {{'\"', 0x7F, 0xF0, 0xC0}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 13") { std::array v = {{'\"', 0x7F, 0xF3, 0x7F}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 14") { std::array v = {{'\"', 0x7F, 0xF3, 0xC0}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 15") { std::array v = {{'\"', 0x7F, 0xF4, 0x7F}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"parse_error(4)"})); } SECTION("case 16") { std::array v = {{'{', '\"', '\"', ':', '1', '1'}}; json _; CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); CHECK(j_error.is_discarded()); SaxEventLogger l; CHECK(!json::sax_parse(std::begin(v), std::end(v), &l)); CHECK(l.events.size() == 4); CHECK(l.events == std::vector( { "start_object()", "key()", "number_unsigned(11)", "parse_error(7)" })); } } } SECTION("ignoring byte-order marks") { std::string bom = "\xEF\xBB\xBF"; SECTION("BOM only") { json _; CHECK_THROWS_WITH_AS(_ = json::parse(bom), "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::parse(std::istringstream(bom)), "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); SaxEventLogger l; CHECK(!json::sax_parse(bom, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector( { "parse_error(4)" })); } SECTION("BOM and content") { CHECK(json::parse(bom + "1") == 1); CHECK(json::parse(std::istringstream(bom + "1")) == 1); SaxEventLogger l1; SaxEventLogger l2; CHECK(json::sax_parse(std::istringstream(bom + "1"), &l1)); CHECK(json::sax_parse(bom + "1", &l2)); CHECK(l1.events.size() == 1); CHECK(l1.events == std::vector( { "number_unsigned(1)" })); CHECK(l2.events.size() == 1); CHECK(l2.events == std::vector( { "number_unsigned(1)" })); } SECTION("2 byte of BOM") { json _; CHECK_THROWS_WITH_AS(_ = json::parse(bom.substr(0, 2)), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::parse(std::istringstream(bom.substr(0, 2))), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'", json::parse_error&); SaxEventLogger l1; SaxEventLogger l2; CHECK(!json::sax_parse(std::istringstream(bom.substr(0, 2)), &l1)); CHECK(!json::sax_parse(bom.substr(0, 2), &l2)); CHECK(l1.events.size() == 1); CHECK(l1.events == std::vector( { "parse_error(3)" })); CHECK(l2.events.size() == 1); CHECK(l2.events == std::vector( { "parse_error(3)" })); } SECTION("1 byte of BOM") { json _; CHECK_THROWS_WITH_AS(_ = json::parse(bom.substr(0, 1)), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::parse(std::istringstream(bom.substr(0, 1))), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'", json::parse_error&); SaxEventLogger l1; SaxEventLogger l2; CHECK(!json::sax_parse(std::istringstream(bom.substr(0, 1)), &l1)); CHECK(!json::sax_parse(bom.substr(0, 1), &l2)); CHECK(l1.events.size() == 1); CHECK(l1.events == std::vector( { "parse_error(2)" })); CHECK(l2.events.size() == 1); CHECK(l2.events == std::vector( { "parse_error(2)" })); } SECTION("variations") { // calculate variations of each byte of the BOM to make sure // that the BOM and only the BOM is skipped for (int i0 = -1; i0 < 2; ++i0) { for (int i1 = -1; i1 < 2; ++i1) { for (int i2 = -1; i2 < 2; ++i2) { // debug output for the variations CAPTURE(i0) CAPTURE(i1) CAPTURE(i2) std::string s; s.push_back(static_cast(bom[0] + i0)); s.push_back(static_cast(bom[1] + i1)); s.push_back(static_cast(bom[2] + i2)); if (i0 == 0 && i1 == 0 && i2 == 0) { // without any variation, we skip the BOM CHECK(json::parse(s + "null") == json()); CHECK(json::parse(std::istringstream(s + "null")) == json()); SaxEventLogger l; CHECK(json::sax_parse(s + "null", &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector( { "null()" })); } else { // any variation is an error json _; CHECK_THROWS_AS(_ = json::parse(s + "null"), json::parse_error&); CHECK_THROWS_AS(_ = json::parse(std::istringstream(s + "null")), json::parse_error&); SaxEventLogger l; CHECK(!json::sax_parse(s + "null", &l)); CHECK(l.events.size() == 1); if (i0 != 0) { CHECK(l.events == std::vector( { "parse_error(1)" })); } else if (i1 != 0) { CHECK(l.events == std::vector( { "parse_error(2)" })); } else { CHECK(l.events == std::vector( { "parse_error(3)" })); } } } } } } SECTION("preserve state after parsing") { std::istringstream s(bom + "123 456"); json j; j << s; CHECK(j == 123); j << s; CHECK(j == 456); } } SECTION("SAX and early abort") { std::string const s = R"([1, ["string", 43.12], null, {"key1": true, "key2": false}])"; SaxEventLogger default_logger; SaxEventLoggerExitAfterStartObject exit_after_start_object; SaxEventLoggerExitAfterKey exit_after_key; SaxEventLoggerExitAfterStartArray exit_after_start_array; json::sax_parse(s, &default_logger); CHECK(default_logger.events.size() == 14); CHECK(default_logger.events == std::vector( { "start_array()", "number_unsigned(1)", "start_array()", "string(string)", "number_float(43.12)", "end_array()", "null()", "start_object()", "key(key1)", "boolean(true)", "key(key2)", "boolean(false)", "end_object()", "end_array()" })); json::sax_parse(s, &exit_after_start_object); CHECK(exit_after_start_object.events.size() == 8); CHECK(exit_after_start_object.events == std::vector( { "start_array()", "number_unsigned(1)", "start_array()", "string(string)", "number_float(43.12)", "end_array()", "null()", "start_object()" })); json::sax_parse(s, &exit_after_key); CHECK(exit_after_key.events.size() == 9); CHECK(exit_after_key.events == std::vector( { "start_array()", "number_unsigned(1)", "start_array()", "string(string)", "number_float(43.12)", "end_array()", "null()", "start_object()", "key(key1)" })); json::sax_parse(s, &exit_after_start_array); CHECK(exit_after_start_array.events.size() == 1); CHECK(exit_after_start_array.events == std::vector( { "start_array()" })); } SECTION("JSON Lines") { SECTION("Example file") { std::stringstream ss; ss << R"({"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]} {"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]} {"name": "May", "wins": []} {"name": "Deloise", "wins": [["three of a kind", "5♣"]]} )"; std::string line; int object_count = 0; while (std::getline(ss, line)) { ++object_count; CHECK(json::accept(line)); } CHECK(object_count == 4); } SECTION("Example file without trailing newline") { std::stringstream ss; ss << R"({"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]} {"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]} {"name": "May", "wins": []} {"name": "Deloise", "wins": [["three of a kind", "5♣"]]})"; std::string line; int object_count = 0; while (std::getline(ss, line)) { ++object_count; CHECK(json::accept(line)); } CHECK(object_count == 4); } } } TEST_CASE_TEMPLATE("deserialization of different character types (ASCII)", T, char, unsigned char, signed char, wchar_t, char16_t, char32_t, std::uint8_t, std::int8_t, std::int16_t, std::uint16_t, std::int32_t, std::uint32_t) { std::vector const v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); CHECK(l.events == std::vector({"boolean(true)"})); } TEST_CASE_TEMPLATE("deserialization of different character types (UTF-8)", T, char, unsigned char, std::uint8_t) { // a star emoji std::vector const v = {'"', static_cast(0xe2u), static_cast(0xadu), static_cast(0x90u), static_cast(0xefu), static_cast(0xb8u), static_cast(0x8fu), '"'}; CHECK(json::parse(v).dump(-1, ' ', true) == "\"\\u2b50\\ufe0f\""); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); } TEST_CASE_TEMPLATE("deserialization of different character types (UTF-16)", T, char16_t, std::uint16_t) { // a star emoji std::vector const v = {static_cast('"'), static_cast(0x2b50), static_cast(0xfe0f), static_cast('"')}; CHECK(json::parse(v).dump(-1, ' ', true) == "\"\\u2b50\\ufe0f\""); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); } TEST_CASE_TEMPLATE("deserialization of different character types (UTF-32)", T, char32_t, std::uint32_t) { // a star emoji std::vector const v = {static_cast('"'), static_cast(0x2b50), static_cast(0xfe0f), static_cast('"')}; CHECK(json::parse(v).dump(-1, ' ', true) == "\"\\u2b50\\ufe0f\""); CHECK(json::accept(v)); SaxEventLogger l; CHECK(json::sax_parse(v, &l)); CHECK(l.events.size() == 1); }