Skip to content

Commit c04c0c2

Browse files
CharReader: Add StructuredError (#1409)
* CharReader: Add Structured Error Add getStructuredError to CharReader * run clang format --------- Co-authored-by: Jordan Bayles <bayles.jordan@gmail.com> Co-authored-by: Jordan Bayles <jophba@chromium.org>
1 parent c857395 commit c04c0c2

File tree

3 files changed

+97
-20
lines changed

3 files changed

+97
-20
lines changed

include/json/reader.h

+26-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,12 @@ 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.
275+
*/
276+
std::vector<StructuredError> getStructuredErrors() const;
266277

267278
class JSON_API Factory {
268279
public:
@@ -272,6 +283,20 @@ class JSON_API CharReader {
272283
*/
273284
virtual CharReader* newCharReader() const = 0;
274285
}; // Factory
286+
287+
protected:
288+
class Impl {
289+
public:
290+
virtual ~Impl() = default;
291+
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
292+
String* errs) = 0;
293+
virtual std::vector<StructuredError> getStructuredErrors() const = 0;
294+
};
295+
296+
explicit CharReader(std::unique_ptr<Impl> impl) : _impl(std::move(impl)) {}
297+
298+
private:
299+
std::unique_ptr<Impl> _impl;
275300
}; // CharReader
276301

277302
/** \brief Build a CharReader implementation.

src/lib_json/json_reader.cpp

+41-19
Original file line numberDiff line numberDiff line change
@@ -878,17 +878,12 @@ class OurReader {
878878
public:
879879
using Char = char;
880880
using Location = const Char*;
881-
struct StructuredError {
882-
ptrdiff_t offset_start;
883-
ptrdiff_t offset_limit;
884-
String message;
885-
};
886881

887882
explicit OurReader(OurFeatures const& features);
888883
bool parse(const char* beginDoc, const char* endDoc, Value& root,
889884
bool collectComments = true);
890885
String getFormattedErrorMessages() const;
891-
std::vector<StructuredError> getStructuredErrors() const;
886+
std::vector<CharReader::StructuredError> getStructuredErrors() const;
892887

893888
private:
894889
OurReader(OurReader const&); // no impl
@@ -1836,10 +1831,11 @@ String OurReader::getFormattedErrorMessages() const {
18361831
return formattedMessage;
18371832
}
18381833

1839-
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
1840-
std::vector<OurReader::StructuredError> allErrors;
1834+
std::vector<CharReader::StructuredError>
1835+
OurReader::getStructuredErrors() const {
1836+
std::vector<CharReader::StructuredError> allErrors;
18411837
for (const auto& error : errors_) {
1842-
OurReader::StructuredError structured;
1838+
CharReader::StructuredError structured;
18431839
structured.offset_start = error.token_.start_ - begin_;
18441840
structured.offset_limit = error.token_.end_ - begin_;
18451841
structured.message = error.message_;
@@ -1849,20 +1845,36 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
18491845
}
18501846

18511847
class OurCharReader : public CharReader {
1852-
bool const collectComments_;
1853-
OurReader reader_;
18541848

18551849
public:
18561850
OurCharReader(bool collectComments, OurFeatures const& features)
1857-
: collectComments_(collectComments), reader_(features) {}
1858-
bool parse(char const* beginDoc, char const* endDoc, Value* root,
1859-
String* errs) override {
1860-
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1861-
if (errs) {
1862-
*errs = reader_.getFormattedErrorMessages();
1851+
: CharReader(
1852+
std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1853+
1854+
protected:
1855+
class OurImpl : public Impl {
1856+
public:
1857+
OurImpl(bool collectComments, OurFeatures const& features)
1858+
: collectComments_(collectComments), reader_(features) {}
1859+
1860+
bool parse(char const* beginDoc, char const* endDoc, Value* root,
1861+
String* errs) override {
1862+
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1863+
if (errs) {
1864+
*errs = reader_.getFormattedErrorMessages();
1865+
}
1866+
return ok;
18631867
}
1864-
return ok;
1865-
}
1868+
1869+
std::vector<CharReader::StructuredError>
1870+
getStructuredErrors() const override {
1871+
return reader_.getStructuredErrors();
1872+
}
1873+
1874+
private:
1875+
bool const collectComments_;
1876+
OurReader reader_;
1877+
};
18661878
};
18671879

18681880
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
@@ -1952,6 +1964,16 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
19521964
//! [CharReaderBuilderDefaults]
19531965
}
19541966

1967+
std::vector<CharReader::StructuredError>
1968+
CharReader::getStructuredErrors() const {
1969+
return _impl->getStructuredErrors();
1970+
}
1971+
1972+
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
1973+
String* errs) {
1974+
return _impl->parse(beginDoc, endDoc, root, errs);
1975+
}
1976+
19551977
//////////////////////////////////
19561978
// global functions
19571979

src/test_lib_json/main.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -3917,6 +3917,36 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
39173917
example.size()));
39183918
}
39193919

3920+
struct ParseWithStructuredErrorsTest : JsonTest::TestCase {
3921+
void testErrors(
3922+
const std::string& doc, bool success,
3923+
const std::vector<Json::CharReader::StructuredError>& expectedErrors) {
3924+
Json::CharReaderBuilder b;
3925+
CharReaderPtr reader(b.newCharReader());
3926+
Json::Value root;
3927+
JSONTEST_ASSERT_EQUAL(
3928+
reader->parse(doc.data(), doc.data() + doc.length(), &root, nullptr),
3929+
success);
3930+
auto actualErrors = reader->getStructuredErrors();
3931+
JSONTEST_ASSERT_EQUAL(expectedErrors.size(), actualErrors.size());
3932+
for (std::size_t i = 0; i < actualErrors.size(); i++) {
3933+
const auto& a = actualErrors[i];
3934+
const auto& e = expectedErrors[i];
3935+
JSONTEST_ASSERT_EQUAL(a.offset_start, e.offset_start);
3936+
JSONTEST_ASSERT_EQUAL(a.offset_limit, e.offset_limit);
3937+
JSONTEST_ASSERT_STRING_EQUAL(a.message, e.message);
3938+
}
3939+
}
3940+
};
3941+
3942+
JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, success) {
3943+
testErrors("{}", true, {});
3944+
}
3945+
3946+
JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, singleError) {
3947+
testErrors("{ 1 : 2 }", false, {{2, 3, "Missing '}' or object member name"}});
3948+
}
3949+
39203950
int main(int argc, const char* argv[]) {
39213951
JsonTest::Runner runner;
39223952

0 commit comments

Comments
 (0)