Builder::settings_

We use Json::Value to configure the builders so we can maintain
binary-compatibility easily.
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 @@
 \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 @@
 
   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 @@
 \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 @@
   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 @@
 
 /** \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<Json::StreamWriter> 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 @@
  *   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 <istream>
 #include <sstream>
 #include <memory>
+#include <set>
 
 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
 #define snprintf _snprintf
@@ -912,14 +913,48 @@
 };
 
 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<std::string>* 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<std::string> 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 <memory>
 #include <sstream>
 #include <utility>
+#include <set>
 #include <assert.h>
 #include <math.h>
 #include <stdio.h>
@@ -950,23 +951,67 @@
 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<std::string>* 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<std::string> 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 {