The return value of json_sax::parse_error() was documented both as "must return false" and as "whether the parsing should continue", and the code just passed it on. For JSON text, parsing stopped anyway, but sax_parse() could report success for invalid input. The binary readers read on after the error, looping forever on a CBOR indefinite-length array without its end. Now false stops parsing, and true recovers from the error: - JSON text is repaired with the smallest local edit (insert a missing ',' or ':', remove a stray token, keep the readable part of a broken string or number, null for a value that cannot be read, close the innermost container at a wrong closing bracket and all of them at the end of the input), and parsing continues. The SAX events stay balanced, every key is followed by exactly one value, and each token is reported at most once. - The binary formats cannot resynchronize, so they stop, but complete the value read so far. sax_parse() returns false after any error. parse(), accept(), and the from_*() functions never recover and compile to the same code as before. Supersedes #4522. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
44 lines
1.2 KiB
C++
44 lines
1.2 KiB
C++
#include <iostream>
|
|
#include <iomanip>
|
|
#include <nlohmann/json.hpp>
|
|
|
|
using json = nlohmann::json;
|
|
|
|
// a SAX parser that creates a JSON value like json::parse does, but that
|
|
// recovers from parse errors instead of stopping at the first one
|
|
class recovering_parser : public nlohmann::detail::json_sax_dom_parser<json>
|
|
{
|
|
public:
|
|
explicit recovering_parser(json& result)
|
|
: nlohmann::detail::json_sax_dom_parser<json>(result, false)
|
|
{}
|
|
|
|
bool parse_error(std::size_t position,
|
|
const std::string& /*last_token*/,
|
|
const json::exception& ex)
|
|
{
|
|
std::cout << "byte " << position << ": " << ex.what() << '\n';
|
|
|
|
// repair the input and continue
|
|
return true;
|
|
}
|
|
};
|
|
|
|
int main()
|
|
{
|
|
// JSON text with several mistakes that ends too early
|
|
const std::string text = R"({
|
|
"name": "Hello World",
|
|
"tags": ["a" "b",],
|
|
"valid": tru,
|
|
"size": 1.,
|
|
"nested": {"x": 1)";
|
|
|
|
json result;
|
|
recovering_parser sax(result);
|
|
const bool valid = json::sax_parse(text, &sax);
|
|
|
|
std::cout << "\nvalid JSON: " << std::boolalpha << valid << '\n'
|
|
<< std::setw(4) << result << std::endl;
|
|
}
|