From 177b7b8f229e16b1e5bbee1d03f818f57978f902 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 26 Jan 2015 10:35:54 -0600 Subject: [PATCH 1/6] OldCompressingStreamWriterBuilder --- include/json/writer.h | 73 +++++++++++++++++++++++++++++++++++- src/lib_json/json_writer.cpp | 25 +++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 5407906..25bc812 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -11,6 +11,7 @@ #endif // if !defined(JSON_IS_AMALGAMATION) #include #include +#include // Disable warning C4251: : needs to have dll-interface to // be used by... @@ -60,7 +61,7 @@ public: /// Because this Builder is non-virtual, we can safely add /// methods without a major version bump. /// \see http://stackoverflow.com/questions/14875052/pure-virtual-functions-and-binary-compatibility - class Builder { + class JSON_API Builder { StreamWriterBuilder* own_; Builder(Builder const&); // noncopyable void operator=(Builder const&); // noncopyable @@ -98,11 +99,81 @@ public: /// Do not take ownership of sout, but maintain a reference. StreamWriter* newStreamWriter(std::ostream* sout) const; }; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /* Because this is only a trivial API (the Factory pattern), we will + * never need to add virtual methods, so we do not need a concrete wrapper. + * This is better than the Builder above, but not everyone will agree. + */ + + /// Do not take ownership of sout, but maintain a reference. + virtual StreamWriter* newStreamWriter(std::ostream* sout) const = 0; + }; + + /** \brief Extensions of this are used to create a StreamWriter::Factory. + */ + class JSON_API FactoryFactory { + virtual ~FactoryFactory(); + virtual Factory* newFactory() const = 0; + /* This class will seem strange to some developers, but it actually + * simplifies our library maintenance. + */ + }; + }; /// \brief Write into stringstream, then return string, for convenience. std::string writeString(Value const& root, StreamWriter::Builder const& builder); +/** \brief Build a StreamWriter implementation. + * Comments are not written, and most whitespace is omitted. + * In addition, there are some special settings to allow compatibility + * with the old FastWriter. + * Usage: + * \code + * OldCompressingStreamWriterBuilder b; + * b.dropNullPlaceHolders_ = true; // etc. + * StreamWriter* w = b.newStreamWriter(&std::cout); + * w.write(value); + * delete w; + * \endcode + */ +class JSON_API OldCompressingStreamWriterBuilder +{ +public: + // Note: We cannot add data-members to this class without a major version bump. + // So these might as well be completely exposed. + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's Javascript, it makes for smaller output and the + * browser can handle the output just fine. + */ + bool dropNullPlaceholders_; + /** \brief Do not add \n at end of document. + * Normally, we add an extra newline, just because. + */ + bool omitEndingLineFeed_; + /** \brief Add a space after ':'. + * If indentation is non-empty, we surround colon with whitespace, + * e.g. " : " + * This will add back the trailing space when there is no indentation. + * This seems dubious when the entire document is on a single line, + * but we leave this here to repduce the behavior of the old `FastWriter`. + */ + bool enableYAMLCompatibility_; + + OldCompressingStreamWriterBuilder() + : dropNullPlaceholders_(false) + , omitEndingLineFeed_(false) + , enableYAMLCompatibility_(false) + {} + virtual StreamWriter* newStreamWriter(std::ostream*) const; +}; /** \brief Abstract class for writers. * \deprecated Use StreamWriter::Builder. diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 7f542aa..a24b3f5 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -1083,11 +1083,34 @@ StreamWriter::Builder& StreamWriter::Builder::withEnableYAMLCompatibility(bool v own_->setEnableYAMLCompatibility(v); return *this; } -StreamWriter* StreamWriter::Builder::newStreamWriter(std::ostream* sout) const +StreamWriter* StreamWriter::Builder::newStreamWriter( + std::ostream* sout) const { return own_->newStreamWriter(sout); } +StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter( + std::ostream* stream) const +{ + std::string colonSymbol = " : "; + if (enableYAMLCompatibility_) { + colonSymbol = ": "; + } else { + colonSymbol = ":"; + } + std::string nullSymbol = "null"; + if (dropNullPlaceholders_) { + nullSymbol = ""; + } + std::string endingLineFeedSymbol = "\n"; + if (omitEndingLineFeed_) { + endingLineFeedSymbol = ""; + } + return new BuiltStyledStreamWriter(stream, + "", StreamWriter::CommentStyle::None, + colonSymbol, nullSymbol, endingLineFeedSymbol); +} + std::string writeString(Value const& root, StreamWriter::Builder const& builder) { std::ostringstream sout; std::unique_ptr const sw(builder.newStreamWriter(&sout)); From 28a20917b0a3e2cf5189cabdf71c9bd6b3363353 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 26 Jan 2015 10:43:39 -0600 Subject: [PATCH 2/6] Move old FastWriter stuff out of new Builder --- doc/jsoncpp.dox | 3 +++ include/json/writer.h | 20 +-------------- src/lib_json/json_writer.cpp | 50 ++---------------------------------- 3 files changed, 6 insertions(+), 67 deletions(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index 2c19664..2dcdbff 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -94,6 +94,9 @@ writer->write( root ); // If you like the defaults, you can insert directly into a stream. std::cout << root; +// If desired, remember to add a linefeed and flush. +std::cout << std::endl; + // Of course, you can write to `std::ostringstream` if you prefer. Or // use `writeString()` for convenience. std::string document = Json::writeString( root, builder ); diff --git a/include/json/writer.h b/include/json/writer.h index 25bc812..176876e 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -36,7 +36,7 @@ Usage: std::shared_ptr writer( builder.newStreamWriter(&std::cout)); writer->write(value); - std::cout.flush(); + std::cout << std::endl; // add lf and flush \endcode */ class JSON_API StreamWriter { @@ -77,24 +77,6 @@ public: Default: "\t" */ Builder& withIndentation(std::string indentation); - /** \brief Drop the "null" string from the writer's output for nullValues. - * Strictly speaking, this is not valid JSON. But when the output is being - * fed to a browser's Javascript, it makes for smaller output and the - * browser can handle the output just fine. - */ - Builder& withDropNullPlaceholders(bool v); - /** \brief Do not add \n at end of document. - * Normally, we add an extra newline, just because. - */ - Builder& withOmitEndingLineFeed(bool v); - /** \brief Add a space after ':'. - * If indentation is non-empty, we surround colon with whitespace, - * e.g. " : " - * This will add back the trailing space when there is no indentation. - * This seems dubious when the entire document is on a single line, - * but we leave this here to repduce the behavior of the old `FastWriter`. - */ - Builder& withEnableYAMLCompatibility(bool v); /// Do not take ownership of sout, but maintain a reference. StreamWriter* newStreamWriter(std::ostream* sout) const; diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index a24b3f5..11c564b 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -963,25 +963,16 @@ class StreamWriterBuilder { typedef StreamWriter::CommentStyle CommentStyle; CommentStyle cs_; std::string indentation_; - bool dropNullPlaceholders_; - bool omitEndingLineFeed_; - bool enableYAMLCompatibility_; public: StreamWriterBuilder(); virtual ~StreamWriterBuilder(); virtual void setCommentStyle(CommentStyle cs); virtual void setIndentation(std::string indentation); - virtual void setDropNullPlaceholders(bool v); - virtual void setOmitEndingLineFeed(bool v); - virtual void setEnableYAMLCompatibility(bool v); virtual StreamWriter* newStreamWriter(std::ostream* sout) const; }; StreamWriterBuilder::StreamWriterBuilder() : cs_(CommentStyle::All) , indentation_("\t") - , dropNullPlaceholders_(false) - , omitEndingLineFeed_(false) - , enableYAMLCompatibility_(false) { } StreamWriterBuilder::~StreamWriterBuilder() @@ -996,36 +987,14 @@ void StreamWriterBuilder::setIndentation(std::string v) indentation_ = v; if (indentation_.empty()) cs_ = CommentStyle::None; } -void StreamWriterBuilder::setDropNullPlaceholders(bool v) -{ - dropNullPlaceholders_ = v; -} -void StreamWriterBuilder::setOmitEndingLineFeed(bool v) -{ - omitEndingLineFeed_ = v; -} -void StreamWriterBuilder::setEnableYAMLCompatibility(bool v) -{ - enableYAMLCompatibility_ = v; -} StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { std::string colonSymbol = " : "; if (indentation_.empty()) { - if (enableYAMLCompatibility_) { - colonSymbol = ": "; - } else { - colonSymbol = ":"; - } + colonSymbol = ":"; } std::string nullSymbol = "null"; - if (dropNullPlaceholders_) { - nullSymbol = ""; - } - std::string endingLineFeedSymbol = "\n"; - if (omitEndingLineFeed_) { - endingLineFeedSymbol = ""; - } + std::string endingLineFeedSymbol = ""; return new BuiltStyledStreamWriter(stream, indentation_, cs_, colonSymbol, nullSymbol, endingLineFeedSymbol); @@ -1068,21 +1037,6 @@ StreamWriter::Builder& StreamWriter::Builder::withIndentation(std::string v) own_->setIndentation(v); return *this; } -StreamWriter::Builder& StreamWriter::Builder::withDropNullPlaceholders(bool v) -{ - own_->setDropNullPlaceholders(v); - return *this; -} -StreamWriter::Builder& StreamWriter::Builder::withOmitEndingLineFeed(bool v) -{ - own_->setOmitEndingLineFeed(v); - return *this; -} -StreamWriter::Builder& StreamWriter::Builder::withEnableYAMLCompatibility(bool v) -{ - own_->setEnableYAMLCompatibility(v); - return *this; -} StreamWriter* StreamWriter::Builder::newStreamWriter( std::ostream* sout) const { From 6065a1c1424fac66f9d6da25f2de58b19f2350b2 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 26 Jan 2015 11:01:15 -0600 Subject: [PATCH 3/6] make StreamWriterBuilder concrete --- include/json/writer.h | 73 ++++++++++++++---------------------- src/jsontestrunner/main.cpp | 3 +- src/lib_json/json_writer.cpp | 68 +++------------------------------ 3 files changed, 36 insertions(+), 108 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 176876e..db71cd7 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -58,58 +58,43 @@ public: /// \throw std::exception possibly, depending on configuration virtual int write(Value const& root) = 0; - /// Because this Builder is non-virtual, we can safely add - /// methods without a major version bump. - /// \see http://stackoverflow.com/questions/14875052/pure-virtual-functions-and-binary-compatibility - class JSON_API Builder { - StreamWriterBuilder* own_; - Builder(Builder const&); // noncopyable - void operator=(Builder const&); // noncopyable - public: - Builder(); - ~Builder(); // delete underlying StreamWriterBuilder - - Builder& withCommentStyle(CommentStyle cs); /// default: All - /** \brief Write in human-friendly style. - - If "", then skip all indentation, newlines, and comments, - which implies CommentStyle::None. - Default: "\t" - */ - Builder& withIndentation(std::string indentation); - - /// Do not take ownership of sout, but maintain a reference. - StreamWriter* newStreamWriter(std::ostream* sout) const; - }; - /** \brief A simple abstract factory. */ class JSON_API Factory { public: virtual ~Factory(); - /* Because this is only a trivial API (the Factory pattern), we will - * never need to add virtual methods, so we do not need a concrete wrapper. - * This is better than the Builder above, but not everyone will agree. - */ - /// Do not take ownership of sout, but maintain a reference. virtual StreamWriter* newStreamWriter(std::ostream* sout) const = 0; - }; - - /** \brief Extensions of this are used to create a StreamWriter::Factory. - */ - class JSON_API FactoryFactory { - virtual ~FactoryFactory(); - virtual Factory* newFactory() const = 0; - /* This class will seem strange to some developers, but it actually - * simplifies our library maintenance. - */ - }; - -}; + }; // Factory +}; // StreamWriter /// \brief Write into stringstream, then return string, for convenience. -std::string writeString(Value const& root, StreamWriter::Builder const& builder); +std::string writeString(Value const& root, StreamWriter::Factory const& factory); + + +/** \brief Build a StreamWriter implementation. + */ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { + // typedef StreamWriter::CommentStyle CommentStyle; +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 cs_ = StreamWriter::CommentStyle::All; + /** \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_ = "\t"; + + /// Do not take ownership of sout, but maintain a reference. + StreamWriter* newStreamWriter(std::ostream* sout) const; +}; /** \brief Build a StreamWriter implementation. * Comments are not written, and most whitespace is omitted. @@ -124,7 +109,7 @@ std::string writeString(Value const& root, StreamWriter::Builder const& builder) * delete w; * \endcode */ -class JSON_API OldCompressingStreamWriterBuilder +class JSON_API OldCompressingStreamWriterBuilder : public StreamWriter::Factory { public: // Note: We cannot add data-members to this class without a major version bump. diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index 3a2229c..dba943b 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -184,8 +184,7 @@ static std::string useStyledStreamWriter( static std::string useBuiltStyledStreamWriter( Json::Value const& root) { - Json::StreamWriter::Builder builder; - builder.withCommentStyle(Json::StreamWriter::CommentStyle::All); + Json::StreamWriterBuilder builder; return writeString(root, builder); } static int rewriteValueTree( diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 11c564b..b8d1ba8 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -959,34 +959,8 @@ int MyStreamWriter::write(Value const& root) sout_ << root; return 0; } -class StreamWriterBuilder { - typedef StreamWriter::CommentStyle CommentStyle; - CommentStyle cs_; - std::string indentation_; -public: - StreamWriterBuilder(); - virtual ~StreamWriterBuilder(); - virtual void setCommentStyle(CommentStyle cs); - virtual void setIndentation(std::string indentation); - virtual StreamWriter* newStreamWriter(std::ostream* sout) const; -}; -StreamWriterBuilder::StreamWriterBuilder() - : cs_(CommentStyle::All) - , indentation_("\t") -{ -} -StreamWriterBuilder::~StreamWriterBuilder() -{ -} -void StreamWriterBuilder::setCommentStyle(CommentStyle v) -{ - cs_ = v; -} -void StreamWriterBuilder::setIndentation(std::string v) -{ - indentation_ = v; - if (indentation_.empty()) cs_ = CommentStyle::None; -} +StreamWriter::Factory::~Factory() +{} StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { std::string colonSymbol = " : "; @@ -999,7 +973,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const indentation_, cs_, colonSymbol, nullSymbol, endingLineFeedSymbol); } - +/* // This might become public someday. class StreamWriterBuilderFactory { public: @@ -1013,35 +987,7 @@ StreamWriterBuilder* StreamWriterBuilderFactory::newStreamWriterBuilder() const { return new StreamWriterBuilder; } - -StreamWriter::Builder::Builder() - : own_(StreamWriterBuilderFactory().newStreamWriterBuilder()) -{ -} -StreamWriter::Builder::~Builder() -{ - delete own_; -} -StreamWriter::Builder::Builder(Builder const&) - : own_(nullptr) -{abort();} -void StreamWriter::Builder::operator=(Builder const&) -{abort();} -StreamWriter::Builder& StreamWriter::Builder::withCommentStyle(CommentStyle v) -{ - own_->setCommentStyle(v); - return *this; -} -StreamWriter::Builder& StreamWriter::Builder::withIndentation(std::string v) -{ - own_->setIndentation(v); - return *this; -} -StreamWriter* StreamWriter::Builder::newStreamWriter( - std::ostream* sout) const -{ - return own_->newStreamWriter(sout); -} +*/ StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter( std::ostream* stream) const @@ -1065,7 +1011,7 @@ StreamWriter* OldCompressingStreamWriterBuilder::newStreamWriter( colonSymbol, nullSymbol, endingLineFeedSymbol); } -std::string writeString(Value const& root, StreamWriter::Builder const& builder) { +std::string writeString(Value const& root, StreamWriter::Factory const& builder) { std::ostringstream sout; std::unique_ptr const sw(builder.newStreamWriter(&sout)); sw->write(root); @@ -1073,9 +1019,7 @@ std::string writeString(Value const& root, StreamWriter::Builder const& builder) } std::ostream& operator<<(std::ostream& sout, Value const& root) { - StreamWriter::Builder builder; - builder.withCommentStyle(StreamWriter::CommentStyle::All); - builder.withIndentation("\t"); + StreamWriterBuilder builder; std::shared_ptr writer(builder.newStreamWriter(&sout)); writer->write(root); return sout; From 472d29f57b1453802ac6ab59eedcbd421fac0264 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 26 Jan 2015 11:04:03 -0600 Subject: [PATCH 4/6] fix doc --- doc/jsoncpp.dox | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index 2dcdbff..fe06d50 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -81,8 +81,8 @@ root["indent"]["use_space"] = getCurrentIndentUseSpace(); // To write into a steam with minimal memory overhead, // create a Builder for a StreamWriter. -Json::StreamWriter::Builder builder; -builder.withIndentation(" "); // or whatever you like +Json::StreamWriterBuilder builder; +builder.indentation_ = " "; // or whatever you like // Then build a StreamWriter. std::shared_ptr writer( From 999f5912f0f99666dddfcc336f031f24c1a0da2a Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 26 Jan 2015 11:12:53 -0600 Subject: [PATCH 5/6] docs --- include/json/writer.h | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index db71cd7..0dca690 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -23,20 +23,18 @@ namespace Json { class Value; -class StreamWriterBuilder; /** Usage: \code using namespace Json; - Value value; - StreamWriter::Builder builder; - builder.withCommentStyle(StreamWriter::CommentStyle::None); - std::shared_ptr writer( - builder.newStreamWriter(&std::cout)); - writer->write(value); - std::cout << std::endl; // add lf and flush + void writeToStdout(StreamWriter::Builder const& builder, Value const& value) { + std::unique_ptr const writer( + builder.newStreamWriter(&std::cout)); + writer->write(value); + std::cout << std::endl; // add lf and flush + } \endcode */ class JSON_API StreamWriter { @@ -73,9 +71,20 @@ std::string writeString(Value const& root, StreamWriter::Factory const& factory) /** \brief Build a StreamWriter implementation. - */ + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriter::Builder builder; + builder.cs_ = StreamWriter::CommentStyle::None; + std::shared_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 { - // typedef StreamWriter::CommentStyle CommentStyle; public: // Note: We cannot add data-members to this class without a major version bump. // So these might as well be completely exposed. From 7eca3b4e88bce2787587ceb06d44181822221a09 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 26 Jan 2015 11:17:42 -0600 Subject: [PATCH 6/6] gcc-4.6 (Travis CI) does not support --- include/json/writer.h | 6 ++++-- src/lib_json/json_writer.cpp | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 0dca690..be8ea38 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -92,14 +92,16 @@ public: /** \brief How to write comments. * Default: All */ - StreamWriter::CommentStyle cs_ = StreamWriter::CommentStyle::All; + StreamWriter::CommentStyle 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_ = "\t"; + std::string indentation_; + + StreamWriterBuilder(); /// Do not take ownership of sout, but maintain a reference. StreamWriter* newStreamWriter(std::ostream* sout) const; diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index b8d1ba8..6e0a429 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -961,6 +961,10 @@ int MyStreamWriter::write(Value const& root) } StreamWriter::Factory::~Factory() {} +StreamWriterBuilder::StreamWriterBuilder() + : cs_(StreamWriter::CommentStyle::All) + , indentation_("\t") +{} StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { std::string colonSymbol = " : ";