Skip to content

Commit aab0cc8

Browse files
committed
CharReader: Add StructuredError
Add getStructuredError to CharReader.
1 parent 42e892d commit aab0cc8

File tree

5 files changed

+91
-21
lines changed

5 files changed

+91
-21
lines changed

include/json/reader.h

+30-4
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
*/
273-
virtual CharReader* newCharReader() const = 0;
295+
virtual CharReader::Impl* newCharReader() const = 0;
274296
}; // Factory
297+
298+
private:
299+
std::unique_ptr<Impl> _impl;
300+
275301
}; // CharReader
276302

277303
/** \brief Build a CharReader implementation.
@@ -337,7 +363,7 @@ class JSON_API CharReaderBuilder : public CharReader::Factory {
337363
CharReaderBuilder();
338364
~CharReaderBuilder() override;
339365

340-
CharReader* newCharReader() const override;
366+
CharReader::Impl* newCharReader() const override;
341367

342368
/** \return true if 'settings' are legal and consistent;
343369
* otherwise, indicate bad settings via 'invalid'.

src/jsontestrunner/main.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ static int parseAndSaveValueTree(const Json::String& input,
138138
features.allowDroppedNullPlaceholders_;
139139
builder.settings_["allowNumericKeys"] = features.allowNumericKeys_;
140140

141-
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
141+
std::unique_ptr<Json::CharReader::Impl> reader(builder.newCharReader());
142142
Json::String errors;
143143
const bool parsingSuccessful =
144144
reader->parse(input.data(), input.data() + input.size(), root, &errors);

src/lib_json/json_reader.cpp

+20-13
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ static size_t const stackLimit_g =
5454
namespace Json {
5555

5656
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
57-
using CharReaderPtr = std::unique_ptr<CharReader>;
57+
using CharReaderPtr = std::unique_ptr<CharReader::Impl>;
5858
#else
59-
using CharReaderPtr = std::auto_ptr<CharReader>;
59+
using CharReaderPtr = std::auto_ptr<CharReader::Impl>;
6060
#endif
6161

6262
// Implementation of class Features
@@ -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_;
@@ -1872,7 +1867,7 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
18721867
return allErrors;
18731868
}
18741869

1875-
class OurCharReader : public CharReader {
1870+
class OurCharReader : public CharReader::Impl {
18761871
bool const collectComments_;
18771872
OurReader reader_;
18781873

@@ -1887,11 +1882,14 @@ class OurCharReader : public CharReader {
18871882
}
18881883
return ok;
18891884
}
1885+
std::vector<CharReader::StructuredError> getStructuredErrors() const {
1886+
return reader_.getStructuredErrors();
1887+
}
18901888
};
18911889

18921890
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
18931891
CharReaderBuilder::~CharReaderBuilder() = default;
1894-
CharReader* CharReaderBuilder::newCharReader() const {
1892+
CharReader::Impl* CharReaderBuilder::newCharReader() const {
18951893
bool collectComments = settings_["collectComments"].asBool();
18961894
OurFeatures features = OurFeatures::all();
18971895
features.allowComments_ = settings_["allowComments"].asBool();
@@ -1976,6 +1974,15 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
19761974
//! [CharReaderBuilderDefaults]
19771975
}
19781976

1977+
auto CharReader::getStructuredErrors() const -> std::vector<CharReader::StructuredError> {
1978+
return _impl->getStructuredErrors();
1979+
}
1980+
1981+
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
1982+
String* errs) {
1983+
return _impl->parse(beginDoc, endDoc, root, errs);
1984+
}
1985+
19791986
//////////////////////////////////
19801987
// global functions
19811988

src/test_lib_json/fuzz.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
4141
builder.settings_["collectComments"] = hash_settings & (1 << 9);
4242
builder.settings_["allowTrailingCommas_"] = hash_settings & (1 << 10);
4343

44-
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
44+
std::unique_ptr<Json::CharReader::Impl> reader(builder.newCharReader());
4545

4646
Json::Value root;
4747
const auto data_str = reinterpret_cast<const char*>(data);

src/test_lib_json/main.cpp

+39-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include <string>
2828
#include <vector>
2929

30-
using CharReaderPtr = std::unique_ptr<Json::CharReader>;
30+
using CharReaderPtr = std::unique_ptr<Json::CharReader::Impl>;
3131

3232
// Make numeric limits more convenient to talk about.
3333
// Assumes int type in 32 bits.
@@ -3454,7 +3454,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowDropNullTest, issue178) {
34543454
for (const auto& spec : specs) {
34553455
Json::CharReaderBuilder b;
34563456
b.settings_["allowDroppedNullPlaceholders"] = true;
3457-
std::unique_ptr<Json::CharReader> reader(b.newCharReader());
3457+
std::unique_ptr<Json::CharReader::Impl> reader(b.newCharReader());
34583458

34593459
Json::Value root;
34603460
Json::String errs;
@@ -3903,6 +3903,43 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
39033903
example.size()));
39043904
}
39053905

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

0 commit comments

Comments
 (0)