From 694dbcb328c9110af654c64c74c65e8cf0149d7c Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 9 Feb 2015 15:25:57 -0600 Subject: [PATCH 1/7] update docs, writeString() --- doc/jsoncpp.dox | 44 ++++++++++++++++++------------------ include/json/writer.h | 6 +++-- src/jsontestrunner/main.cpp | 2 +- src/lib_json/json_writer.cpp | 6 ++--- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index acde674..f2c948a 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -53,23 +53,26 @@ preserved. Json::Value root; // 'root' will contain the root value after parsing. 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. 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 'encoding'; 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. loadPlugIn( plugins[index].asString() ); -setIndentLength( root["indent"].get("length", 3).asInt() ); -setIndentUseSpace( root["indent"].get("use_space", true).asBool() ); +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: -root["encoding"] = getCurrentEncoding(); -root["indent"]["length"] = getCurrentIndentLength(); -root["indent"]["use_space"] = getCurrentIndentUseSpace(); +root["encoding"] = foo::getCurrentEncoding(); +root["indent"]["length"] = foo::getCurrentIndentLength(); +root["indent"]["use_space"] = foo::getCurrentIndentUseSpace(); // If you like the defaults, you can insert directly into a stream. std::cout << root; @@ -80,27 +83,24 @@ std::cout << std::endl; \endcode \section _advanced Advanced usage -We are finalizing the new *Builder* API, which will be in versions -`1.4.0` and `0.8.0` when released. Until then, you may continue to -use the old API, include `Writer`, `Reader`, and `Feature`. + +Configure *builders* to create *readers* and *writers*. For +configuration, we use our own `Json::Value` (rather than +standard setters/getters) so that we can add +features without losing binary-compatibility. + \code - -// EXPERIMENTAL -// Or use `writeString()` for convenience, with a specialized builder. +// For convenience, use `writeString()` with a specialized builder. Json::StreamWriterBuilder wbuilder; -builder.indentation_ = "\t"; -std::string document = Json::writeString(root, wbuilder); +wbuilder.settings["indentation"] = "\t"; +std::string document = Json::writeString(wbuilder, root); -// You can also read into a particular sub-value. -std::cin >> root["subtree"]; - -// EXPERIMENTAL -// Here we use a specialized Builder, discard comments, and -// record errors. +// Here, using a specialized Builder, we discard comments and +// record errors as we parse. Json::CharReaderBuilder rbuilder; -rbuilder.collectComments_ = false; +rbuilder.settings["collectComments"] = false; std::string errs; -Json::parseFromStream(rbuilder, std::cin, &root["subtree"], &errs); +bool ok = Json::parseFromStream(rbuilder, std::cin, &root, &errs); \endcode \section _pbuild Build instructions diff --git a/include/json/writer.h b/include/json/writer.h index e81c245..bb14f52 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -69,8 +69,10 @@ public: }; // Factory }; // StreamWriter -/// \brief Write into stringstream, then return string, for convenience. -std::string writeString(Value const& root, StreamWriter::Factory const& factory); +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +std::string writeString(StreamWriter::Factory const& factory, Value const& root); /** \brief Build a StreamWriter implementation. diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index dba943b..1ec1fb6 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -185,7 +185,7 @@ static std::string useBuiltStyledStreamWriter( Json::Value const& root) { Json::StreamWriterBuilder builder; - return writeString(root, builder); + return Json::writeString(builder, root); } static int rewriteValueTree( const std::string& rewritePath, diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 44aa9ec..2ccfcba 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -1005,10 +1005,10 @@ StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter( colonSymbol, nullSymbol, endingLineFeedSymbol); } -std::string writeString(Value const& root, StreamWriter::Factory const& builder) { +std::string writeString(StreamWriter::Factory const& builder, Value const& root) { std::ostringstream sout; - StreamWriterPtr const sw(builder.newStreamWriter(&sout)); - sw->write(root); + StreamWriterPtr const writer(builder.newStreamWriter(&sout)); + writer->write(root); return sout.str(); } From a9e1ab302dbf5a40291d71016bb378510b3e74c0 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 9 Feb 2015 17:22:28 -0600 Subject: [PATCH 2/7] Builder::settings_ We use Json::Value to configure the builders so we can maintain binary-compatibility easily. --- doc/jsoncpp.dox | 4 +-- include/json/reader.h | 38 ++++++++++++++++--------- include/json/writer.h | 53 +++++++++++++++++++++------------- src/lib_json/json_reader.cpp | 43 +++++++++++++++++++++++++--- src/lib_json/json_writer.cpp | 55 ++++++++++++++++++++++++++++++++---- 5 files changed, 149 insertions(+), 44 deletions(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index f2c948a..ed18809 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -92,13 +92,13 @@ features without losing binary-compatibility. \code // For convenience, use `writeString()` with a specialized builder. Json::StreamWriterBuilder wbuilder; -wbuilder.settings["indentation"] = "\t"; +wbuilder.settings_["indentation"] = "\t"; std::string document = Json::writeString(wbuilder, root); // Here, using a specialized Builder, we discard comments and // record errors as we parse. Json::CharReaderBuilder rbuilder; -rbuilder.settings["collectComments"] = false; +rbuilder.settings_["collectComments"] = false; std::string errs; bool ok = Json::parseFromStream(rbuilder, std::cin, &root, &errs); \endcode diff --git a/include/json/reader.h b/include/json/reader.h index d2bd140..2736931 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -270,7 +270,9 @@ public: class Factory { public: - /// \brief Allocate a CharReader via operator new(). + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ virtual CharReader* newCharReader() const = 0; }; // Factory }; // CharReader @@ -283,29 +285,39 @@ Usage: \code using namespace Json; CharReaderBuilder builder; - builder.collectComments_ = false; + builder.settings_["collectComments"] = false; Value value; std::string errs; bool ok = parseFromStream(builder, std::cin, &value, &errs); \endcode */ -class CharReaderBuilder : public CharReader::Factory { +class JSON_API CharReaderBuilder : public CharReader::Factory { public: - /** default: true - * - * It is possible to "allow" comments but still not "collect" them. - */ - bool collectComments_; - /** default: all() - * - * For historical reasons, Features is a separate structure. - */ - Features features_; + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "collectComments": false or true (default=true) + - TODO: other features ... + But don't trust these docs. You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + */ + Json::Value settings_; CharReaderBuilder(); virtual ~CharReaderBuilder(); virtual CharReader* newCharReader() const; + + /** \return true if 'settings' are illegal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + */ + static void setDefaults(Json::Value* settings); }; /** Consume entire stream and use its begin/end. diff --git a/include/json/writer.h b/include/json/writer.h index bb14f52..39f8cdc 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -64,7 +64,10 @@ public: class JSON_API Factory { public: virtual ~Factory(); - /// Do not take ownership of sout, but maintain a reference. + /** \brief Allocate a CharReader via operator new(). + * Do not take ownership of sout, but maintain a reference. + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ virtual StreamWriter* newStreamWriter(std::ostream* sout) const = 0; }; // Factory }; // StreamWriter @@ -77,41 +80,49 @@ std::string writeString(StreamWriter::Factory const& factory, Value const& root) /** \brief Build a StreamWriter implementation. - \deprecated This is experimental and will be altered before the next release. - Usage: \code using namespace Json; Value value = ...; StreamWriterBuilder builder; - builder.cs_ = StreamWriter::CommentStyle::None; - builder.indentation_ = " "; // or whatever you like + builder.settings_["commentStyle"] = "None"; + builder.settings_["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter(&std::cout)); writer->write(value); std::cout << std::endl; // add lf and flush \endcode */ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { public: - // Note: We cannot add data-members to this class without a major version bump. - // So these might as well be completely exposed. - - /** \brief How to write comments. - * Default: All - */ - StreamWriter::CommentStyle::Enum cs_; - /** \brief Write in human-friendly style. - - If "", then skip all indentation and newlines. - In that case, you probably want CommentStyle::None also. - Default: "\t" - */ - std::string indentation_; + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None", "Some", or "All" (default="All") + - "indentation": (default="\t") + But don't trust these docs. You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + */ + Json::Value settings_; StreamWriterBuilder(); virtual ~StreamWriterBuilder(); - /// Do not take ownership of sout, but maintain a reference. + /** Do not take ownership of sout, but maintain a reference. + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ virtual StreamWriter* newStreamWriter(std::ostream* sout) const; + + /** \return true if 'settings' are illegal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + */ + static void setDefaults(Json::Value* settings); }; /** \brief Build a StreamWriter implementation. @@ -126,6 +137,8 @@ public: * w->write(value); * delete w; * \endcode + * + * \deprecated Use StreamWriterBuilder */ class JSON_API OldCompressingStreamWriterBuilder : public StreamWriter::Factory { diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 410e793..82c0a23 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below #define snprintf _snprintf @@ -912,14 +913,48 @@ public: }; CharReaderBuilder::CharReaderBuilder() - : collectComments_(true) - , features_(Features::all()) -{} +{ + setDefaults(&settings_); +} CharReaderBuilder::~CharReaderBuilder() {} CharReader* CharReaderBuilder::newCharReader() const { - return new OldReader(collectComments_, features_); + if (!validate(NULL)) throw std::runtime_error("invalid settings"); + // TODO: Maybe serialize the invalid settings into the exception. + + bool collectComments = settings_["collectComments"].asBool(); + Features features = Features::all(); + // TODO: Fill in features. + return new OldReader(collectComments, features); +} +static void getValidReaderKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + bool valid = true; + std::set valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return valid; +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ + (*settings)["collectComments"] = true; } ////////////////////////////////// diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 2ccfcba..d0eadf5 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -950,23 +951,67 @@ StreamWriter::~StreamWriter() StreamWriter::Factory::~Factory() {} StreamWriterBuilder::StreamWriterBuilder() - : cs_(StreamWriter::CommentStyle::All) - , indentation_("\t") -{} +{ + setDefaults(&settings_); +} StreamWriterBuilder::~StreamWriterBuilder() {} StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { + if (!validate(NULL)) throw std::runtime_error("invalid settings"); + // TODO: Maybe serialize the invalid settings into the exception. + + std::string indentation = settings_["indentation"].asString(); + std::string cs_str = settings_["commentStyle"].asString(); + StreamWriter::CommentStyle::Enum cs = StreamWriter::CommentStyle::All; + if (cs_str == "All") { + cs = StreamWriter::CommentStyle::All; + } else if (cs_str == "None") { + cs = StreamWriter::CommentStyle::None; + } else { + return NULL; + } std::string colonSymbol = " : "; - if (indentation_.empty()) { + if (indentation.empty()) { colonSymbol = ":"; } std::string nullSymbol = "null"; std::string endingLineFeedSymbol = ""; return new BuiltStyledStreamWriter(stream, - indentation_, cs_, + indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol); } +static void getValidWriterKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + bool valid = true; + std::set valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return valid; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; +} + /* // This might become public someday. class StreamWriterBuilderFactory { From 3cf9175bde9bea72bc221a2defef0f45ba91efc0 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 9 Feb 2015 18:16:24 -0600 Subject: [PATCH 3/7] remark defaults via doxygen snippet --- doc/jsoncpp.dox | 18 ++++++++++++++++++ include/json/reader.h | 12 +++++++++++- include/json/writer.h | 2 ++ src/lib_json/json_reader.cpp | 16 ++++++++++++++++ src/lib_json/json_writer.cpp | 2 ++ 5 files changed, 49 insertions(+), 1 deletion(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index ed18809..563590c 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -103,6 +103,22 @@ std::string errs; bool ok = Json::parseFromStream(rbuilder, std::cin, &root, &errs); \endcode +Yes, compile-time configuration-checking would be helpful, +but `Json::Value` lets you +write and read the builder configuration, which is better! In other words, +you can configure your JSON parser using JSON. + +CharReaders and StreamWriters are not thread-safe, but they are re-usable. +\code +Json::CharReaderBuilder rbuilder; +cfg >> rbuilder.settings_; +std::unique_ptr const reader(rbuilder.newCharReader()); +reader->parse(start, stop, &value1, &errs); +// ... +reader->parse(start, stop, &value2, &errs); +// etc. +\endcode + \section _pbuild Build instructions The build instructions are located in the file README.md in the top-directory of the project. @@ -137,5 +153,7 @@ and recognized in your jurisdiction. \author Baptiste Lepilleur (originator) \version \include version +We make strong guarantees about binary-compatibility, consistent with +the Apache versioning scheme. \sa version.h */ diff --git a/include/json/reader.h b/include/json/reader.h index 2736931..eb4ef97 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -296,12 +296,14 @@ public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. + These are case-sensitive. Available settings (case-sensitive): - "collectComments": false or true (default=true) - TODO: other features ... - But don't trust these docs. You can examine 'settings_` yourself + You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any JSON Value. + \sa setDefaults(Json::Value*) */ Json::Value settings_; @@ -316,8 +318,16 @@ public: bool validate(Json::Value* invalid) const; /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode */ static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void strictMode(Json::Value* settings); }; /** Consume entire stream and use its begin/end. diff --git a/include/json/writer.h b/include/json/writer.h index 39f8cdc..2e074c1 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -121,6 +121,8 @@ public: bool validate(Json::Value* invalid) const; /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults */ static void setDefaults(Json::Value* settings); }; diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 82c0a23..4a3ffda 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -952,9 +952,25 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const return valid; } // static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; +//! [CharReaderBuilderStrictMode] +} +// static void CharReaderBuilder::setDefaults(Json::Value* settings) { +//! [CharReaderBuilderDefaults] (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; +//! [CharReaderBuilderDefaults] } ////////////////////////////////// diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index d0eadf5..bc9401b 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -1008,8 +1008,10 @@ bool StreamWriterBuilder::validate(Json::Value* invalid) const // static void StreamWriterBuilder::setDefaults(Json::Value* settings) { + //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; (*settings)["indentation"] = "\t"; + //! [StreamWriterBuilderDefaults] } /* From f757c18ca0525b1951594afee5a60fc94c20d695 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 9 Feb 2015 18:24:56 -0600 Subject: [PATCH 4/7] add all features --- include/json/reader.h | 12 ++++++++---- include/json/writer.h | 10 ++++++---- src/lib_json/json_reader.cpp | 9 ++++++++- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index eb4ef97..c0dd137 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -298,12 +298,16 @@ public: /** Configuration of this builder. These are case-sensitive. Available settings (case-sensitive): - - "collectComments": false or true (default=true) - - TODO: other features ... + - "collectComments": false or true + - "allowComments" + - "strictRoot" + - "allowDroppedNullPlaceholders" + - "allowNumericKeys" + You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any JSON Value. - \sa setDefaults(Json::Value*) + \sa setDefaults() */ Json::Value settings_; @@ -312,7 +316,7 @@ public: virtual CharReader* newCharReader() const; - /** \return true if 'settings' are illegal and consistent; + /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. */ bool validate(Json::Value* invalid) const; diff --git a/include/json/writer.h b/include/json/writer.h index 2e074c1..7dad7f6 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -99,11 +99,13 @@ public: // without a major version bump. /** Configuration of this builder. Available settings (case-sensitive): - - "commentStyle": "None", "Some", or "All" (default="All") - - "indentation": (default="\t") - But don't trust these docs. You can examine 'settings_` yourself + - "commentStyle": "None", "Some", or "All" + - "indentation": "" + + You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any JSON Value. + \sa setDefaults() */ Json::Value settings_; @@ -115,7 +117,7 @@ public: */ virtual StreamWriter* newStreamWriter(std::ostream* sout) const; - /** \return true if 'settings' are illegal and consistent; + /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. */ bool validate(Json::Value* invalid) const; diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 4a3ffda..8038c11 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -925,13 +925,20 @@ CharReader* CharReaderBuilder::newCharReader() const bool collectComments = settings_["collectComments"].asBool(); Features features = Features::all(); - // TODO: Fill in features. + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); return new OldReader(collectComments, features); } static void getValidReaderKeys(std::set* valid_keys) { valid_keys->clear(); valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); } bool CharReaderBuilder::validate(Json::Value* invalid) const { From b56381a636e0a5d13d046fa05693e604f5738f92 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 9 Feb 2015 18:29:11 -0600 Subject: [PATCH 5/7] --- src/lib_json/json_reader.cpp | 1 + src/lib_json/json_writer.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 8038c11..703a5c2 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below #define snprintf _snprintf diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index bc9401b..2b73d79 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include From c41609b9f9e8e298c5e07fee851003288df60f51 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 9 Feb 2015 18:44:53 -0600 Subject: [PATCH 6/7] set output stream in write(), not in builder --- include/json/writer.h | 37 ++++++++-------- src/lib_json/json_writer.cpp | 82 ++++++++++++++---------------------- 2 files changed, 51 insertions(+), 68 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 7dad7f6..c62d080 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -31,15 +31,15 @@ Usage: using namespace Json; void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { std::unique_ptr const writer( - factory.newStreamWriter(&std::cout)); - writer->write(value); + factory.newStreamWriter()); + writer->write(value, &std::cout); std::cout << std::endl; // add lf and flush } \endcode */ class JSON_API StreamWriter { protected: - std::ostream& sout_; // not owned; will not delete + std::ostream* sout_; // not owned; will not delete public: /// Scoped enums are not available until C++11. struct CommentStyle { @@ -51,13 +51,15 @@ public: }; }; - /// Keep a reference, but do not take ownership of `sout`. - StreamWriter(std::ostream* sout); + StreamWriter(); virtual ~StreamWriter(); - /// Write Value into document as configured in sub-class. - /// \return zero on success - /// \throw std::exception possibly, depending on configuration - virtual int write(Value const& root) = 0; + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, std::ostream* sout) = 0; /** \brief A simple abstract factory. */ @@ -65,10 +67,9 @@ public: public: virtual ~Factory(); /** \brief Allocate a CharReader via operator new(). - * Do not take ownership of sout, but maintain a reference. * \throw std::exception if something goes wrong (e.g. invalid settings) */ - virtual StreamWriter* newStreamWriter(std::ostream* sout) const = 0; + virtual StreamWriter* newStreamWriter() const = 0; }; // Factory }; // StreamWriter @@ -88,8 +89,8 @@ Usage: builder.settings_["commentStyle"] = "None"; builder.settings_["indentation"] = " "; // or whatever you like std::unique_ptr writer( - builder.newStreamWriter(&std::cout)); - writer->write(value); + builder.newStreamWriter()); + writer->write(value, &std::cout); std::cout << std::endl; // add lf and flush \endcode */ @@ -112,10 +113,10 @@ public: StreamWriterBuilder(); virtual ~StreamWriterBuilder(); - /** Do not take ownership of sout, but maintain a reference. + /** * \throw std::exception if something goes wrong (e.g. invalid settings) */ - virtual StreamWriter* newStreamWriter(std::ostream* sout) const; + virtual StreamWriter* newStreamWriter() const; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. @@ -137,8 +138,8 @@ public: * \code * OldCompressingStreamWriterBuilder b; * b.dropNullPlaceHolders_ = true; // etc. - * StreamWriter* w = b.newStreamWriter(&std::cout); - * w->write(value); + * StreamWriter* w = b.newStreamWriter(); + * w->write(value, &std::cout); * delete w; * \endcode * @@ -174,7 +175,7 @@ public: , omitEndingLineFeed_(false) , enableYAMLCompatibility_(false) {} - virtual StreamWriter* newStreamWriter(std::ostream*) const; + virtual StreamWriter* newStreamWriter() const; }; /** \brief Abstract class for writers. diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 2b73d79..d00f501 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -679,13 +679,12 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { struct BuiltStyledStreamWriter : public StreamWriter { BuiltStyledStreamWriter( - std::ostream* sout, std::string const& indentation, StreamWriter::CommentStyle::Enum cs, std::string const& colonSymbol, std::string const& nullSymbol, std::string const& endingLineFeedSymbol); - virtual int write(Value const& root); + virtual int write(Value const& root, std::ostream* sout); private: void writeValue(Value const& value); void writeArrayValue(Value const& value); @@ -713,14 +712,12 @@ private: bool indented_ : 1; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( - std::ostream* sout, std::string const& indentation, StreamWriter::CommentStyle::Enum cs, std::string const& colonSymbol, std::string const& nullSymbol, std::string const& endingLineFeedSymbol) - : StreamWriter(sout) - , rightMargin_(74) + : rightMargin_(74) , indentation_(indentation) , cs_(cs) , colonSymbol_(colonSymbol) @@ -730,8 +727,9 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter( , indented_(false) { } -int BuiltStyledStreamWriter::write(Value const& root) +int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) { + sout_ = sout; addChildValues_ = false; indented_ = true; indentString_ = ""; @@ -740,7 +738,8 @@ int BuiltStyledStreamWriter::write(Value const& root) indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); - sout_ << endingLineFeedSymbol_; + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { @@ -779,13 +778,13 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { Value const& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); - sout_ << colonSymbol_; + *sout_ << colonSymbol_; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } - sout_ << ","; + *sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); @@ -821,7 +820,7 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { writeCommentAfterValueOnSameLine(childValue); break; } - sout_ << ","; + *sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); @@ -829,15 +828,15 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { } else // output on a single line { assert(childValues_.size() == size); - sout_ << "["; - if (!indentation_.empty()) sout_ << " "; + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) - sout_ << ", "; - sout_ << childValues_[index]; + *sout_ << ", "; + *sout_ << childValues_[index]; } - if (!indentation_.empty()) sout_ << " "; - sout_ << "]"; + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; } } } @@ -874,7 +873,7 @@ void BuiltStyledStreamWriter::pushValue(std::string const& value) { if (addChildValues_) childValues_.push_back(value); else - sout_ << value; + *sout_ << value; } void BuiltStyledStreamWriter::writeIndent() { @@ -885,13 +884,13 @@ void BuiltStyledStreamWriter::writeIndent() { if (!indentation_.empty()) { // In this case, drop newlines too. - sout_ << '\n' << indentString_; + *sout_ << '\n' << indentString_; } } void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { if (!indented_) writeIndent(); - sout_ << value; + *sout_ << value; indented_ = false; } @@ -911,11 +910,11 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { const std::string& comment = root.getComment(commentBefore); std::string::const_iterator iter = comment.begin(); while (iter != comment.end()) { - sout_ << *iter; + *sout_ << *iter; if (*iter == '\n' && (iter != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would write extra newline - sout_ << indentString_; + *sout_ << indentString_; ++iter; } indented_ = false; @@ -924,11 +923,11 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { if (cs_ == CommentStyle::None) return; if (root.hasComment(commentAfterOnSameLine)) - sout_ << " " + root.getComment(commentAfterOnSameLine); + *sout_ << " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { writeIndent(); - sout_ << root.getComment(commentAfter); + *sout_ << root.getComment(commentAfter); } } @@ -942,8 +941,8 @@ bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { /////////////// // StreamWriter -StreamWriter::StreamWriter(std::ostream* sout) - : sout_(*sout) +StreamWriter::StreamWriter() + : sout_(NULL) { } StreamWriter::~StreamWriter() @@ -957,7 +956,7 @@ StreamWriterBuilder::StreamWriterBuilder() } StreamWriterBuilder::~StreamWriterBuilder() {} -StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const +StreamWriter* StreamWriterBuilder::newStreamWriter() const { if (!validate(NULL)) throw std::runtime_error("invalid settings"); // TODO: Maybe serialize the invalid settings into the exception. @@ -978,7 +977,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const } std::string nullSymbol = "null"; std::string endingLineFeedSymbol = ""; - return new BuiltStyledStreamWriter(stream, + return new BuiltStyledStreamWriter( indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol); } @@ -1015,24 +1014,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings) //! [StreamWriterBuilderDefaults] } -/* -// This might become public someday. -class StreamWriterBuilderFactory { -public: - virtual ~StreamWriterBuilderFactory(); - virtual StreamWriterBuilder* newStreamWriterBuilder() const; -}; -StreamWriterBuilderFactory::~StreamWriterBuilderFactory() -{ -} -StreamWriterBuilder* StreamWriterBuilderFactory::newStreamWriterBuilder() const -{ - return new StreamWriterBuilder; -} -*/ - -StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter( - std::ostream* stream) const +StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter() const { std::string colonSymbol = " : "; if (enableYAMLCompatibility_) { @@ -1048,22 +1030,22 @@ StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter( if (omitEndingLineFeed_) { endingLineFeedSymbol = ""; } - return new BuiltStyledStreamWriter(stream, + return new BuiltStyledStreamWriter( "", StreamWriter::CommentStyle::None, colonSymbol, nullSymbol, endingLineFeedSymbol); } std::string writeString(StreamWriter::Factory const& builder, Value const& root) { std::ostringstream sout; - StreamWriterPtr const writer(builder.newStreamWriter(&sout)); - writer->write(root); + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); return sout.str(); } std::ostream& operator<<(std::ostream& sout, Value const& root) { StreamWriterBuilder builder; - StreamWriterPtr const writer(builder.newStreamWriter(&sout)); - writer->write(root); + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); return sout; } From db75cdf21edd0aecec333703d94d5e775d984040 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 9 Feb 2015 18:48:45 -0600 Subject: [PATCH 7/7] mv CommentStyle to .cpp --- doc/jsoncpp.dox | 4 ++-- include/json/writer.h | 10 ---------- src/lib_json/json_writer.cpp | 22 ++++++++++++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index 563590c..bf29778 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -92,13 +92,13 @@ features without losing binary-compatibility. \code // For convenience, use `writeString()` with a specialized builder. Json::StreamWriterBuilder wbuilder; -wbuilder.settings_["indentation"] = "\t"; +wbuilder.settings_["indentation"] = "\t"; // simple Json::Value std::string document = Json::writeString(wbuilder, root); // Here, using a specialized Builder, we discard comments and // record errors as we parse. Json::CharReaderBuilder rbuilder; -rbuilder.settings_["collectComments"] = false; +rbuilder.settings_["collectComments"] = false; // simple Json::Value std::string errs; bool ok = Json::parseFromStream(rbuilder, std::cin, &root, &errs); \endcode diff --git a/include/json/writer.h b/include/json/writer.h index c62d080..2b081a7 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -41,16 +41,6 @@ class JSON_API StreamWriter { protected: std::ostream* sout_; // not owned; will not delete public: - /// Scoped enums are not available until C++11. - struct CommentStyle { - /// Decide whether to write comments. - enum Enum { - None, ///< Drop all comments. - Most, ///< Recover odd behavior of previous versions (not implemented yet). - All ///< Keep all comments. - }; - }; - StreamWriter(); virtual ~StreamWriter(); /** Write Value into document as configured in sub-class. diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index d00f501..27d5f56 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -676,11 +676,21 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { ////////////////////////// // BuiltStyledStreamWriter +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + struct BuiltStyledStreamWriter : public StreamWriter { BuiltStyledStreamWriter( std::string const& indentation, - StreamWriter::CommentStyle::Enum cs, + CommentStyle::Enum cs, std::string const& colonSymbol, std::string const& nullSymbol, std::string const& endingLineFeedSymbol); @@ -713,7 +723,7 @@ private: }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::string const& indentation, - StreamWriter::CommentStyle::Enum cs, + CommentStyle::Enum cs, std::string const& colonSymbol, std::string const& nullSymbol, std::string const& endingLineFeedSymbol) @@ -963,11 +973,11 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const std::string indentation = settings_["indentation"].asString(); std::string cs_str = settings_["commentStyle"].asString(); - StreamWriter::CommentStyle::Enum cs = StreamWriter::CommentStyle::All; + CommentStyle::Enum cs = CommentStyle::All; if (cs_str == "All") { - cs = StreamWriter::CommentStyle::All; + cs = CommentStyle::All; } else if (cs_str == "None") { - cs = StreamWriter::CommentStyle::None; + cs = CommentStyle::None; } else { return NULL; } @@ -1031,7 +1041,7 @@ StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter() const endingLineFeedSymbol = ""; } return new BuiltStyledStreamWriter( - "", StreamWriter::CommentStyle::None, + "", CommentStyle::None, colonSymbol, nullSymbol, endingLineFeedSymbol); }