diff --git a/include/json/reader.h b/include/json/reader.h index be0d7676a..4fabe6b14 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -243,6 +243,13 @@ class JSON_API Reader { */ class JSON_API CharReader { public: + class JSON_API StructuredError { + public: + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + virtual ~CharReader() = default; /** \brief Read a Value from a JSON * document. The document must be a UTF-8 encoded string containing the @@ -263,6 +270,12 @@ class JSON_API CharReader { virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, String* errs) = 0; + /** \brief Get a list of structured error messages from parsing the document. + * + * \return list of error messages. + */ + virtual std::vector getStructuredErrors() const = 0; + class JSON_API Factory { public: virtual ~Factory() = default; diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 1ac5e81ab..719a35a5a 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -890,17 +890,12 @@ class OurReader { public: using Char = char; using Location = const Char*; - struct StructuredError { - ptrdiff_t offset_start; - ptrdiff_t offset_limit; - String message; - }; explicit OurReader(OurFeatures const& features); bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); String getFormattedErrorMessages() const; - std::vector getStructuredErrors() const; + std::vector getStructuredErrors() const; private: OurReader(OurReader const&); // no impl @@ -1860,10 +1855,11 @@ String OurReader::getFormattedErrorMessages() const { return formattedMessage; } -std::vector OurReader::getStructuredErrors() const { - std::vector allErrors; +std::vector +OurReader::getStructuredErrors() const { + std::vector allErrors; for (const auto& error : errors_) { - OurReader::StructuredError structured; + CharReader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; structured.message = error.message_; @@ -1887,6 +1883,10 @@ class OurCharReader : public CharReader { } return ok; } + + std::vector getStructuredErrors() const override { + return reader_.getStructuredErrors(); + } }; CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index d0f5364ac..d75cddf5c 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -3903,6 +3903,43 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) { example.size())); } +struct ParseWithStructuredErrorsTest : JsonTest::TestCase { + void + testErrors(const std::string& doc, bool success, + const std::vector& errors) { + Json::CharReaderBuilder b; + CharReaderPtr reader(b.newCharReader()); + Json::Value root; + JSONTEST_ASSERT_EQUAL( + success, + reader->parse(doc.data(), doc.data() + doc.length(), &root, nullptr)); + auto actualErrors = reader->getStructuredErrors(); + JSONTEST_ASSERT_EQUAL(errors.size(), actualErrors.size()); + for (std::size_t i = 0; i < errors.size() && i < actualErrors.size(); i++) { + JSONTEST_ASSERT_EQUAL(errors[i].offset_start, + actualErrors[i].offset_start); + JSONTEST_ASSERT_EQUAL(errors[i].offset_limit, + actualErrors[i].offset_limit); + JSONTEST_ASSERT_STRING_EQUAL(errors[i].message, actualErrors[i].message); + } + } +}; + +JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, success) { + testErrors("{}", true, {}); +} + +JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, singleError) { + testErrors("{ 1 : 2 }", false, + { + { + /*offset_start=*/2, + /*offset_limit=*/3, + /*message=*/"Missing '}' or object member name", + }, + }); +} + int main(int argc, const char* argv[]) { JsonTest::Runner runner;