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..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(); @@ -152,6 +156,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. diff --git a/include/json/reader.h b/include/json/reader.h index 674f6a4..2e30a7c 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -254,21 +254,26 @@ 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. + - `"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/include/json/version.h b/include/json/version.h index b206d1c..7d0b5aa 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 "0.8.0" +# define JSONCPP_VERSION_STRING "0.8.1" # define JSONCPP_VERSION_MAJOR 0 # define JSONCPP_VERSION_MINOR 8 -# 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/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 964e409..3dd4096 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -826,6 +826,7 @@ public: bool strictRoot_; bool allowDroppedNullPlaceholders_; bool allowNumericKeys_; + bool failIfExtra_; int stackLimit_; }; // OurFeatures @@ -991,6 +992,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_) { @@ -1704,6 +1711,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) @@ -1715,6 +1723,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 { @@ -1742,6 +1751,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings) (*settings)["strictRoot"] = true; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; + (*settings)["failIfExtra"] = true; //! [CharReaderBuilderStrictMode] } // static @@ -1754,6 +1764,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 3010e31..12d59e6 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -1671,6 +1671,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); @@ -1708,6 +1828,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, StreamWriterTest, dropNullPlaceholders); return runner.runCommandLine(argc, argv); diff --git a/version b/version index a3df0a6..6f4eebd 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.8.0 +0.8.1