From b27d2adcbe7a3bcf2f994ef2ce0ed4acefda204a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Jun 2017 19:23:55 +0200 Subject: [PATCH] :sparkles: accept functions to check if input is valid JSON #458 --- src/json.hpp | 47 +++++++++++++++++++++++++++++++ test/src/unit-deserialization.cpp | 44 ++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a4ea2c8f..6bd09cd1 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7421,6 +7421,13 @@ class basic_json return parse(std::begin(array), std::end(array), cb); } + template + static bool accept(T (&array)[N]) + { + // delegate the call to the iterator-range accept overload + return accept(std::begin(array), std::end(array)); + } + /*! @brief deserialize from string literal @@ -7462,6 +7469,15 @@ class basic_json return parser(input_adapter::create(s), cb).parse(true); } + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static bool accept(const CharT s) + { + return parser(input_adapter::create(s)).accept(true); + } + /*! @brief deserialize from stream @@ -7497,6 +7513,11 @@ class basic_json return parser(input_adapter::create(i), cb).parse(true); } + static bool accept(std::istream& i) + { + return parser(input_adapter::create(i)).accept(true); + } + /*! @copydoc parse(std::istream&, const parser_callback_t) */ @@ -7506,6 +7527,11 @@ class basic_json return parser(input_adapter::create(i), cb).parse(true); } + static bool accept(std::istream&& i) + { + return parser(input_adapter::create(i)).accept(true); + } + /*! @brief deserialize from an iterator range with contiguous storage @@ -7561,6 +7587,15 @@ class basic_json return parser(input_adapter::create(first, last), cb).parse(true); } + template::iterator_category>::value, int>::type = 0> + static bool accept(IteratorType first, IteratorType last) + { + return parser(input_adapter::create(first, last)).accept(true); + } + /*! @brief deserialize from a container with contiguous storage @@ -7618,6 +7653,18 @@ class basic_json return parse(std::begin(c), std::end(c), cb); } + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static bool accept(const ContiguousContainer& c) + { + // delegate the call to the iterator-range accept overload + return accept(std::begin(c), std::end(c)); + } + /*! @brief deserialize from stream @deprecated This stream operator is deprecated and will be removed in a diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 49a642b1..66fb5e1b 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -39,9 +39,11 @@ TEST_CASE("deserialization") { SECTION("stream") { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j = json::parse(ss); + std::stringstream ss1, ss2; + ss1 << "[\"foo\",1,2,3,false,{\"one\":1}]"; + ss2 << "[\"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}}})); } @@ -49,6 +51,7 @@ TEST_CASE("deserialization") { auto s = "[\"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}}})); } @@ -56,6 +59,7 @@ TEST_CASE("deserialization") { json::string_t s = "[\"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}}})); } @@ -87,12 +91,14 @@ TEST_CASE("deserialization") { SECTION("stream") { - std::stringstream ss1, ss2; + std::stringstream ss1, ss2, ss3; ss1 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss2 << "[\"foo\",1,2,3,false,{\"one\":1}"; + ss3 << "[\"foo\",1,2,3,false,{\"one\":1}"; CHECK_THROWS_AS(json::parse(ss1), json::parse_error); CHECK_THROWS_WITH(json::parse(ss2), "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'"); + CHECK(not json::accept(ss3)); } SECTION("string") @@ -101,6 +107,7 @@ TEST_CASE("deserialization") CHECK_THROWS_AS(json::parse(s), json::parse_error); CHECK_THROWS_WITH(json::parse(s), "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'"); + CHECK(not json::accept(s)); } SECTION("operator<<") @@ -141,18 +148,21 @@ TEST_CASE("deserialization") { std::vector v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); + CHECK(json::accept(v)); } SECTION("from std::array") { std::array v { {'t', 'r', 'u', 'e'} }; CHECK(json::parse(v) == json(true)); + CHECK(json::accept(v)); } SECTION("from array") { uint8_t v[] = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); + CHECK(json::accept(v)); } SECTION("from chars") @@ -164,6 +174,7 @@ TEST_CASE("deserialization") v[3] = 'e'; v[4] = '\0'; CHECK(json::parse(v) == json(true)); + CHECK(json::accept(v)); delete[] v; } @@ -171,18 +182,21 @@ TEST_CASE("deserialization") { std::string v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); + CHECK(json::accept(v)); } SECTION("from std::initializer_list") { std::initializer_list v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); + CHECK(json::accept(v)); } SECTION("empty container") { std::vector v; CHECK_THROWS_AS(json::parse(v), json::parse_error); + CHECK(not json::accept(v)); } } @@ -192,42 +206,49 @@ TEST_CASE("deserialization") { 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))); } 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))); } SECTION("from array") { uint8_t 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))); } 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))); } SECTION("from std::initializer_list") { std::initializer_list 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))); } 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))); } SECTION("with empty range") { std::vector v; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } } @@ -238,90 +259,105 @@ TEST_CASE("deserialization") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 2") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 3") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 4") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 5") { uint8_t v[] = {'\"', 0x7F, 0xC1}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 6") { uint8_t v[] = {'\"', 0x7F, 0xDF, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 7") { uint8_t v[] = {'\"', 0x7F, 0xDF, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 8") { uint8_t v[] = {'\"', 0x7F, 0xE0, 0x9F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 9") { uint8_t v[] = {'\"', 0x7F, 0xEF, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 10") { uint8_t v[] = {'\"', 0x7F, 0xED, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 11") { uint8_t v[] = {'\"', 0x7F, 0xF0, 0x8F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 12") { uint8_t v[] = {'\"', 0x7F, 0xF0, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 13") { uint8_t v[] = {'\"', 0x7F, 0xF3, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 14") { uint8_t v[] = {'\"', 0x7F, 0xF3, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } SECTION("case 15") { uint8_t v[] = {'\"', 0x7F, 0xF4, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error); + CHECK(not json::accept(std::begin(v), std::end(v))); } } }