Skip to content

Commit

Permalink
CharReader: Add Structured Error
Browse files Browse the repository at this point in the history
Add getStructuredError to CharReader
  • Loading branch information
martinduffy1 committed Oct 5, 2022
1 parent 42e892d commit 4c0a5ef
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 22 deletions.
26 changes: 25 additions & 1 deletion include/json/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,12 @@ class JSON_API Reader {
*/
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 <a HREF="http://www.json.org">JSON</a>
* document. The document must be a UTF-8 encoded string containing the
Expand All @@ -262,7 +268,22 @@ class JSON_API CharReader {
* 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. Each call to this
* function reads the stored list of errors and builds a vector of
* structured errors to return.
*/
std::vector<StructuredError> getStructuredErrors() const;

class Impl {
public:
virtual ~Impl() = default;
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
virtual std::vector<StructuredError> getStructuredErrors() const = 0;
};

class JSON_API Factory {
public:
Expand All @@ -272,6 +293,9 @@ class JSON_API CharReader {
*/
virtual CharReader* newCharReader() const = 0;
}; // Factory

protected:
std::unique_ptr<Impl> _impl;
}; // CharReader

/** \brief Build a CharReader implementation.
Expand Down
8 changes: 4 additions & 4 deletions include/json/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,23 +437,23 @@ class JSON_API Value {
/// \post type() is arrayValue
void resize(ArrayIndex newSize);

//@{
///@{
/// Access an array element (zero based index). If the array contains less
/// than index element, then null value are inserted in the array so that
/// its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index);
Value& operator[](int index);
//@}
///@}

//@{
///@{
/// Access an array element (zero based index).
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const;
const Value& operator[](int index) const;
//@}
///@}

/// If the array contains at least index+1 elements, returns the element
/// value, otherwise returns defaultValue.
Expand Down
52 changes: 35 additions & 17 deletions src/lib_json/json_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<StructuredError> getStructuredErrors() const;
std::vector<CharReader::StructuredError> getStructuredErrors() const;

private:
OurReader(OurReader const&); // no impl
Expand Down Expand Up @@ -1860,10 +1855,10 @@ String OurReader::getFormattedErrorMessages() const {
return formattedMessage;
}

std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
std::vector<OurReader::StructuredError> allErrors;
std::vector<CharReader::StructuredError> OurReader::getStructuredErrors() const {
std::vector<CharReader::StructuredError> 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_;
Expand All @@ -1878,15 +1873,29 @@ class OurCharReader : public CharReader {

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();
: collectComments_(collectComments), reader_(features) {
this->_impl=std::unique_ptr<Impl>(new Impl(collectComments, features));
}

class Impl : public CharReader::Impl {
bool const collectComments_;
OurReader reader_;

public:
Impl(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<CharReader::StructuredError> getStructuredErrors() const override {
return reader_.getStructuredErrors();
}
};
};

CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
Expand Down Expand Up @@ -1976,6 +1985,15 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
//! [CharReaderBuilderDefaults]
}

std::vector<CharReader::StructuredError> 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

Expand Down
29 changes: 29 additions & 0 deletions src/test_lib_json/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3903,6 +3903,35 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
example.size()));
}

struct ParseWithStructuredErrorsTest : JsonTest::TestCase {
void testErrors(const std::string& doc, bool success,
const std::vector<Json::CharReader::StructuredError>& 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;

Expand Down

0 comments on commit 4c0a5ef

Please sign in to comment.