Skip to content

Commit 59780d8

Browse files
committed
CharReader: Add Structured Error
Add getStructuredError to CharReader
1 parent 42e892d commit 59780d8

File tree

4 files changed

+99
-24
lines changed

4 files changed

+99
-24
lines changed

include/json/reader.h

+28-2
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,14 @@ class JSON_API Reader {
244244
*/
245245
class JSON_API CharReader {
246246
public:
247+
248+
class JSON_API StructuredError {
249+
public:
250+
ptrdiff_t offset_start;
251+
ptrdiff_t offset_limit;
252+
String message;
253+
};
254+
247255
virtual ~CharReader() = default;
248256
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
249257
* document. The document must be a UTF-8 encoded string containing the
@@ -262,16 +270,34 @@ class JSON_API CharReader {
262270
* error occurred.
263271
*/
264272
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
265-
String* errs) = 0;
273+
String* errs) final;
274+
275+
/** \brief Returns a vector of structured errors encountered while parsing.
276+
*
277+
* \return A (possibly empty) vector of StructuredError objects.
278+
*/
279+
std::vector<StructuredError> getStructuredErrors() const;
280+
281+
class Impl {
282+
public:
283+
virtual ~Impl() = default;
284+
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
285+
String* errs) = 0;
286+
virtual std::vector<CharReader::StructuredError> getStructuredErrors() const = 0;
287+
};
266288

267289
class JSON_API Factory {
268290
public:
269291
virtual ~Factory() = default;
270-
/** \brief Allocate a CharReader via operator new().
292+
/** \brief Allocate a CharReader Impl via operator new().
271293
* \throw std::exception if something goes wrong (e.g. invalid settings)
272294
*/
273295
virtual CharReader* newCharReader() const = 0;
274296
}; // Factory
297+
298+
protected:
299+
std::unique_ptr<Impl> _impl;
300+
275301
}; // CharReader
276302

277303
/** \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

+38-18
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_;
@@ -1873,20 +1868,36 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
18731868
}
18741869

18751870
class OurCharReader : public CharReader {
1871+
public:
18761872
bool const collectComments_;
18771873
OurReader reader_;
18781874

1879-
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<OurCharReader::Impl>(new OurCharReader::Impl(collectComments, features));
1878+
}
1879+
1880+
class Impl : public CharReader::Impl {
1881+
public:
1882+
bool const collectComments_;
1883+
OurReader reader_;
1884+
1885+
Impl(bool collectComments, OurFeatures const& features)
1886+
: collectComments_(collectComments), reader_(features) {}
1887+
1888+
bool parse(char const* beginDoc, char const* endDoc, Value* root,
1889+
String* errs) override {
1890+
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1891+
if (errs) {
1892+
*errs = reader_.getFormattedErrorMessages();
1893+
}
1894+
return ok;
18871895
}
1888-
return ok;
1889-
}
1896+
1897+
std::vector<CharReader::StructuredError> getStructuredErrors() const {
1898+
return reader_.getStructuredErrors();
1899+
}
1900+
};
18901901
};
18911902

18921903
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
@@ -1976,6 +1987,15 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
19761987
//! [CharReaderBuilderDefaults]
19771988
}
19781989

1990+
std::vector<CharReader::StructuredError> CharReader::getStructuredErrors() const {
1991+
return _impl->getStructuredErrors();
1992+
}
1993+
1994+
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
1995+
String* errs) {
1996+
return _impl->parse(beginDoc, endDoc, root, errs);
1997+
}
1998+
19791999
//////////////////////////////////
19802000
// global functions
19812001

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)