Skip to content

Commit 4c0a5ef

Browse files
committed
CharReader: Add Structured Error
Add getStructuredError to CharReader
1 parent 42e892d commit 4c0a5ef

File tree

4 files changed

+93
-22
lines changed

4 files changed

+93
-22
lines changed

include/json/reader.h

+25-1
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ class JSON_API Reader {
244244
*/
245245
class JSON_API CharReader {
246246
public:
247+
struct JSON_API StructuredError {
248+
ptrdiff_t offset_start;
249+
ptrdiff_t offset_limit;
250+
String message;
251+
};
252+
247253
virtual ~CharReader() = default;
248254
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
249255
* document. The document must be a UTF-8 encoded string containing the
@@ -262,7 +268,22 @@ class JSON_API CharReader {
262268
* error occurred.
263269
*/
264270
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
265-
String* errs) = 0;
271+
String* errs);
272+
273+
/** \brief Returns a vector of structured errors encountered while parsing.
274+
* Each parse call resets the stored list of errors. Each call to this
275+
* function reads the stored list of errors and builds a vector of
276+
* structured errors to return.
277+
*/
278+
std::vector<StructuredError> getStructuredErrors() const;
279+
280+
class Impl {
281+
public:
282+
virtual ~Impl() = default;
283+
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
284+
String* errs) = 0;
285+
virtual std::vector<StructuredError> getStructuredErrors() const = 0;
286+
};
266287

267288
class JSON_API Factory {
268289
public:
@@ -272,6 +293,9 @@ class JSON_API CharReader {
272293
*/
273294
virtual CharReader* newCharReader() const = 0;
274295
}; // Factory
296+
297+
protected:
298+
std::unique_ptr<Impl> _impl;
275299
}; // CharReader
276300

277301
/** \brief Build a CharReader implementation.

include/json/value.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -437,23 +437,23 @@ class JSON_API Value {
437437
/// \post type() is arrayValue
438438
void resize(ArrayIndex newSize);
439439

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

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

458458
/// If the array contains at least index+1 elements, returns the element
459459
/// value, otherwise returns defaultValue.

src/lib_json/json_reader.cpp

+35-17
Original file line numberDiff line numberDiff line change
@@ -890,17 +890,12 @@ class OurReader {
890890
public:
891891
using Char = char;
892892
using Location = const Char*;
893-
struct StructuredError {
894-
ptrdiff_t offset_start;
895-
ptrdiff_t offset_limit;
896-
String message;
897-
};
898893

899894
explicit OurReader(OurFeatures const& features);
900895
bool parse(const char* beginDoc, const char* endDoc, Value& root,
901896
bool collectComments = true);
902897
String getFormattedErrorMessages() const;
903-
std::vector<StructuredError> getStructuredErrors() const;
898+
std::vector<CharReader::StructuredError> getStructuredErrors() const;
904899

905900
private:
906901
OurReader(OurReader const&); // no impl
@@ -1860,10 +1855,10 @@ String OurReader::getFormattedErrorMessages() const {
18601855
return formattedMessage;
18611856
}
18621857

1863-
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
1864-
std::vector<OurReader::StructuredError> allErrors;
1858+
std::vector<CharReader::StructuredError> OurReader::getStructuredErrors() const {
1859+
std::vector<CharReader::StructuredError> allErrors;
18651860
for (const auto& error : errors_) {
1866-
OurReader::StructuredError structured;
1861+
CharReader::StructuredError structured;
18671862
structured.offset_start = error.token_.start_ - begin_;
18681863
structured.offset_limit = error.token_.end_ - begin_;
18691864
structured.message = error.message_;
@@ -1878,15 +1873,29 @@ class OurCharReader : public CharReader {
18781873

18791874
public:
18801875
OurCharReader(bool collectComments, OurFeatures const& features)
1881-
: collectComments_(collectComments), reader_(features) {}
1882-
bool parse(char const* beginDoc, char const* endDoc, Value* root,
1883-
String* errs) override {
1884-
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1885-
if (errs) {
1886-
*errs = reader_.getFormattedErrorMessages();
1876+
: collectComments_(collectComments), reader_(features) {
1877+
this->_impl=std::unique_ptr<Impl>(new Impl(collectComments, features));
1878+
}
1879+
1880+
class Impl : public CharReader::Impl {
1881+
bool const collectComments_;
1882+
OurReader reader_;
1883+
1884+
public:
1885+
Impl(bool collectComments, OurFeatures const& features)
1886+
: collectComments_(collectComments), reader_(features) {}
1887+
bool parse(char const* beginDoc, char const* endDoc, Value* root,
1888+
String* errs) override {
1889+
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1890+
if (errs) {
1891+
*errs = reader_.getFormattedErrorMessages();
1892+
}
1893+
return ok;
18871894
}
1888-
return ok;
1889-
}
1895+
std::vector<CharReader::StructuredError> getStructuredErrors() const override {
1896+
return reader_.getStructuredErrors();
1897+
}
1898+
};
18901899
};
18911900

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

1988+
std::vector<CharReader::StructuredError> CharReader::getStructuredErrors() const {
1989+
return _impl->getStructuredErrors();
1990+
}
1991+
1992+
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
1993+
String* errs) {
1994+
return _impl->parse(beginDoc, endDoc, root, errs);
1995+
}
1996+
19791997
//////////////////////////////////
19801998
// global functions
19811999

src/test_lib_json/main.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -3903,6 +3903,35 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
39033903
example.size()));
39043904
}
39053905

3906+
struct ParseWithStructuredErrorsTest : JsonTest::TestCase {
3907+
void testErrors(const std::string& doc, bool success,
3908+
const std::vector<Json::CharReader::StructuredError>& expectedErrors) {
3909+
Json::CharReaderBuilder b;
3910+
CharReaderPtr reader(b.newCharReader());
3911+
Json::Value root;
3912+
JSONTEST_ASSERT_EQUAL(
3913+
reader->parse(doc.data(), doc.data() + doc.length(), &root, nullptr),
3914+
success);
3915+
auto actualErrors = reader->getStructuredErrors();
3916+
JSONTEST_ASSERT_EQUAL(expectedErrors.size(), actualErrors.size());
3917+
for (std::size_t i = 0; i < actualErrors.size(); i++) {
3918+
const auto& a = actualErrors[i];
3919+
const auto& e = expectedErrors[i];
3920+
JSONTEST_ASSERT_EQUAL(a.offset_start, e.offset_start);
3921+
JSONTEST_ASSERT_EQUAL(a.offset_limit, e.offset_limit);
3922+
JSONTEST_ASSERT_STRING_EQUAL(a.message, e.message);
3923+
}
3924+
}
3925+
};
3926+
3927+
JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, success) {
3928+
testErrors("{}", true, {});
3929+
}
3930+
3931+
JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, singleError) {
3932+
testErrors("{ 1 : 2 }", false, {{2, 3, "Missing '}' or object member name"}});
3933+
}
3934+
39063935
int main(int argc, const char* argv[]) {
39073936
JsonTest::Runner runner;
39083937

0 commit comments

Comments
 (0)