From 5fbfe3cdb948a32706ad88635563a41a54819f83 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Thu, 22 Jan 2015 14:31:32 -0600 Subject: [PATCH 01/17] StreamWriter --- include/json/writer.h | 29 +++++++++++++++++++ src/lib_json/json_writer.cpp | 54 ++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index cacb10e..4198498 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -23,6 +23,35 @@ namespace Json { class Value; +class JSON_API StreamWriter { +protected: + std::ostream& sout_; // not owned; will not delete +public: + StreamWriter(std::ostream* sout); + 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) const = 0; +}; + +class JSON_API StreamWriterBuilder { +public: + virtual ~StreamWriterBuilder(); + /// Do not delete stream (i.e. not owned), but keep a reference. + virtual StreamWriter* newStreamWriter(std::ostream* stream) const; +}; + +class JSON_API StreamWriterBuilderFactory { +public: + virtual ~StreamWriterBuilderFactory(); + virtual StreamWriterBuilder* newStreamWriterBuilder(); +}; + +/// \brief Write into stringstream, then return string, for convenience. +std::string writeString(Value const& root, StreamWriterBuilder const& builder); + + /** \brief Abstract class for writers. */ class JSON_API Writer { diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index f1e3b58..3f53f94 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -7,13 +7,14 @@ #include #include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include #include #include +#include #include #include -#include -#include -#include #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below #include @@ -670,4 +671,51 @@ std::ostream& operator<<(std::ostream& sout, const Value& root) { return sout; } +StreamWriter::StreamWriter(std::ostream* sout) + : sout_(*sout) +{ +} +StreamWriter::~StreamWriter() +{ +} +struct MyStreamWriter : public StreamWriter { +public: + MyStreamWriter(std::ostream* sout); + virtual ~MyStreamWriter(); + virtual int write(Value const& root) const = 0; +}; +MyStreamWriter::MyStreamWriter(std::ostream* sout) + : StreamWriter(sout) +{ +} +MyStreamWriter::~MyStreamWriter() +{ +} +int MyStreamWriter::write(Value const& root) const +{ + sout_ << root; + return 0; +} +StreamWriterBuilder::~StreamWriterBuilder() +{ +} +StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const +{ + // return new StyledStreamWriter(stream); + return nullptr; +} +StreamWriterBuilderFactory::~StreamWriterBuilderFactory() +{ +} +StreamWriterBuilder* StreamWriterBuilderFactory::newStreamWriterBuilder() +{ + return new StreamWriterBuilder; +} +std::string writeString(Value const& root, StreamWriterBuilder const& builder) { + std::ostringstream sout; + std::unique_ptr const sw(builder.newStreamWriter(&sout)); + sw->write(root); + return sout.str(); +} + } // namespace Json From 489707ff602217cbfa9e6699078a54787c9dd73d Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Thu, 22 Jan 2015 15:25:30 -0600 Subject: [PATCH 02/17] StreamWriter::Builder --- include/json/writer.h | 45 +++++++++++++++++++++++++++--------- src/lib_json/json_writer.cpp | 30 +++++++++++++++++++++++- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 4198498..d5306c9 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -22,30 +22,53 @@ namespace Json { class Value; +class StreamWriterBuilder; + +/** + +Usage: + + using namespace Json; + Value value; + StreamWriterBuilderFactory f; + StreamWriter::Builder builder(&f); + builder.setCommentStyle(StreamWriter::CommentStyle::None); + std::shared_ptr writer(builder.newStreamWriter(&std::cout)); + writer.write(value); +*/ +class JSON_API StreamWriterBuilderFactory { +public: + virtual ~StreamWriterBuilderFactory(); + virtual StreamWriterBuilder* newStreamWriterBuilder() const; +}; class JSON_API StreamWriter { protected: std::ostream& sout_; // not owned; will not delete public: + enum class CommentStyle {None, Some, All}; + StreamWriter(std::ostream* sout); 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) const = 0; -}; -class JSON_API StreamWriterBuilder { -public: - virtual ~StreamWriterBuilder(); - /// Do not delete stream (i.e. not owned), but keep a reference. - virtual StreamWriter* newStreamWriter(std::ostream* stream) const; -}; + /// 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 { + StreamWriterBuilder* own_; + public: + Builder(StreamWriterBuilderFactory const*); + ~Builder(); // delete underlying StreamWriterBuilder -class JSON_API StreamWriterBuilderFactory { -public: - virtual ~StreamWriterBuilderFactory(); - virtual StreamWriterBuilder* newStreamWriterBuilder(); + void setCommentStyle(CommentStyle cs); /// default: All + + /// Do not take ownership of sout, but maintain a reference. + StreamWriter* newStreamWriter(std::ostream* sout); + }; }; /// \brief Write into stringstream, then return string, for convenience. diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 3f53f94..15fcf64 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -696,9 +696,21 @@ int MyStreamWriter::write(Value const& root) const sout_ << root; return 0; } +class StreamWriterBuilder { + typedef StreamWriter::CommentStyle CommentStyle; + CommentStyle cs_; +public: + virtual ~StreamWriterBuilder(); + virtual void setCommentStyle(CommentStyle cs); + virtual StreamWriter* newStreamWriter(std::ostream* sout) const; +}; StreamWriterBuilder::~StreamWriterBuilder() { } +void StreamWriterBuilder::setCommentStyle(CommentStyle cs) +{ + cs_ = cs; +} StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { // return new StyledStreamWriter(stream); @@ -707,10 +719,26 @@ StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const StreamWriterBuilderFactory::~StreamWriterBuilderFactory() { } -StreamWriterBuilder* StreamWriterBuilderFactory::newStreamWriterBuilder() +StreamWriterBuilder* StreamWriterBuilderFactory::newStreamWriterBuilder() const { return new StreamWriterBuilder; } + +StreamWriter::Builder::Builder(StreamWriterBuilderFactory const* f) + : own_(f->newStreamWriterBuilder()) +{ +} +StreamWriter::Builder::~Builder() +{ + delete own_; +} +void StreamWriter::Builder::setCommentStyle(CommentStyle cs) +{ + own_->setCommentStyle(cs); +} + +/// Do not take ownership of sout, but maintain a reference. +StreamWriter* newStreamWriter(std::ostream* sout); std::string writeString(Value const& root, StreamWriterBuilder const& builder) { std::ostringstream sout; std::unique_ptr const sw(builder.newStreamWriter(&sout)); From 4d649402b02b6bac4b419949a5223a720c6e9956 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Thu, 22 Jan 2015 16:08:21 -0600 Subject: [PATCH 03/17] setIndentation() --- include/json/writer.h | 7 +++++++ src/lib_json/json_writer.cpp | 23 +++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index d5306c9..2aff642 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -65,6 +65,13 @@ public: ~Builder(); // delete underlying StreamWriterBuilder void setCommentStyle(CommentStyle cs); /// default: All + /** \brief Write in human-friendly style. + + If "", then skip all indentation, newlines, and comments, + which implies CommentStyle::None. + Default: "\t" + */ + void setIndentation(std::string indentation); /// Do not take ownership of sout, but maintain a reference. StreamWriter* newStreamWriter(std::ostream* sout); diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 15fcf64..14fef06 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -699,17 +699,24 @@ int MyStreamWriter::write(Value const& root) const class StreamWriterBuilder { typedef StreamWriter::CommentStyle CommentStyle; CommentStyle cs_; + std::string indentation_; public: virtual ~StreamWriterBuilder(); virtual void setCommentStyle(CommentStyle cs); + virtual void setIndentation(std::string indentation); virtual StreamWriter* newStreamWriter(std::ostream* sout) const; }; StreamWriterBuilder::~StreamWriterBuilder() { } -void StreamWriterBuilder::setCommentStyle(CommentStyle cs) +void StreamWriterBuilder::setCommentStyle(CommentStyle v) { - cs_ = cs; + cs_ = v; +} +void StreamWriterBuilder::setIndentation(std::string v) +{ + indentation_ = v; + if (indentation_.empty()) cs_ = CommentStyle::None; } StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { @@ -732,9 +739,17 @@ StreamWriter::Builder::~Builder() { delete own_; } -void StreamWriter::Builder::setCommentStyle(CommentStyle cs) +void StreamWriter::Builder::setCommentStyle(CommentStyle v) { - own_->setCommentStyle(cs); + own_->setCommentStyle(v); +} +void StreamWriter::Builder::setIndentation(std::string v) +{ + own_->setIndentation(v); +} +StreamWriter* StreamWriter::Builder::newStreamWriter(std::ostream* sout) +{ + return own_->newStreamWriter(sout); } /// Do not take ownership of sout, but maintain a reference. From d49ab5aee194781ae343e7a701bc1b8f20f62b4a Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Thu, 22 Jan 2015 16:10:10 -0600 Subject: [PATCH 04/17] use new BuiltStyledStreamWriter in operator<<() --- src/lib_json/json_writer.cpp | 39 +++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 14fef06..e69224c 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -665,12 +665,29 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { value.hasComment(commentAfter); } -std::ostream& operator<<(std::ostream& sout, const Value& root) { - Json::StyledStreamWriter writer; - writer.write(sout, root); - return sout; -} +struct BuiltStyledStreamWriter : public StreamWriter +{ + mutable StyledStreamWriter old_; + BuiltStyledStreamWriter( + std::ostream* sout, + std::string const& indentation, + StreamWriter::CommentStyle cs); + virtual int write(Value const& root) const; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + std::ostream* sout, + std::string const& indentation, + StreamWriter::CommentStyle cs) + : StreamWriter(sout) + , old_(indentation) +{ +} +int BuiltStyledStreamWriter::write(Value const& root) const +{ + old_.write(sout_, root); + return 0; +} StreamWriter::StreamWriter(std::ostream* sout) : sout_(*sout) { @@ -720,8 +737,7 @@ void StreamWriterBuilder::setIndentation(std::string v) } StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { - // return new StyledStreamWriter(stream); - return nullptr; + return new BuiltStyledStreamWriter(stream, indentation_, cs_); } StreamWriterBuilderFactory::~StreamWriterBuilderFactory() { @@ -761,4 +777,13 @@ std::string writeString(Value const& root, StreamWriterBuilder const& builder) { return sout.str(); } +std::ostream& operator<<(std::ostream& sout, const Value& root) { + StreamWriterBuilderFactory f; + StreamWriter::Builder builder(&f); + builder.setCommentStyle(StreamWriter::CommentStyle::Some); + std::shared_ptr writer(builder.newStreamWriter(&sout)); + writer->write(root); + return sout; +} + } // namespace Json From 77ce057f14f0dbe19d4fa7b90ae38d4a5b934db1 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 23 Jan 2015 07:11:06 -0600 Subject: [PATCH 05/17] fix comment --- include/json/writer.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 2aff642..bef756c 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -33,8 +33,10 @@ Usage: StreamWriterBuilderFactory f; StreamWriter::Builder builder(&f); builder.setCommentStyle(StreamWriter::CommentStyle::None); - std::shared_ptr writer(builder.newStreamWriter(&std::cout)); - writer.write(value); + std::shared_ptr writer( + builder.newStreamWriter(&std::cout)); + writer->write(value); + std::cout.flush(); */ class JSON_API StreamWriterBuilderFactory { public: From ceef7f52198ee524b0e3d7beb931821b5548d66a Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 23 Jan 2015 07:33:01 -0600 Subject: [PATCH 06/17] copied impl of StyledStreamWriter --- src/lib_json/json_writer.cpp | 219 ++++++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 4 deletions(-) diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index e69224c..8bc75ed 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -667,27 +667,238 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { struct BuiltStyledStreamWriter : public StreamWriter { - mutable StyledStreamWriter old_; - BuiltStyledStreamWriter( std::ostream* sout, std::string const& indentation, StreamWriter::CommentStyle cs); virtual int write(Value const& root) const; +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::ostream* sout, std::string const& indentation, StreamWriter::CommentStyle cs) : StreamWriter(sout) - , old_(indentation) + , indentation_(indentation) + , rightMargin_(74) { } int BuiltStyledStreamWriter::write(Value const& root) const { - old_.write(sout_, root); + write(sout_, root); return 0; } +void BuiltStyledStreamWriter::write(std::ostream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void BuiltStyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + pushValue(valueToQuotedString(value.asCString())); + break; + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + +void BuiltStyledStreamWriter::writeWithIndent(const std::string& value) { + writeIndent(); + *document_ << value; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + *document_ << root.getComment(commentBefore); + *document_ << "\n"; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + *document_ << "\n"; + *document_ << root.getComment(commentAfter); + *document_ << "\n"; + } +} + +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + StreamWriter::StreamWriter(std::ostream* sout) : sout_(*sout) { From beb6f35c63e3ffedc049b105f7a599913c9a0ffa Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 23 Jan 2015 07:51:40 -0600 Subject: [PATCH 07/17] non-const write --- include/json/writer.h | 2 +- src/lib_json/json_writer.cpp | 101 +++++++++++++++++------------------ 2 files changed, 49 insertions(+), 54 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index bef756c..e4a761c 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -55,7 +55,7 @@ public: /// 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) const = 0; + virtual int write(Value const& root) = 0; /// Because this Builder is non-virtual, we can safely add /// methods without a major version bump. diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 8bc75ed..0518999 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -671,28 +671,28 @@ struct BuiltStyledStreamWriter : public StreamWriter std::ostream* sout, std::string const& indentation, StreamWriter::CommentStyle cs); - virtual int write(Value const& root) const; + virtual int write(Value const& root); private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); - void pushValue(const std::string& value); + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(std::string const& value); void writeIndent(); - void writeWithIndent(const std::string& value); + void writeWithIndent(std::string const& value); void indent(); void unindent(); - void writeCommentBeforeValue(const Value& root); - void writeCommentAfterValueOnSameLine(const Value& root); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); bool hasCommentForValue(const Value& value); - static std::string normalizeEOL(const std::string& text); + static std::string normalizeEOL(std::string const& text); typedef std::vector ChildValues; ChildValues childValues_; - std::ostream* document_; std::string indentString_; int rightMargin_; std::string indentation_; + CommentStyle cs_; bool addChildValues_; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( @@ -700,27 +700,22 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::string const& indentation, StreamWriter::CommentStyle cs) : StreamWriter(sout) - , indentation_(indentation) , rightMargin_(74) + , indentation_(indentation) + , cs_(cs) { } -int BuiltStyledStreamWriter::write(Value const& root) const +int BuiltStyledStreamWriter::write(Value const& root) { - write(sout_, root); - return 0; -} -void BuiltStyledStreamWriter::write(std::ostream& out, const Value& root) { - document_ = &out; addChildValues_ = false; indentString_ = ""; writeCommentBeforeValue(root); writeValue(root); writeCommentAfterValueOnSameLine(root); - *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. + sout_ << "\n"; + return 0; } - -void BuiltStyledStreamWriter::writeValue(const Value& value) { +void BuiltStyledStreamWriter::writeValue(Value const& value) { switch (value.type()) { case nullValue: pushValue("null"); @@ -752,17 +747,17 @@ void BuiltStyledStreamWriter::writeValue(const Value& value) { indent(); Value::Members::iterator it = members.begin(); for (;;) { - const std::string& name = *it; - const Value& childValue = value[name]; + std::string const& name = *it; + Value const& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); - *document_ << " : "; + sout_ << " : "; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } - *document_ << ","; + sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); @@ -772,7 +767,7 @@ void BuiltStyledStreamWriter::writeValue(const Value& value) { } } -void BuiltStyledStreamWriter::writeArrayValue(const Value& value) { +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); @@ -784,7 +779,7 @@ void BuiltStyledStreamWriter::writeArrayValue(const Value& value) { bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { - const Value& childValue = value[index]; + Value const& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); @@ -796,7 +791,7 @@ void BuiltStyledStreamWriter::writeArrayValue(const Value& value) { writeCommentAfterValueOnSameLine(childValue); break; } - *document_ << ","; + sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); @@ -804,23 +799,23 @@ void BuiltStyledStreamWriter::writeArrayValue(const Value& value) { } else // output on a single line { assert(childValues_.size() == size); - *document_ << "[ "; + sout_ << "[ "; for (unsigned index = 0; index < size; ++index) { if (index > 0) - *document_ << ", "; - *document_ << childValues_[index]; + sout_ << ", "; + sout_ << childValues_[index]; } - *document_ << " ]"; + sout_ << " ]"; } } } -bool BuiltStyledStreamWriter::isMultineArray(const Value& value) { +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (int index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; + Value const& childValue = value[index]; isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); @@ -840,32 +835,32 @@ bool BuiltStyledStreamWriter::isMultineArray(const Value& value) { return isMultiLine; } -void BuiltStyledStreamWriter::pushValue(const std::string& value) { +void BuiltStyledStreamWriter::pushValue(std::string const& value) { if (addChildValues_) childValues_.push_back(value); else - *document_ << value; + sout_ << value; } void BuiltStyledStreamWriter::writeIndent() { /* Some comments in this method would have been nice. ;-) - if ( !document_.empty() ) + if ( !sout_.empty() ) { - char last = document_[document_.length()-1]; + char last = sout_[sout_.length()-1]; if ( last == ' ' ) // already indented return; if ( last != '\n' ) // Comments may add new-line - *document_ << '\n'; + sout_ << '\n'; } */ - *document_ << '\n' << indentString_; + sout_ << '\n' << indentString_; } -void BuiltStyledStreamWriter::writeWithIndent(const std::string& value) { +void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { writeIndent(); - *document_ << value; + sout_ << value; } void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } @@ -875,21 +870,21 @@ void BuiltStyledStreamWriter::unindent() { indentString_.resize(indentString_.size() - indentation_.size()); } -void BuiltStyledStreamWriter::writeCommentBeforeValue(const Value& root) { +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { if (!root.hasComment(commentBefore)) return; - *document_ << root.getComment(commentBefore); - *document_ << "\n"; + sout_ << root.getComment(commentBefore); + sout_ << "\n"; } -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { if (root.hasComment(commentAfterOnSameLine)) - *document_ << " " + root.getComment(commentAfterOnSameLine); + sout_ << " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { - *document_ << "\n"; - *document_ << root.getComment(commentAfter); - *document_ << "\n"; + sout_ << "\n"; + sout_ << root.getComment(commentAfter); + sout_ << "\n"; } } @@ -910,7 +905,7 @@ struct MyStreamWriter : public StreamWriter { public: MyStreamWriter(std::ostream* sout); virtual ~MyStreamWriter(); - virtual int write(Value const& root) const = 0; + virtual int write(Value const& root) = 0; }; MyStreamWriter::MyStreamWriter(std::ostream* sout) : StreamWriter(sout) @@ -919,7 +914,7 @@ MyStreamWriter::MyStreamWriter(std::ostream* sout) MyStreamWriter::~MyStreamWriter() { } -int MyStreamWriter::write(Value const& root) const +int MyStreamWriter::write(Value const& root) { sout_ << root; return 0; @@ -988,7 +983,7 @@ std::string writeString(Value const& root, StreamWriterBuilder const& builder) { return sout.str(); } -std::ostream& operator<<(std::ostream& sout, const Value& root) { +std::ostream& operator<<(std::ostream& sout, Value const& root) { StreamWriterBuilderFactory f; StreamWriter::Builder builder(&f); builder.setCommentStyle(StreamWriter::CommentStyle::Some); From 9243d602fe88b8cfd173dfbd50dfdae2ed310b1a Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 23 Jan 2015 08:38:32 -0600 Subject: [PATCH 08/17] const stuff --- include/json/writer.h | 4 ++-- src/jsontestrunner/main.cpp | 11 ++++++++++- src/lib_json/json_writer.cpp | 10 +++++----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index e4a761c..b207849 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -76,12 +76,12 @@ public: void setIndentation(std::string indentation); /// Do not take ownership of sout, but maintain a reference. - StreamWriter* newStreamWriter(std::ostream* sout); + StreamWriter* newStreamWriter(std::ostream* sout) const; }; }; /// \brief Write into stringstream, then return string, for convenience. -std::string writeString(Value const& root, StreamWriterBuilder const& builder); +std::string writeString(Value const& root, StreamWriter::Builder const& builder); /** \brief Abstract class for writers. diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index f6f12b8..61411b4 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -151,7 +151,6 @@ static int parseAndSaveValueTree(const std::string& input, reader.getFormattedErrorMessages().c_str()); return 1; } - if (!parseOnly) { FILE* factual = fopen(actual.c_str(), "wt"); if (!factual) { @@ -182,6 +181,14 @@ static std::string useStyledStreamWriter( writer.write(sout, root); return sout.str(); } +static std::string useBuiltStyledStreamWriter( + Json::Value const& root) +{ + Json::StreamWriterBuilderFactory f; + Json::StreamWriter::Builder builder(&f); + builder.setCommentStyle(Json::StreamWriter::CommentStyle::All); + return writeString(root, builder); +} static int rewriteValueTree( const std::string& rewritePath, const Json::Value& root, @@ -248,6 +255,8 @@ static int parseCommandLine( opts->write = &useStyledWriter; } else if (writerName == "StyledStreamWriter") { opts->write = &useStyledStreamWriter; + } else if (writerName == "BuiltStyledStreamWriter") { + opts->write = &useBuiltStyledStreamWriter; } else { printf("Unknown '--json-writer %s'\n", writerName.c_str()); return 4; diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 0518999..27ff6e3 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -772,8 +772,8 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { if (size == 0) pushValue("[]"); else { - bool isArrayMultiLine = isMultineArray(value); - if (isArrayMultiLine) { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { writeWithIndent("["); indent(); bool hasChildValue = !childValues_.empty(); @@ -969,14 +969,14 @@ void StreamWriter::Builder::setIndentation(std::string v) { own_->setIndentation(v); } -StreamWriter* StreamWriter::Builder::newStreamWriter(std::ostream* sout) +StreamWriter* StreamWriter::Builder::newStreamWriter(std::ostream* sout) const { return own_->newStreamWriter(sout); } /// Do not take ownership of sout, but maintain a reference. StreamWriter* newStreamWriter(std::ostream* sout); -std::string writeString(Value const& root, StreamWriterBuilder const& builder) { +std::string writeString(Value const& root, StreamWriter::Builder const& builder) { std::ostringstream sout; std::unique_ptr const sw(builder.newStreamWriter(&sout)); sw->write(root); @@ -986,7 +986,7 @@ std::string writeString(Value const& root, StreamWriterBuilder const& builder) { std::ostream& operator<<(std::ostream& sout, Value const& root) { StreamWriterBuilderFactory f; StreamWriter::Builder builder(&f); - builder.setCommentStyle(StreamWriter::CommentStyle::Some); + builder.setCommentStyle(StreamWriter::CommentStyle::All); std::shared_ptr writer(builder.newStreamWriter(&sout)); writer->write(root); return sout; From 9e4bcf354f0a77128eeef278ba566f46a9c259cb Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 23 Jan 2015 14:39:57 -0600 Subject: [PATCH 09/17] test BuiltStyledStreamWriter too --- test/runjsontests.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/test/runjsontests.py b/test/runjsontests.py index 728d415..597bf2f 100644 --- a/test/runjsontests.py +++ b/test/runjsontests.py @@ -147,16 +147,23 @@ def main(): else: input_path = None status = runAllTests(jsontest_executable_path, input_path, - use_valgrind=options.valgrind, - with_json_checker=options.with_json_checker, - writerClass='StyledWriter') + use_valgrind=options.valgrind, + with_json_checker=options.with_json_checker, + writerClass='StyledWriter') if status: sys.exit(status) status = runAllTests(jsontest_executable_path, input_path, - use_valgrind=options.valgrind, - with_json_checker=options.with_json_checker, - writerClass='StyledStreamWriter') - sys.exit(status) + use_valgrind=options.valgrind, + with_json_checker=options.with_json_checker, + writerClass='StyledStreamWriter') + if status: + sys.exit(status) + status = runAllTests(jsontest_executable_path, input_path, + use_valgrind=options.valgrind, + with_json_checker=options.with_json_checker, + writerClass='BuiltStyledStreamWriter') + if status: + sys.exit(status) if __name__ == '__main__': main() From 94665eab72c482c5622d8c9d89af62fc1e072f96 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Fri, 23 Jan 2015 14:49:17 -0600 Subject: [PATCH 10/17] copy fixes from StyledStreamWriter --- src/lib_json/json_writer.cpp | 58 +++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 27ff6e3..b6127d3 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -665,6 +665,9 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { value.hasComment(commentAfter); } +////////////////////////// +// BuiltStyledStreamWriter + struct BuiltStyledStreamWriter : public StreamWriter { BuiltStyledStreamWriter( @@ -683,8 +686,7 @@ private: void unindent(); void writeCommentBeforeValue(Value const& root); void writeCommentAfterValueOnSameLine(Value const& root); - bool hasCommentForValue(const Value& value); - static std::string normalizeEOL(std::string const& text); + static bool hasCommentForValue(const Value& value); typedef std::vector ChildValues; @@ -693,7 +695,8 @@ private: int rightMargin_; std::string indentation_; CommentStyle cs_; - bool addChildValues_; + bool addChildValues_ : 1; + bool indented_ : 1; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::ostream* sout, @@ -703,11 +706,14 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter( , rightMargin_(74) , indentation_(indentation) , cs_(cs) + , addChildValues_(false) + , indented_(false) { } int BuiltStyledStreamWriter::write(Value const& root) { addChildValues_ = false; + indented_ = false; indentString_ = ""; writeCommentBeforeValue(root); writeValue(root); @@ -784,8 +790,10 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - writeIndent(); + if (!indented_) writeIndent(); + indented_ = true; writeValue(childValue); + indented_ = false; } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); @@ -826,6 +834,9 @@ bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { addChildValues_ = true; int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } writeValue(value[index]); lineLength += int(childValues_[index].length()); } @@ -843,24 +854,17 @@ void BuiltStyledStreamWriter::pushValue(std::string const& value) { } void BuiltStyledStreamWriter::writeIndent() { - /* - Some comments in this method would have been nice. ;-) - - if ( !sout_.empty() ) - { - char last = sout_[sout_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - sout_ << '\n'; - } - */ + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. sout_ << '\n' << indentString_; } void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { - writeIndent(); + if (!indented_) writeIndent(); sout_ << value; + indented_ = false; } void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } @@ -873,8 +877,22 @@ void BuiltStyledStreamWriter::unindent() { void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { if (!root.hasComment(commentBefore)) return; - sout_ << root.getComment(commentBefore); + sout_ << "\n"; + writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + sout_ << "\n"; + indented_ = false; } void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { @@ -888,12 +906,16 @@ void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root } } +// static bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); } +/////////////// +// StreamWriter + StreamWriter::StreamWriter(std::ostream* sout) : sout_(*sout) { From fe3979cd8a94c4a23994171a6e48c6905cef05b5 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Sat, 24 Jan 2015 13:54:28 -0600 Subject: [PATCH 11/17] drop StreamWriterBuilderFactory, for now --- include/json/writer.h | 14 +++++--------- src/jsontestrunner/main.cpp | 3 +-- src/lib_json/json_writer.cpp | 19 +++++++++++++++---- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index b207849..6e46cf1 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -30,26 +30,20 @@ Usage: using namespace Json; Value value; - StreamWriterBuilderFactory f; - StreamWriter::Builder builder(&f); + StreamWriter::Builder builder; builder.setCommentStyle(StreamWriter::CommentStyle::None); std::shared_ptr writer( builder.newStreamWriter(&std::cout)); writer->write(value); std::cout.flush(); */ -class JSON_API StreamWriterBuilderFactory { -public: - virtual ~StreamWriterBuilderFactory(); - virtual StreamWriterBuilder* newStreamWriterBuilder() const; -}; - class JSON_API StreamWriter { protected: std::ostream& sout_; // not owned; will not delete public: enum class CommentStyle {None, Some, All}; + /// Keep a reference, but do not take ownership of `sout`. StreamWriter(std::ostream* sout); virtual ~StreamWriter(); /// Write Value into document as configured in sub-class. @@ -62,8 +56,10 @@ public: /// \see http://stackoverflow.com/questions/14875052/pure-virtual-functions-and-binary-compatibility class Builder { StreamWriterBuilder* own_; + Builder(Builder const&); // noncopyable + void operator=(Builder const&); // noncopyable public: - Builder(StreamWriterBuilderFactory const*); + Builder(); ~Builder(); // delete underlying StreamWriterBuilder void setCommentStyle(CommentStyle cs); /// default: All diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index 61411b4..28894ce 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::StreamWriterBuilderFactory f; - Json::StreamWriter::Builder builder(&f); + Json::StreamWriter::Builder builder; builder.setCommentStyle(Json::StreamWriter::CommentStyle::All); return writeString(root, builder); } diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index b6127d3..a3fa50d 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -967,6 +967,13 @@ StreamWriter* StreamWriterBuilder::newStreamWriter(std::ostream* stream) const { return new BuiltStyledStreamWriter(stream, indentation_, cs_); } + +// This might become public someday. +class StreamWriterBuilderFactory { +public: + virtual ~StreamWriterBuilderFactory(); + virtual StreamWriterBuilder* newStreamWriterBuilder() const; +}; StreamWriterBuilderFactory::~StreamWriterBuilderFactory() { } @@ -975,14 +982,19 @@ StreamWriterBuilder* StreamWriterBuilderFactory::newStreamWriterBuilder() const return new StreamWriterBuilder; } -StreamWriter::Builder::Builder(StreamWriterBuilderFactory const* f) - : own_(f->newStreamWriterBuilder()) +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();} void StreamWriter::Builder::setCommentStyle(CommentStyle v) { own_->setCommentStyle(v); @@ -1006,8 +1018,7 @@ std::string writeString(Value const& root, StreamWriter::Builder const& builder) } std::ostream& operator<<(std::ostream& sout, Value const& root) { - StreamWriterBuilderFactory f; - StreamWriter::Builder builder(&f); + StreamWriter::Builder builder; builder.setCommentStyle(StreamWriter::CommentStyle::All); std::shared_ptr writer(builder.newStreamWriter(&sout)); writer->write(root); From 648843d1482f895f3476f78859a5c10174d2ac40 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Sat, 24 Jan 2015 13:57:29 -0600 Subject: [PATCH 12/17] clarify CommentStyle --- include/json/writer.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/json/writer.h b/include/json/writer.h index 6e46cf1..e4d665e 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -41,7 +41,11 @@ class JSON_API StreamWriter { protected: std::ostream& sout_; // not owned; will not delete public: - enum class CommentStyle {None, Some, All}; + /// `All`: Keep all comments. + /// `None`: Drop all comments. + /// Use `Most` to recover the odd behavior of previous versions. + /// Only `All` is currently implemented. + enum class CommentStyle {None, Most, All}; /// Keep a reference, but do not take ownership of `sout`. StreamWriter(std::ostream* sout); From dea6f8d9a63f2219e77d57d86caf6f68dd2482c8 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Sun, 25 Jan 2015 15:46:33 -0600 Subject: [PATCH 13/17] incorporate 'proper newlines for comments' into new StreamWriter --- src/lib_json/json_writer.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index a3fa50d..1c9b7a4 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -713,9 +713,11 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter( int BuiltStyledStreamWriter::write(Value const& root) { addChildValues_ = false; - indented_ = false; + indented_ = true; indentString_ = ""; writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); sout_ << "\n"; @@ -878,20 +880,17 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { if (!root.hasComment(commentBefore)) return; - sout_ << "\n"; - writeIndent(); + if (!indented_) writeIndent(); const std::string& comment = root.getComment(commentBefore); std::string::const_iterator iter = comment.begin(); while (iter != comment.end()) { sout_ << *iter; if (*iter == '\n' && (iter != comment.end() && *(iter + 1) == '/')) - writeIndent(); + // writeIndent(); // would write extra newline + sout_ << indentString_; ++iter; } - - // Comments are stripped of trailing newlines, so add one here - sout_ << "\n"; indented_ = false; } @@ -900,9 +899,8 @@ void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root sout_ << " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { - sout_ << "\n"; + writeIndent(); sout_ << root.getComment(commentAfter); - sout_ << "\n"; } } From 1e21e63853f461d3f35df77c2f1aef26b648161e Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Sun, 25 Jan 2015 16:01:59 -0600 Subject: [PATCH 14/17] default \t indentation, All comments --- src/lib_json/json_writer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 1c9b7a4..eea17b6 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -944,11 +944,17 @@ class StreamWriterBuilder { 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() { } From c6e0688e5a67c17ef03a0d1ee1ee434af72ad766 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Sun, 25 Jan 2015 17:32:11 -0600 Subject: [PATCH 15/17] implement CommentStyle::None/indentation_=="" --- src/lib_json/json_writer.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index eea17b6..43166ac 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -720,7 +720,9 @@ int BuiltStyledStreamWriter::write(Value const& root) indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); - sout_ << "\n"; + if (!indentation_.empty()) { + sout_ << "\n"; + } return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { @@ -759,7 +761,9 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { Value const& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); - sout_ << " : "; + if (!indentation_.empty()) sout_ << " "; + sout_ << ":"; + if (!indentation_.empty()) sout_ << " "; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); @@ -809,13 +813,15 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { } else // output on a single line { assert(childValues_.size() == size); - sout_ << "[ "; + sout_ << "["; + if (!indentation_.empty()) sout_ << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) sout_ << ", "; sout_ << childValues_[index]; } - sout_ << " ]"; + if (!indentation_.empty()) sout_ << " "; + sout_ << "]"; } } } @@ -860,7 +866,11 @@ void BuiltStyledStreamWriter::writeIndent() { // to determine whether we are already indented, but // with a stream we cannot do that. So we rely on some saved state. // The caller checks indented_. - sout_ << '\n' << indentString_; + + if (!indentation_.empty()) { + // In this case, drop newlines too. + sout_ << '\n' << indentString_; + } } void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { @@ -877,6 +887,7 @@ void BuiltStyledStreamWriter::unindent() { } void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; if (!root.hasComment(commentBefore)) return; @@ -895,6 +906,7 @@ 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); From d78caa3851766504d06bfd8ba7766863bcc59a38 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Sun, 25 Jan 2015 18:15:54 -0600 Subject: [PATCH 16/17] implement strange setting from FastWriter --- include/json/writer.h | 18 ++++++++ src/lib_json/json_writer.cpp | 82 ++++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 12 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index e4d665e..763a949 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -74,6 +74,24 @@ public: Default: "\t" */ void setIndentation(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. + */ + void setDropNullPlaceholders(bool v); + /** \brief Do not add \n at end of document. + * Normally, we add an extra newline, just because. + */ + void setOmitEndingLineFeed(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`. + */ + void setEnableYAMLCompatibility(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 43166ac..bf103d2 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -673,7 +673,10 @@ struct BuiltStyledStreamWriter : public StreamWriter BuiltStyledStreamWriter( std::ostream* sout, std::string const& indentation, - StreamWriter::CommentStyle cs); + StreamWriter::CommentStyle cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol); virtual int write(Value const& root); private: void writeValue(Value const& value); @@ -695,17 +698,26 @@ private: int rightMargin_; std::string indentation_; CommentStyle cs_; + std::string colonSymbol_; + std::string nullSymbol_; + std::string endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::ostream* sout, std::string const& indentation, - StreamWriter::CommentStyle cs) + StreamWriter::CommentStyle cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol) : StreamWriter(sout) , rightMargin_(74) , indentation_(indentation) , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) , addChildValues_(false) , indented_(false) { @@ -720,15 +732,13 @@ int BuiltStyledStreamWriter::write(Value const& root) indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); - if (!indentation_.empty()) { - sout_ << "\n"; - } + sout_ << endingLineFeedSymbol_; return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { switch (value.type()) { case nullValue: - pushValue("null"); + pushValue(nullSymbol_); break; case intValue: pushValue(valueToString(value.asLargestInt())); @@ -761,9 +771,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { Value const& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); - if (!indentation_.empty()) sout_ << " "; - sout_ << ":"; - if (!indentation_.empty()) sout_ << " "; + sout_ << colonSymbol_; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); @@ -955,16 +963,25 @@ 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() @@ -979,9 +996,39 @@ 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 { - return new BuiltStyledStreamWriter(stream, indentation_, cs_); + std::string colonSymbol = " : "; + if (indentation_.empty()) { + if (enableYAMLCompatibility_) { + colonSymbol = ": "; + } else { + colonSymbol = ":"; + } + } + std::string nullSymbol = "null"; + if (dropNullPlaceholders_) { + nullSymbol = ""; + } + std::string endingLineFeedSymbol = "\n"; + if (omitEndingLineFeed_) { + endingLineFeedSymbol = ""; + } + return new BuiltStyledStreamWriter(stream, + indentation_, cs_, + colonSymbol, nullSymbol, endingLineFeedSymbol); } // This might become public someday. @@ -1019,13 +1066,23 @@ void StreamWriter::Builder::setIndentation(std::string v) { own_->setIndentation(v); } +void StreamWriter::Builder::setDropNullPlaceholders(bool v) +{ + own_->setDropNullPlaceholders(v); +} +void StreamWriter::Builder::setOmitEndingLineFeed(bool v) +{ + own_->setOmitEndingLineFeed(v); +} +void StreamWriter::Builder::setEnableYAMLCompatibility(bool v) +{ + own_->setEnableYAMLCompatibility(v); +} StreamWriter* StreamWriter::Builder::newStreamWriter(std::ostream* sout) const { return own_->newStreamWriter(sout); } -/// Do not take ownership of sout, but maintain a reference. -StreamWriter* newStreamWriter(std::ostream* sout); std::string writeString(Value const& root, StreamWriter::Builder const& builder) { std::ostringstream sout; std::unique_ptr const sw(builder.newStreamWriter(&sout)); @@ -1036,6 +1093,7 @@ std::string writeString(Value const& root, StreamWriter::Builder const& builder) std::ostream& operator<<(std::ostream& sout, Value const& root) { StreamWriter::Builder builder; builder.setCommentStyle(StreamWriter::CommentStyle::All); + builder.setIndentation("\t"); std::shared_ptr writer(builder.newStreamWriter(&sout)); writer->write(root); return sout; From c7b39c2e2536386a1badc46968e82374e85184ea Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Sun, 25 Jan 2015 18:45:59 -0600 Subject: [PATCH 17/17] deprecate old Writers also, use withers instead of setters, and update docs --- doc/jsoncpp.dox | 25 ++++++++++++++++--------- include/json/writer.h | 16 ++++++++++------ src/jsontestrunner/main.cpp | 2 +- src/lib_json/json_writer.cpp | 19 ++++++++++++------- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/doc/jsoncpp.dox b/doc/jsoncpp.dox index f193719..17d82d5 100644 --- a/doc/jsoncpp.dox +++ b/doc/jsoncpp.dox @@ -73,24 +73,31 @@ for ( int index = 0; index < plugins.size(); ++index ) // Iterates over the seq setIndentLength( root["indent"].get("length", 3).asInt() ); setIndentUseSpace( root["indent"].get("use_space", true).asBool() ); -// ... -// At application shutdown to make the new configuration document: // 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(); -Json::StyledWriter writer; -// Make a new JSON document for the configuration. Preserve original comments. -std::string outputConfig = writer.write( root ); +// 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 -// You can also use streams. This will put the contents of any JSON +// Then build a StreamWriter. +// (Of course, you can write to std::ostringstream if you prefer.) +std::shared_ptr writer( + builder.newStreamWriter( &std::cout ); + +// Make a new JSON document for the configuration. Preserve original comments. +writer->write( root ); + +// If you like the defaults, you can insert directly into a stream. +std::cout << root; + +// You can also read from a stream. This will put the contents of any JSON // stream at a particular sub-value, if you'd like. std::cin >> root["subtree"]; - -// And you can write to a stream, using the StyledWriter automatically. -std::cout << root; \endcode \section _pbuild Build instructions diff --git a/include/json/writer.h b/include/json/writer.h index 763a949..fb824e3 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -31,7 +31,7 @@ Usage: using namespace Json; Value value; StreamWriter::Builder builder; - builder.setCommentStyle(StreamWriter::CommentStyle::None); + builder.withCommentStyle(StreamWriter::CommentStyle::None); std::shared_ptr writer( builder.newStreamWriter(&std::cout)); writer->write(value); @@ -66,24 +66,24 @@ public: Builder(); ~Builder(); // delete underlying StreamWriterBuilder - void setCommentStyle(CommentStyle cs); /// default: All + 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" */ - void setIndentation(std::string indentation); + 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. */ - void setDropNullPlaceholders(bool v); + Builder& withDropNullPlaceholders(bool v); /** \brief Do not add \n at end of document. * Normally, we add an extra newline, just because. */ - void setOmitEndingLineFeed(bool v); + Builder& withOmitEndingLineFeed(bool v); /** \brief Add a space after ':'. * If indentation is non-empty, we surround colon with whitespace, * e.g. " : " @@ -91,7 +91,7 @@ public: * 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`. */ - void setEnableYAMLCompatibility(bool v); + Builder& withEnableYAMLCompatibility(bool v); /// Do not take ownership of sout, but maintain a reference. StreamWriter* newStreamWriter(std::ostream* sout) const; @@ -103,6 +103,7 @@ std::string writeString(Value const& root, StreamWriter::Builder const& builder) /** \brief Abstract class for writers. + * \deprecated Use StreamWriter::Builder. */ class JSON_API Writer { public: @@ -118,6 +119,7 @@ public: *consumption, * but may be usefull to support feature such as RPC where bandwith is limited. * \sa Reader, Value + * \deprecated Use StreamWriter::Builder. */ class JSON_API FastWriter : public Writer { public: @@ -169,6 +171,7 @@ private: *#CommentPlacement. * * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriter::Builder. */ class JSON_API StyledWriter : public Writer { public: @@ -230,6 +233,7 @@ private: * * \param indentation Each level will be indented by this amount extra. * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriter::Builder. */ class JSON_API StyledStreamWriter { public: diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index 28894ce..3a2229c 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -185,7 +185,7 @@ static std::string useBuiltStyledStreamWriter( Json::Value const& root) { Json::StreamWriter::Builder builder; - builder.setCommentStyle(Json::StreamWriter::CommentStyle::All); + builder.withCommentStyle(Json::StreamWriter::CommentStyle::All); return writeString(root, builder); } static int rewriteValueTree( diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index bf103d2..7f542aa 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -1058,25 +1058,30 @@ StreamWriter::Builder::Builder(Builder const&) {abort();} void StreamWriter::Builder::operator=(Builder const&) {abort();} -void StreamWriter::Builder::setCommentStyle(CommentStyle v) +StreamWriter::Builder& StreamWriter::Builder::withCommentStyle(CommentStyle v) { own_->setCommentStyle(v); + return *this; } -void StreamWriter::Builder::setIndentation(std::string v) +StreamWriter::Builder& StreamWriter::Builder::withIndentation(std::string v) { own_->setIndentation(v); + return *this; } -void StreamWriter::Builder::setDropNullPlaceholders(bool v) +StreamWriter::Builder& StreamWriter::Builder::withDropNullPlaceholders(bool v) { own_->setDropNullPlaceholders(v); + return *this; } -void StreamWriter::Builder::setOmitEndingLineFeed(bool v) +StreamWriter::Builder& StreamWriter::Builder::withOmitEndingLineFeed(bool v) { own_->setOmitEndingLineFeed(v); + return *this; } -void StreamWriter::Builder::setEnableYAMLCompatibility(bool v) +StreamWriter::Builder& StreamWriter::Builder::withEnableYAMLCompatibility(bool v) { own_->setEnableYAMLCompatibility(v); + return *this; } StreamWriter* StreamWriter::Builder::newStreamWriter(std::ostream* sout) const { @@ -1092,8 +1097,8 @@ std::string writeString(Value const& root, StreamWriter::Builder const& builder) std::ostream& operator<<(std::ostream& sout, Value const& root) { StreamWriter::Builder builder; - builder.setCommentStyle(StreamWriter::CommentStyle::All); - builder.setIndentation("\t"); + builder.withCommentStyle(StreamWriter::CommentStyle::All); + builder.withIndentation("\t"); std::shared_ptr writer(builder.newStreamWriter(&sout)); writer->write(root); return sout;