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 {