From becdb49d52e48e9e532aad8e5cca4ed8e3b46b75 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 16 Aug 2017 16:54:27 -0400 Subject: [PATCH 1/2] Add StreamWriter for non-buffered JSON file writing. --- Jzon.cpp | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Jzon.h | 41 +++++++++++++- README.md | 31 +++++++++++ 3 files changed, 233 insertions(+), 1 deletion(-) diff --git a/Jzon.cpp b/Jzon.cpp index 56ede79..54b14b9 100644 --- a/Jzon.cpp +++ b/Jzon.cpp @@ -598,6 +598,168 @@ namespace Jzon } + StreamWriter::StreamWriter(std::ostream & stream, const Format & format) + : stream(stream), first(true) + { + this->format = format; + indentationChar = (format.useTabs ? '\t' : ' '); + spacing = (format.spacing ? " " : ""); + newline = (format.newline ? "\n" : spacing); + } + StreamWriter::~StreamWriter() + { + } + void StreamWriter::startObject() + { + if (!isComplete() && streamStack.back() == StreamState::SS_OBJECT) + throw "Object members must be properties"; + if (isComplete() && !first) + throw "Root JSON is complete"; + if (!first) stream << "," << newline; + stream << getIndentation(streamStack.size()) << "{" << newline; + streamStack.push_back(StreamState::SS_OBJECT); + first = true; + } + void StreamWriter::startObject(const std::string & name) + { + if (!isComplete() && streamStack.back() != StreamState::SS_OBJECT) + throw "Cannot create property outside of an object"; + if (isComplete() && !first) + throw "Root JSON is complete"; + if (!first) stream << "," << newline; + stream << getIndentation(streamStack.size()) << "\"" << name << "\": {" << newline; + streamStack.push_back(StreamState::SS_OBJECT); + first = true; + } + void StreamWriter::endObject() + { + if (streamStack.back() != StreamState::SS_OBJECT) + throw "Cannot end object while not in object"; + streamStack.pop_back(); + stream << newline << getIndentation(streamStack.size()) << "}"; + first = false; + } + void StreamWriter::startArray() + { + if (!isComplete() && streamStack.back() == StreamState::SS_OBJECT) + throw "Object members must be properties"; + if (isComplete() && !first) + throw "Root JSON is complete"; + if (!first) stream << "," << newline; + stream << getIndentation(streamStack.size()) << "[" << newline; + streamStack.push_back(StreamState::SS_ARRAY); + first = true; + } + void StreamWriter::startArray(const std::string & name) + { + if (!isComplete() && streamStack.back() != StreamState::SS_OBJECT) + throw "Cannot create property outside of an object"; + if (isComplete() && !first) + throw "Root JSON is complete"; + if (!first) stream << "," << newline; + stream << getIndentation(streamStack.size()) << "\"" << name << "\": [" << newline; + streamStack.push_back(StreamState::SS_ARRAY); + first = true; + } + void StreamWriter::endArray() + { + if (streamStack.back() != StreamState::SS_ARRAY) + throw "Cannot end array while not in array"; + streamStack.pop_back(); + stream << newline << getIndentation(streamStack.size()) << "]"; + first = false; + } + void StreamWriter::addNode(const Node & node) + { + if (!isComplete() && streamStack.back() == StreamState::SS_OBJECT) + throw "Object members must be properties"; + if (isComplete() && !first) + throw "Root JSON is complete"; + if (!first) stream << "," << newline; + stream << getIndentation(streamStack.size()); + writeNode(node, streamStack.size()); + first = false; + } + void StreamWriter::addNode(const std::string & name, const Node & node) + { + if (!isComplete() && streamStack.back() != StreamState::SS_OBJECT) + throw "Cannot create property outside of an object"; + if (isComplete() && !first) + throw "Root JSON is complete"; + if (!first) stream << "," << newline; + stream << getIndentation(streamStack.size()) << "\"" << name << "\": "; + writeNode(node, streamStack.size()); + first = false; + } + bool StreamWriter::isComplete() + { + return streamStack.size() == 0; + } + std::string StreamWriter::getIndentation(unsigned int level) const + { + if (!format.newline) return ""; + else return std::string(format.indentSize * level, indentationChar); + } + void StreamWriter::writeNode(const Node& node, unsigned int level) const + { + switch (node.getType()) + { + case Node::T_INVALID: break; + case Node::T_OBJECT: writeObject(node, level); break; + case Node::T_ARRAY: writeArray(node, level); break; + case Node::T_NULL: // Fallthrough + case Node::T_STRING: // Fallthrough + case Node::T_NUMBER: // Fallthrough + case Node::T_BOOL: writeValue(node); break; + } + } + void StreamWriter::writeObject(const Node& node, unsigned int level) const + { + stream << "{" << newline; + + for (Node::const_iterator it = node.begin(); it != node.end(); ++it) + { + const std::string& name = (*it).first; + const Node& value = (*it).second; + + if (it != node.begin()) + stream << "," << newline; + stream << getIndentation(level + 1) << "\"" << name << "\"" << ":" << spacing; + writeNode(value, level + 1); + } + + stream << newline << getIndentation(level) << "}"; + } + void StreamWriter::writeArray(const Node& node, unsigned int level) const + { + stream << "[" << newline; + + for (Node::const_iterator it = node.begin(); it != node.end(); ++it) + { + const Node& value = (*it).second; + + if (it != node.begin()) + stream << "," << newline; + stream << getIndentation(level + 1); + writeNode(value, level + 1); + } + + stream << newline << getIndentation(level) << "]"; + } + void StreamWriter::writeValue(const Node& node) const + { + if (node.isString()) + { + stream << "\"" << escapeString(node.toString()) << "\""; + } + else + { + stream << node.toString(); + } + } + + + Parser::Parser() { } diff --git a/Jzon.h b/Jzon.h index 901ebed..ca10f4b 100644 --- a/Jzon.h +++ b/Jzon.h @@ -66,7 +66,7 @@ namespace Jzon class iterator : public std::iterator { public: - iterator() : p(0) {} + iterator() : p(0) {} iterator(NamedNode *o) : p(o) {} iterator(const iterator &it) : p(it.p) {} @@ -254,6 +254,45 @@ namespace Jzon const char *spacing; }; + class JZON_API StreamWriter + { + public: + explicit StreamWriter(std::ostream& stream, const Format& format = NoFormat); + ~StreamWriter(); + + void startObject(); + void startObject(const std::string& name); + void endObject(); + void startArray(); + void startArray(const std::string& name); + void endArray(); + void addNode(const Node& node); + void addNode(const std::string& name, const Node& node); + + bool isComplete(); + + private: + enum StreamState + { + SS_OBJECT, + SS_ARRAY, + }; + std::string getIndentation(unsigned int level) const; + void writeNode(const Node& node, unsigned int level) const; + void writeObject(const Node& node, unsigned int level) const; + void writeArray(const Node& node, unsigned int level) const; + void writeValue(const Node& node) const; + + Format format; + char indentationChar; + const char* newline; + const char* spacing; + + std::ostream& stream; + bool first; + std::vector streamStack; + }; + class JZON_API Parser { public: diff --git a/README.md b/README.md index 12d1603..cd4ad0a 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,40 @@ node.add("bool", true); } Jzon::Writer writer; +writer.setFormat(Jzon::StandardFormat); writer.writeStream(node, cout); ``` +```c +Jzon::StreamWriter writer = Jzon::StreamWriter(std::cout, Jzon::StandardFormat); +writer.startObject(); +{ + writer.addNode("name", "value"); + writer.addNode("number", 20); + writer.addNode("anothernumber", 15.3); + writer.addNode("bool", true); + writer.startArray("array"); + { + writer.addNode(1); + writer.addNode("asdf"); + writer.startObject(); + { + writer.addNode("key1", "val1"); + writer.addNode("key2", "val2"); + } + writer.endObject(); + } + writer.endArray(); + writer.startObject("subnode"); + { + writer.addNode("key1", "val1"); + writer.addNode("key2", "val2"); + } + writer.endObject(); +} +writer.endObject(); +``` + #### Result ```json { From 4ff565a2b56bd5e0ce6ae017cec5b664bdbd1c30 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 16 Aug 2017 17:07:58 -0400 Subject: [PATCH 2/2] Use format definition for spacing. --- Jzon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jzon.cpp b/Jzon.cpp index 54b14b9..fd8128b 100644 --- a/Jzon.cpp +++ b/Jzon.cpp @@ -627,7 +627,7 @@ namespace Jzon if (isComplete() && !first) throw "Root JSON is complete"; if (!first) stream << "," << newline; - stream << getIndentation(streamStack.size()) << "\"" << name << "\": {" << newline; + stream << getIndentation(streamStack.size()) << "\"" << name << "\":" << spacing << "{" << newline; streamStack.push_back(StreamState::SS_OBJECT); first = true; } @@ -657,7 +657,7 @@ namespace Jzon if (isComplete() && !first) throw "Root JSON is complete"; if (!first) stream << "," << newline; - stream << getIndentation(streamStack.size()) << "\"" << name << "\": [" << newline; + stream << getIndentation(streamStack.size()) << "\"" << name << "\":" << spacing << "[" << newline; streamStack.push_back(StreamState::SS_ARRAY); first = true; } @@ -687,7 +687,7 @@ namespace Jzon if (isComplete() && !first) throw "Root JSON is complete"; if (!first) stream << "," << newline; - stream << getIndentation(streamStack.size()) << "\"" << name << "\": "; + stream << getIndentation(streamStack.size()) << "\"" << name << "\":" << spacing; writeNode(node, streamStack.size()); first = false; }