diff --git a/include/json/reader.h b/include/json/reader.h
index 85539d1..38b9360 100644
--- a/include/json/reader.h
+++ b/include/json/reader.h
@@ -244,6 +244,12 @@ private:
*/
class JSON_API CharReader {
public:
+ struct JSON_API StructuredError {
+ 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
@@ -262,7 +268,12 @@ public:
* error occurred.
*/
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
- String* errs) = 0;
+ String* errs);
+
+ /** \brief Returns a vector of structured errors encountered while parsing.
+ * Each parse call resets the stored list of errors.
+ */
+ std::vector getStructuredErrors() const;
class JSON_API Factory {
public:
@@ -272,6 +283,20 @@ public:
*/
virtual CharReader* newCharReader() const = 0;
}; // Factory
+
+protected:
+ class Impl {
+ public:
+ virtual ~Impl() = default;
+ virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
+ String* errs) = 0;
+ virtual std::vector getStructuredErrors() const = 0;
+ };
+
+ explicit CharReader(std::unique_ptr impl) : _impl(std::move(impl)) {}
+
+private:
+ std::unique_ptr _impl;
}; // CharReader
/** \brief Build a CharReader implementation.
diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp
index 86ed030..4ab4dff 100644
--- a/src/lib_json/json_reader.cpp
+++ b/src/lib_json/json_reader.cpp
@@ -878,17 +878,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
@@ -1836,10 +1831,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_;
@@ -1849,20 +1845,36 @@ std::vector OurReader::getStructuredErrors() const {
}
class OurCharReader : public CharReader {
- bool const collectComments_;
- OurReader reader_;
public:
OurCharReader(bool collectComments, OurFeatures const& features)
- : collectComments_(collectComments), reader_(features) {}
- bool parse(char const* beginDoc, char const* endDoc, Value* root,
- String* errs) override {
- bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
- if (errs) {
- *errs = reader_.getFormattedErrorMessages();
+ : CharReader(
+ std::unique_ptr(new OurImpl(collectComments, features))) {}
+
+protected:
+ class OurImpl : public Impl {
+ public:
+ OurImpl(bool collectComments, OurFeatures const& features)
+ : collectComments_(collectComments), reader_(features) {}
+
+ bool parse(char const* beginDoc, char const* endDoc, Value* root,
+ String* errs) override {
+ bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
+ if (errs) {
+ *errs = reader_.getFormattedErrorMessages();
+ }
+ return ok;
}
- return ok;
- }
+
+ std::vector
+ getStructuredErrors() const override {
+ return reader_.getStructuredErrors();
+ }
+
+ private:
+ bool const collectComments_;
+ OurReader reader_;
+ };
};
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
@@ -1952,6 +1964,16 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
//! [CharReaderBuilderDefaults]
}
+std::vector
+CharReader::getStructuredErrors() const {
+ return _impl->getStructuredErrors();
+}
+
+bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
+ String* errs) {
+ return _impl->parse(beginDoc, endDoc, root, errs);
+}
+
//////////////////////////////////
// global functions
diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp
index 1ef33bb..fa41d19 100644
--- a/src/test_lib_json/main.cpp
+++ b/src/test_lib_json/main.cpp
@@ -3917,6 +3917,36 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
example.size()));
}
+struct ParseWithStructuredErrorsTest : JsonTest::TestCase {
+ void testErrors(
+ const std::string& doc, bool success,
+ const std::vector& expectedErrors) {
+ Json::CharReaderBuilder b;
+ CharReaderPtr reader(b.newCharReader());
+ Json::Value root;
+ JSONTEST_ASSERT_EQUAL(
+ reader->parse(doc.data(), doc.data() + doc.length(), &root, nullptr),
+ success);
+ auto actualErrors = reader->getStructuredErrors();
+ JSONTEST_ASSERT_EQUAL(expectedErrors.size(), actualErrors.size());
+ for (std::size_t i = 0; i < actualErrors.size(); i++) {
+ const auto& a = actualErrors[i];
+ const auto& e = expectedErrors[i];
+ JSONTEST_ASSERT_EQUAL(a.offset_start, e.offset_start);
+ JSONTEST_ASSERT_EQUAL(a.offset_limit, e.offset_limit);
+ JSONTEST_ASSERT_STRING_EQUAL(a.message, e.message);
+ }
+ }
+};
+
+JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, success) {
+ testErrors("{}", true, {});
+}
+
+JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, singleError) {
+ testErrors("{ 1 : 2 }", false, {{2, 3, "Missing '}' or object member name"}});
+}
+
int main(int argc, const char* argv[]) {
JsonTest::Runner runner;