From da0fcfbaa2ed1e21c1d582f2d97b0250414f7157 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Thu, 12 Feb 2015 11:18:23 -0600 Subject: [PATCH 1/4] link web docs --- README.md | 42 ++++++++++++++---------------------------- doc/jsoncpp.dox | 1 + 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index cf8b3db..a74693a 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,20 @@ pairs. [json-org]: http://json.org/ -JsonCpp is a C++ library that allows manipulating JSON values, including +[JsonCpp][] is a C++ library that allows manipulating JSON values, including serialization and deserialization to and from strings. It can also preserve existing comment in unserialization/serialization steps, making it a convenient format to store user input files. +[JsonCpp]: http://open-source-parsers.github.io/jsoncpp-docs/doxygen/index.html + ## A note on backward-compatibility -Very soon, we are switching to C++11 only. For older compilers, try the `pre-C++11` branch. +* `1.y.z` is built with C++11. +* `0.8.z` can be used with older compilers. +* Major versions maintain binary-compatibility. Using JsonCpp in your project ----------------------------- - The recommended approach to integrating JsonCpp in your project is to build the amalgamated source (a single `.cpp` file) with your own build system. This ensures consistency of compilation flags and ABI compatibility. See the section @@ -28,13 +31,11 @@ should be included as follow: #include -If JsonCpp was build as a dynamic library on Windows, then your project needs to +If JsonCpp was built as a dynamic library on Windows, then your project needs to define the macro `JSON_DLL`. - -Building and testing with new CMake ------------------------------------ - +Building and testing with CMake +------------------------------- [CMake][] is a C++ Makefiles/Solution generator. It is usually available on most Linux system as package. On Ubuntu: @@ -75,10 +76,8 @@ the `-G` option). By default CMake hides compilation commands. This can be modified by specifying `-DCMAKE_VERBOSE_MAKEFILE=true` when generating makefiles. - Building and testing with SCons ------------------------------- - **Note:** The SCons-based build system is deprecated. Please use CMake; see the section above. @@ -107,14 +106,7 @@ If you are building with Microsoft Visual Studio 2008, you need to set up the environment by running `vcvars32.bat` (e.g. MSVC 2008 command prompt) before running SCons. - -Running the tests manually --------------------------- - -Note that test can be run using SCons using the `check` target: - - scons platform=$PLATFORM check - +# Running the tests manually You need to run tests manually only if you are troubleshooting an issue. In the instructions below, replace `path/to/jsontest` with the path of the @@ -137,20 +129,21 @@ In the instructions below, replace `path/to/jsontest` with the path of the # You can run the tests using valgrind: python rununittests.py --valgrind path/to/test_lib_json +## Running the tests using scons +Note that tests can be run using SCons using the `check` target: + + scons platform=$PLATFORM check Building the documentation -------------------------- - Run the Python script `doxybuild.py` from the top directory: python doxybuild.py --doxygen=$(which doxygen) --open --with-dot See `doxybuild.py --help` for options. - Generating amalgamated source and header ---------------------------------------- - JsonCpp is provided with a script to generate a single header and a single source file to ease inclusion into an existing project. The amalgamated source can be generated at any time by running the following command from the @@ -172,10 +165,8 @@ The amalgamated sources are generated by concatenating JsonCpp source in the correct order and defining the macro `JSON_IS_AMALGAMATION` to prevent inclusion of other headers. - Adding a reader/writer test --------------------------- - To add a test, you need to create two files in test/data: * a `TESTNAME.json` file, that contains the input document in JSON format. @@ -195,10 +186,8 @@ The `TESTNAME.expected` file format is as follows: See the examples `test_complex_01.json` and `test_complex_01.expected` to better understand element paths. - Understanding reader/writer test output --------------------------------------- - When a test is run, output files are generated beside the input test files. Below is a short description of the content of each file: @@ -215,10 +204,7 @@ Below is a short description of the content of each file: * `test_complex_01.process-output`: `jsontest` output, typically useful for understanding parsing errors. - License ------- - See the `LICENSE` file for details. In summary, JsonCpp is licensed under the MIT license, or public domain if desired and recognized in your jurisdiction. - diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index bf29778..86bd24f 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -152,6 +152,7 @@ Basically JsonCpp is licensed under MIT license, or public domain if desired and recognized in your jurisdiction. \author Baptiste Lepilleur (originator) +\author Christopher Dunn (primary maintainer) \version \include version We make strong guarantees about binary-compatibility, consistent with the Apache versioning scheme. From aa13a8ba402dbd430e5d77bd9fced682a47545b2 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 13 Feb 2015 09:37:39 -0600 Subject: [PATCH 2/4] comments/minor typos --- doc/jsoncpp.dox | 16 ++++++++++------ include/json/reader.h | 14 ++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index 86bd24f..639c909 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -56,20 +56,24 @@ std::cin >> root; // You can also read into a particular sub-value. std::cin >> root["subtree"]; -// Get the value of the member of root named 'encoding', return 'UTF-8' if there is no -// such member. +// Get the value of the member of root named 'encoding', +// and return 'UTF-8' if there is no such member. std::string encoding = root.get("encoding", "UTF-8" ).asString(); -// Get the value of the member of root named 'encoding'; return a 'null' value if + +// Get the value of the member of root named 'plug-ins'; return a 'null' value if // there is no such member. const Json::Value plugins = root["plug-ins"]; -for ( int index = 0; index < plugins.size(); ++index ) // Iterates over the sequence elements. + +// Iterate over the sequence elements. +for ( int index = 0; index < plugins.size(); ++index ) loadPlugIn( plugins[index].asString() ); +// Try other datatypes. Some are auto-convertible to others. foo::setIndentLength( root["indent"].get("length", 3).asInt() ); foo::setIndentUseSpace( root["indent"].get("use_space", true).asBool() ); -// Since Json::Value has implicit constructor for all value types, it is not -// necessary to explicitly construct the Json::Value object: +// Since Json::Value has an implicit constructor for all value types, it is not +// necessary to explicitly construct the Json::Value object. root["encoding"] = foo::getCurrentEncoding(); root["indent"]["length"] = foo::getCurrentIndentLength(); root["indent"]["use_space"] = foo::getCurrentIndentUseSpace(); diff --git a/include/json/reader.h b/include/json/reader.h index b5b2e97..255ff8e 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -298,19 +298,21 @@ public: /** Configuration of this builder. These are case-sensitive. Available settings (case-sensitive): - - "collectComments": false or true + - `"collectComments": false or true` - true to collect comment and allow writing them back during serialization, false to discard comments. This parameter is ignored if allowComments is false. - - "allowComments": false or true + - `"allowComments": false or true` - true if comments are allowed. - - "strictRoot": false or true + - `"strictRoot": false or true` - true if root must be either an array or an object value - - "allowDroppedNullPlaceholders": false or true + - `"allowDroppedNullPlaceholders": false or true` - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) - - "allowNumericKeys": false or true + - `"allowNumericKeys": false or true` - true if numeric object keys are allowed. - - "stackLimit": integer + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. - This is a security issue (seg-faults caused by deeply nested JSON), so the default is low. From f4be815c863deef1bc9248edf43ffac25ae80cd6 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Thu, 12 Feb 2015 12:34:23 -0600 Subject: [PATCH 3/4] failIfExtra 1. failing regression tests, from #164 and #107 2. implemented; tests pass 3. allow trailing comments --- include/json/reader.h | 3 + src/lib_json/json_reader.cpp | 11 +++ src/test_lib_json/main.cpp | 126 +++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) diff --git a/include/json/reader.h b/include/json/reader.h index 255ff8e..b59e183 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -315,6 +315,9 @@ public: cause an exception. - This is a security issue (seg-faults caused by deeply nested JSON), so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 103bf67..2e78ff0 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -914,6 +914,7 @@ public: bool strictRoot_; bool allowDroppedNullPlaceholders_; bool allowNumericKeys_; + bool failIfExtra_; int stackLimit_; }; // OurFeatures @@ -1083,6 +1084,12 @@ bool OurReader::parse(const char* beginDoc, bool successful = readValue(); Token token; skipCommentTokens(token); + if (features_.failIfExtra_) { + if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter); if (features_.strictRoot_) { @@ -1870,6 +1877,7 @@ CharReader* CharReaderBuilder::newCharReader() const features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); return new OurCharReader(collectComments, features); } static void getValidReaderKeys(std::set* valid_keys) @@ -1881,6 +1889,7 @@ static void getValidReaderKeys(std::set* valid_keys) valid_keys->insert("allowDroppedNullPlaceholders"); valid_keys->insert("allowNumericKeys"); valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); } bool CharReaderBuilder::validate(Json::Value* invalid) const { @@ -1908,6 +1917,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings) (*settings)["strictRoot"] = true; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; + (*settings)["failIfExtra"] = true; //! [CharReaderBuilderStrictMode] } // static @@ -1920,6 +1930,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; //! [CharReaderBuilderDefaults] } diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 30c80e2..bc8b9ae 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -1741,6 +1741,126 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) { } } +struct CharReaderFailIfExtraTest : JsonTest::TestCase {}; + +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) { + // This is interpretted as a string value followed by a colon. + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + " \"property\" : \"value\" }"; + { + b.settings_["failIfExtra"] = false; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT(errs == ""); + JSONTEST_ASSERT_EQUAL("property", root); + delete reader; + } + { + b.settings_["failIfExtra"] = true; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(!ok); + JSONTEST_ASSERT_STRING_EQUAL(errs, + "* Line 1, Column 13\n" + " Extra non-whitespace after JSON value.\n"); + JSONTEST_ASSERT_EQUAL("property", root); + delete reader; + } + { + b.settings_["failIfExtra"] = false; + b.strictMode(&b.settings_); + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(!ok); + JSONTEST_ASSERT_STRING_EQUAL(errs, + "* Line 1, Column 13\n" + " Extra non-whitespace after JSON value.\n"); + JSONTEST_ASSERT_EQUAL("property", root); + delete reader; + } +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) { + // This is interpretted as an int value followed by a colon. + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + "1:2:3"; + b.settings_["failIfExtra"] = true; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(!ok); + JSONTEST_ASSERT_STRING_EQUAL( + "* Line 1, Column 2\n" + " Extra non-whitespace after JSON value.\n", + errs); + JSONTEST_ASSERT_EQUAL(1, root.asInt()); + delete reader; +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) { + Json::CharReaderBuilder b; + Json::Value root; + { + char const doc[] = + "{ \"property\" : \"value\" } //trailing\n//comment\n"; + b.settings_["failIfExtra"] = true; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT_STRING_EQUAL("", errs); + JSONTEST_ASSERT_EQUAL("value", root["property"]); + delete reader; + } +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) { + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + "[ \"property\" , \"value\" ] //trailing\n//comment\n"; + b.settings_["failIfExtra"] = true; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT_STRING_EQUAL("", errs); + JSONTEST_ASSERT_EQUAL("value", root[1u]); + delete reader; +} +JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) { + Json::CharReaderBuilder b; + Json::Value root; + char const doc[] = + " true /*trailing\ncomment*/"; + b.settings_["failIfExtra"] = true; + Json::CharReader* reader(b.newCharReader()); + std::string errs; + bool ok = reader->parse( + doc, doc + std::strlen(doc), + &root, &errs); + JSONTEST_ASSERT(ok); + JSONTEST_ASSERT_STRING_EQUAL("", errs); + JSONTEST_ASSERT_EQUAL(true, root.asBool()); + delete reader; +} int main(int argc, const char* argv[]) { JsonTest::Runner runner; JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr); @@ -1779,6 +1899,12 @@ int main(int argc, const char* argv[]) { JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError); JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithStackLimit); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue164); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue107); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterObject); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterArray); + JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterBool); + JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders); JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders); From e7233bf0565d09a8b8a5f72c617993866f4ea280 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 13 Feb 2015 10:00:38 -0600 Subject: [PATCH 4/4] 1.4.1 <- 1.4.0 --- include/json/version.h | 4 ++-- version | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/json/version.h b/include/json/version.h index 9b338b8..2cd6bea 100644 --- a/include/json/version.h +++ b/include/json/version.h @@ -4,10 +4,10 @@ #ifndef JSON_VERSION_H_INCLUDED # define JSON_VERSION_H_INCLUDED -# define JSONCPP_VERSION_STRING "1.4.0" +# define JSONCPP_VERSION_STRING "1.4.1" # define JSONCPP_VERSION_MAJOR 1 # define JSONCPP_VERSION_MINOR 4 -# define JSONCPP_VERSION_PATCH 0 +# define JSONCPP_VERSION_PATCH 1 # define JSONCPP_VERSION_QUALIFIER # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) diff --git a/version b/version index 4c0cef3..7bae217 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.4.0 \ No newline at end of file +1.4.1 \ No newline at end of file