Compare commits

...

3 Commits

Author SHA1 Message Date
Niels Lohmann
cd1e38dcc4
♻️ allow to continue after parse error 2024-11-30 13:56:32 +01:00
Niels Lohmann
7c97655f59
♻️ allow to continue after parse error 2024-11-30 13:54:43 +01:00
Niels Lohmann
17b5262e7d
♻️ allow to continue after parse error 2024-11-29 17:47:58 +01:00
3 changed files with 126 additions and 48 deletions

View File

@ -212,9 +212,12 @@ class parser
// parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)))
{
return false;
}
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
@ -224,9 +227,12 @@ class parser
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)))
{
return false;
}
}
// remember we are now inside an object
@ -267,9 +273,12 @@ class parser
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)))
{
return false;
}
}
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
@ -337,24 +346,33 @@ class parser
case token_type::parse_error:
{
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)))
{
return false;
}
break;
}
case token_type::end_of_input:
{
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)))
{
return false;
}
}
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
}
case token_type::uninitialized:
case token_type::end_array:
case token_type::end_object:
@ -422,9 +440,12 @@ class parser
// parse key
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)))
{
return false;
}
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
@ -435,9 +456,12 @@ class parser
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)))
{
return false;
}
}
// parse values

View File

@ -12568,9 +12568,12 @@ class parser
// parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)))
{
return false;
}
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
@ -12580,9 +12583,12 @@ class parser
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)))
{
return false;
}
}
// remember we are now inside an object
@ -12623,9 +12629,12 @@ class parser
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)))
{
return false;
}
}
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
@ -12693,24 +12702,33 @@ class parser
case token_type::parse_error:
{
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)))
{
return false;
}
break;
}
case token_type::end_of_input:
{
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)))
{
return false;
}
}
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
}
case token_type::uninitialized:
case token_type::end_array:
case token_type::end_object:
@ -12778,9 +12796,12 @@ class parser
// parse key
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)))
{
return false;
}
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
@ -12791,9 +12812,12 @@ class parser
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
if (!sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)))
{
return false;
}
}
// parse values

View File

@ -117,15 +117,28 @@ class SaxEventLogger
return true;
}
bool parse_error(std::size_t position, const std::string& /*unused*/, const json::exception& /*unused*/)
bool parse_error(std::size_t position, const std::string& msg, const json::exception& exception)
{
errored = true;
events.push_back("parse_error(" + std::to_string(position) + ")");
return false;
events.push_back("parse_error(position=" + std::to_string(position) + ", token=<" + msg + ">, exception=" + exception.what() + ")");
return return_value_for_parse_error;
}
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
bool errored = false;
const bool return_value_for_parse_error = false;
std::string event_string()
{
return std::accumulate(events.begin(), events.end(), std::string(),
[](const std::string & a, const std::string & b)
{
return a.empty() ? b : a + '\n' + b;
});
}
explicit SaxEventLogger(bool continue_after_parse_error)
: return_value_for_parse_error(continue_after_parse_error)
{}
};
class SaxCountdown : public nlohmann::json::json_sax_t
@ -244,7 +257,7 @@ bool accept_helper(const std::string& s)
CHECK(ok_noexcept == ok_accept);
// 4. parse with SAX (compare with relaxed accept result)
SaxEventLogger el;
SaxEventLogger el(false);
CHECK_NOTHROW(json::sax_parse(s, &el, json::input_format_t::json, false));
CHECK(json::parser(nlohmann::detail::input_adapter(s)).accept(false) == !el.errored);
@ -1680,6 +1693,23 @@ TEST_CASE("parser class")
CHECK(json::sax_parse("\"foo\"", &s) == false);
}
}
SECTION("SAX parser continuing after parse error")
{
SaxEventLogger sax(true);
SECTION("Foo")
{
CHECK(json::sax_parse("[{1}, \"a\"]", &sax));
CHECK(sax.event_string() == "start_array()\n"
"start_object()\n"
"parse_error(position=3, token=<1>, exception=[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing object key - unexpected number literal; expected string literal)\n"
"key(1)\n"
"parse_error(position=4, token=<1}>, exception=[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing object separator - unexpected '}'; expected ':')\n"
"parse_error(position=5, token=<1},>, exception=[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - unexpected ','; expected '[', '{', or a literal)\n"
"parse_error(position=9, token=<\"a\">, exception=[json.exception.parse_error.101] parse error at line 1, column 9: syntax error while parsing value - unexpected string literal; expected end of input)");
}
}
}
SECTION("error messages for comments")