Skip to content

Commit a587d04

Browse files
committed
Merge pull request open-source-parsers#161 from cdunn2001/master
CharReader/Builder I guess we should but the patch-level version. We will set the version properly soon...
2 parents 2a94618 + 2c1197c commit a587d04

3 files changed

Lines changed: 214 additions & 4 deletions

File tree

include/json/reader.h

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <iosfwd>
1515
#include <stack>
1616
#include <string>
17+
#include <istream>
1718

1819
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
1920
// be used by...
@@ -78,7 +79,7 @@ class JSON_API Reader {
7879
document to read.
7980
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
8081
document to read.
81-
\ Must be >= beginDoc.
82+
* Must be >= beginDoc.
8283
* \param root [out] Contains the root value of the document if it was
8384
* successfully parsed.
8485
* \param collectComments \c true to collect comment and allow writing them
@@ -238,8 +239,69 @@ class JSON_API Reader {
238239
std::string commentsBefore_;
239240
Features features_;
240241
bool collectComments_;
242+
}; // Reader
243+
244+
/** Interface for reading JSON from a char array.
245+
*/
246+
class JSON_API CharReader {
247+
public:
248+
virtual ~CharReader() {}
249+
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
250+
document.
251+
* The document must be a UTF-8 encoded string containing the document to read.
252+
*
253+
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
254+
document to read.
255+
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
256+
document to read.
257+
* Must be >= beginDoc.
258+
* \param root [out] Contains the root value of the document if it was
259+
* successfully parsed.
260+
* \param errs [out] Formatted error messages (if not NULL)
261+
* a user friendly string that lists errors in the parsed
262+
* document.
263+
* \return \c true if the document was successfully parsed, \c false if an
264+
error occurred.
265+
*/
266+
virtual bool parse(
267+
char const* beginDoc, char const* endDoc,
268+
Value* root, std::string* errs) = 0;
269+
270+
class Factory {
271+
public:
272+
/// \brief Allocate a CharReader via operator new().
273+
virtual CharReader* newCharReader() const = 0;
274+
}; // Factory
275+
}; // CharReader
276+
277+
class CharReaderBuilder : public CharReader::Factory {
278+
bool collectComments_;
279+
Features features_;
280+
public:
281+
CharReaderBuilder();
282+
283+
CharReaderBuilder& withCollectComments(bool v) {
284+
collectComments_ = v;
285+
return *this;
286+
}
287+
288+
CharReaderBuilder& withFeatures(Features const& v) {
289+
features_ = v;
290+
return *this;
291+
}
292+
293+
virtual CharReader* newCharReader() const;
241294
};
242295

296+
/** Consume entire stream and use its begin/end.
297+
* Someday we might have a real StreamReader, but for now this
298+
* is convenient.
299+
*/
300+
bool parseFromStream(
301+
CharReader::Factory const&,
302+
std::istream&,
303+
Value* root, std::string* errs);
304+
243305
/** \brief Read from 'sin' into 'root'.
244306
245307
Always keep comments from the input JSON.

src/lib_json/json_reader.cpp

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <cassert>
1515
#include <cstring>
1616
#include <istream>
17+
#include <sstream>
18+
#include <memory>
1719

1820
#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
1921
#define snprintf _snprintf
@@ -26,6 +28,12 @@
2628

2729
namespace Json {
2830

31+
#if __cplusplus >= 201103L
32+
typedef std::unique_ptr<CharReader> CharReaderPtr;
33+
#else
34+
typedef std::auto_ptr<CharReader> CharReaderPtr;
35+
#endif
36+
2937
// Implementation of class Features
3038
// ////////////////////////////////
3139

@@ -882,13 +890,61 @@ bool Reader::good() const {
882890
return !errors_.size();
883891
}
884892

893+
class OldReader : public CharReader {
894+
bool const collectComments_;
895+
Reader reader_;
896+
public:
897+
OldReader(
898+
bool collectComments,
899+
Features const& features)
900+
: collectComments_(collectComments)
901+
, reader_(features)
902+
{}
903+
virtual bool parse(
904+
char const* beginDoc, char const* endDoc,
905+
Value* root, std::string* errs) {
906+
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
907+
if (errs) {
908+
*errs = reader_.getFormattedErrorMessages();
909+
}
910+
return ok;
911+
}
912+
};
913+
914+
CharReaderBuilder::CharReaderBuilder()
915+
: collectComments_(true)
916+
, features_(Features::all())
917+
{}
918+
CharReader* CharReaderBuilder::newCharReader() const
919+
{
920+
return new OldReader(collectComments_, features_);
921+
}
922+
923+
//////////////////////////////////
924+
// global functions
925+
926+
bool parseFromStream(
927+
CharReader::Factory const& fact, std::istream& sin,
928+
Value* root, std::string* errs)
929+
{
930+
std::ostringstream ssin;
931+
ssin << sin.rdbuf();
932+
std::string doc = ssin.str();
933+
char const* begin = doc.data();
934+
char const* end = begin + doc.size();
935+
// Note that we do not actually need a null-terminator.
936+
CharReaderPtr const reader(fact.newCharReader());
937+
return reader->parse(begin, end, root, errs);
938+
}
939+
885940
std::istream& operator>>(std::istream& sin, Value& root) {
886-
Json::Reader reader;
887-
bool ok = reader.parse(sin, root, true);
941+
CharReaderBuilder b;
942+
std::string errs;
943+
bool ok = parseFromStream(b, sin, &root, &errs);
888944
if (!ok) {
889945
fprintf(stderr,
890946
"Error from reader: %s",
891-
reader.getFormattedErrorMessages().c_str());
947+
errs.c_str());
892948

893949
JSON_FAIL_MESSAGE("reader error");
894950
}

src/test_lib_json/main.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <json/config.h>
88
#include <json/json.h>
99
#include <stdexcept>
10+
#include <cstring>
1011

1112
// Make numeric limits more convenient to talk about.
1213
// Assumes int type in 32 bits.
@@ -1617,6 +1618,90 @@ JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) {
16171618
JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string");
16181619
}
16191620

1621+
struct CharReaderTest : JsonTest::TestCase {};
1622+
1623+
JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrors) {
1624+
Json::CharReaderBuilder b;
1625+
Json::CharReader* reader(b.newCharReader());
1626+
std::string errs;
1627+
Json::Value root;
1628+
char const doc[] = "{ \"property\" : \"value\" }";
1629+
bool ok = reader->parse(
1630+
doc, doc + std::strlen(doc),
1631+
&root, &errs);
1632+
JSONTEST_ASSERT(ok);
1633+
JSONTEST_ASSERT(errs.size() == 0);
1634+
delete reader;
1635+
}
1636+
1637+
JSONTEST_FIXTURE(CharReaderTest, parseWithNoErrorsTestingOffsets) {
1638+
Json::CharReaderBuilder b;
1639+
Json::CharReader* reader(b.newCharReader());
1640+
std::string errs;
1641+
Json::Value root;
1642+
char const doc[] =
1643+
"{ \"property\" : [\"value\", \"value2\"], \"obj\" : "
1644+
"{ \"nested\" : 123, \"bool\" : true}, \"null\" : "
1645+
"null, \"false\" : false }";
1646+
bool ok = reader->parse(
1647+
doc, doc + std::strlen(doc),
1648+
&root, &errs);
1649+
JSONTEST_ASSERT(ok);
1650+
JSONTEST_ASSERT(errs.size() == 0);
1651+
delete reader;
1652+
}
1653+
1654+
JSONTEST_FIXTURE(CharReaderTest, parseWithOneError) {
1655+
Json::CharReaderBuilder b;
1656+
Json::CharReader* reader(b.newCharReader());
1657+
std::string errs;
1658+
Json::Value root;
1659+
char const doc[] =
1660+
"{ \"property\" :: \"value\" }";
1661+
bool ok = reader->parse(
1662+
doc, doc + std::strlen(doc),
1663+
&root, &errs);
1664+
JSONTEST_ASSERT(!ok);
1665+
JSONTEST_ASSERT(errs ==
1666+
"* Line 1, Column 15\n Syntax error: value, object or array "
1667+
"expected.\n");
1668+
delete reader;
1669+
}
1670+
1671+
JSONTEST_FIXTURE(CharReaderTest, parseChineseWithOneError) {
1672+
Json::CharReaderBuilder b;
1673+
Json::CharReader* reader(b.newCharReader());
1674+
std::string errs;
1675+
Json::Value root;
1676+
char const doc[] =
1677+
"{ \"pr佐藤erty\" :: \"value\" }";
1678+
bool ok = reader->parse(
1679+
doc, doc + std::strlen(doc),
1680+
&root, &errs);
1681+
JSONTEST_ASSERT(!ok);
1682+
JSONTEST_ASSERT(errs ==
1683+
"* Line 1, Column 19\n Syntax error: value, object or array "
1684+
"expected.\n");
1685+
delete reader;
1686+
}
1687+
1688+
JSONTEST_FIXTURE(CharReaderTest, parseWithDetailError) {
1689+
Json::CharReaderBuilder b;
1690+
Json::CharReader* reader(b.newCharReader());
1691+
std::string errs;
1692+
Json::Value root;
1693+
char const doc[] =
1694+
"{ \"property\" : \"v\\alue\" }";
1695+
bool ok = reader->parse(
1696+
doc, doc + std::strlen(doc),
1697+
&root, &errs);
1698+
JSONTEST_ASSERT(!ok);
1699+
JSONTEST_ASSERT(errs ==
1700+
"* Line 1, Column 16\n Bad escape sequence in string\nSee "
1701+
"Line 1, Column 20 for detail.\n");
1702+
delete reader;
1703+
}
1704+
16201705
int main(int argc, const char* argv[]) {
16211706
JsonTest::Runner runner;
16221707
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
@@ -1647,6 +1732,13 @@ int main(int argc, const char* argv[]) {
16471732
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseChineseWithOneError);
16481733
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithDetailError);
16491734

1735+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithNoErrors);
1736+
JSONTEST_REGISTER_FIXTURE(
1737+
runner, CharReaderTest, parseWithNoErrorsTestingOffsets);
1738+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithOneError);
1739+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseChineseWithOneError);
1740+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError);
1741+
16501742
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
16511743

16521744
return runner.runCommandLine(argc, argv);

0 commit comments

Comments
 (0)