Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/miloyip/rapidjson.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilo Yip <miloyip@gmail.com>2017-12-13 16:53:18 +0300
committerMilo Yip <miloyip@gmail.com>2017-12-13 16:53:18 +0300
commitf2a28ee4720f34efae516c04eccb97e6820644b2 (patch)
tree9200cf527b0ffcd1438c158eb99214eb8a2893df
parent87d4e07ffd0fb2603661243b43bdcb647a070aa0 (diff)
Add archiver examplearchiver_example
A simple (de)serialization framework using DOM and SAX API
-rw-r--r--example/CMakeLists.txt2
-rw-r--r--example/archiver/archiver.cpp292
-rw-r--r--example/archiver/archiver.h139
-rw-r--r--example/archiver/archivertest.cpp281
4 files changed, 714 insertions, 0 deletions
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index e00f77aa..ff541993 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -32,6 +32,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
+add_executable(archivertest archiver/archiver.cpp archiver/archivertest.cpp)
+
foreach (example ${EXAMPLES})
add_executable(${example} ${example}/${example}.cpp)
endforeach()
diff --git a/example/archiver/archiver.cpp b/example/archiver/archiver.cpp
new file mode 100644
index 00000000..59ae4c41
--- /dev/null
+++ b/example/archiver/archiver.cpp
@@ -0,0 +1,292 @@
+#include "archiver.h"
+#include <cassert>
+#include <stack>
+#include "rapidjson/document.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+
+using namespace rapidjson;
+
+struct JsonReaderStackItem {
+ enum State {
+ BeforeStart, //!< An object/array is in the stack but it is not yet called by StartObject()/StartArray().
+ Started, //!< An object/array is called by StartObject()/StartArray().
+ Closed //!< An array is closed after read all element, but before EndArray().
+ };
+
+ JsonReaderStackItem(const Value* value, State state) : value(value), state(state), index() {}
+
+ const Value* value;
+ State state;
+ SizeType index; // For array iteration
+};
+
+typedef std::stack<JsonReaderStackItem> JsonReaderStack;
+
+#define DOCUMENT reinterpret_cast<Document*>(mDocument)
+#define STACK (reinterpret_cast<JsonReaderStack*>(mStack))
+#define TOP (STACK->top())
+#define CURRENT (*TOP.value)
+
+JsonReader::JsonReader(const char* json) : mDocument(), mStack(), mError(false) {
+ mDocument = new Document;
+ DOCUMENT->Parse(json);
+ if (DOCUMENT->HasParseError())
+ mError = true;
+ else {
+ mStack = new JsonReaderStack;
+ STACK->push(JsonReaderStackItem(DOCUMENT, JsonReaderStackItem::BeforeStart));
+ }
+}
+
+JsonReader::~JsonReader() {
+ delete DOCUMENT;
+ delete STACK;
+}
+
+// Archive concept
+JsonReader& JsonReader::StartObject() {
+ if (!mError) {
+ if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::BeforeStart)
+ TOP.state = JsonReaderStackItem::Started;
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::EndObject() {
+ if (!mError) {
+ if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
+ Next();
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::Member(const char* name) {
+ if (!mError) {
+ if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started) {
+ Value::ConstMemberIterator memberItr = CURRENT.FindMember(name);
+ if (memberItr != CURRENT.MemberEnd())
+ STACK->push(JsonReaderStackItem(&memberItr->value, JsonReaderStackItem::BeforeStart));
+ else
+ mError = true;
+ }
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+bool JsonReader::HasMember(const char* name) const {
+ if (!mError && CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
+ return CURRENT.HasMember(name);
+ return false;
+}
+
+JsonReader& JsonReader::StartArray(size_t* size) {
+ if (!mError) {
+ if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::BeforeStart) {
+ TOP.state = JsonReaderStackItem::Started;
+ if (size)
+ *size = CURRENT.Size();
+
+ if (!CURRENT.Empty()) {
+ const Value* value = &CURRENT[TOP.index];
+ STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
+ }
+ else
+ TOP.state = JsonReaderStackItem::Closed;
+ }
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::EndArray() {
+ if (!mError) {
+ if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::Closed)
+ Next();
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::operator&(bool& b) {
+ if (!mError) {
+ if (CURRENT.IsBool()) {
+ b = CURRENT.GetBool();
+ Next();
+ }
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::operator&(unsigned& u) {
+ if (!mError) {
+ if (CURRENT.IsUint()) {
+ u = CURRENT.GetUint();
+ Next();
+ }
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::operator&(int& i) {
+ if (!mError) {
+ if (CURRENT.IsInt()) {
+ i = CURRENT.GetInt();
+ Next();
+ }
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::operator&(double& d) {
+ if (!mError) {
+ if (CURRENT.IsNumber()) {
+ d = CURRENT.GetDouble();
+ Next();
+ }
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::operator&(std::string& s) {
+ if (!mError) {
+ if (CURRENT.IsString()) {
+ s = CURRENT.GetString();
+ Next();
+ }
+ else
+ mError = true;
+ }
+ return *this;
+}
+
+JsonReader& JsonReader::SetNull() {
+ // This function is for JsonWriter only.
+ mError = true;
+ return *this;
+}
+
+void JsonReader::Next() {
+ if (!mError) {
+ assert(!STACK->empty());
+ STACK->pop();
+
+ if (!STACK->empty() && CURRENT.IsArray()) {
+ if (TOP.state == JsonReaderStackItem::Started) { // Otherwise means reading array item pass end
+ if (TOP.index < CURRENT.Size() - 1) {
+ const Value* value = &CURRENT[++TOP.index];
+ STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
+ }
+ else
+ TOP.state = JsonReaderStackItem::Closed;
+ }
+ else
+ mError = true;
+ }
+ }
+}
+
+#undef DOCUMENT
+#undef STACK
+#undef TOP
+#undef CURRENT
+
+////////////////////////////////////////////////////////////////////////////////
+// JsonWriter
+
+#define WRITER reinterpret_cast<PrettyWriter<StringBuffer>*>(mWriter)
+#define STREAM reinterpret_cast<StringBuffer*>(mStream)
+
+JsonWriter::JsonWriter() : mWriter(), mStream() {
+ mStream = new StringBuffer;
+ mWriter = new PrettyWriter<StringBuffer>(*STREAM);
+}
+
+JsonWriter::~JsonWriter() {
+ delete WRITER;
+ delete STREAM;
+}
+
+const char* JsonWriter::GetString() const {
+ return STREAM->GetString();
+}
+
+JsonWriter& JsonWriter::StartObject() {
+ WRITER->StartObject();
+ return *this;
+}
+
+JsonWriter& JsonWriter::EndObject() {
+ WRITER->EndObject();
+ return *this;
+}
+
+JsonWriter& JsonWriter::Member(const char* name) {
+ WRITER->String(name, static_cast<SizeType>(strlen(name)));
+ return *this;
+}
+
+bool JsonWriter::HasMember(const char*) const {
+ // This function is for JsonReader only.
+ assert(false);
+ return false;
+}
+
+JsonWriter& JsonWriter::StartArray(size_t*) {
+ WRITER->StartArray();
+ return *this;
+}
+
+JsonWriter& JsonWriter::EndArray() {
+ WRITER->EndArray();
+ return *this;
+}
+
+JsonWriter& JsonWriter::operator&(bool& b) {
+ WRITER->Bool(b);
+ return *this;
+}
+
+JsonWriter& JsonWriter::operator&(unsigned& u) {
+ WRITER->Uint(u);
+ return *this;
+}
+
+JsonWriter& JsonWriter::operator&(int& i) {
+ WRITER->Int(i);
+ return *this;
+}
+
+JsonWriter& JsonWriter::operator&(double& d) {
+ WRITER->Double(d);
+ return *this;
+}
+
+JsonWriter& JsonWriter::operator&(std::string& s) {
+ WRITER->String(s.c_str(), static_cast<SizeType>(s.size()));
+ return *this;
+}
+
+JsonWriter& JsonWriter::SetNull() {
+ WRITER->Null();
+ return *this;
+}
+
+#undef STREAM
+#undef WRITER
diff --git a/example/archiver/archiver.h b/example/archiver/archiver.h
new file mode 100644
index 00000000..c7e74f0c
--- /dev/null
+++ b/example/archiver/archiver.h
@@ -0,0 +1,139 @@
+#ifndef ARCHIVER_H_
+#define ARCHIVER_H_
+
+#include <cstddef>
+#include <string>
+
+/**
+\class Archiver
+\brief Archiver concept
+
+Archiver can be a reader or writer for serialization or deserialization respectively.
+
+class Archiver {
+public:
+ /// \returns true if the archiver is in normal state. false if it has errors.
+ operator bool() const;
+
+ /// Starts an object
+ Archiver& StartObject();
+
+ /// After calling StartObject(), assign a member with a name
+ Archiver& Member(const char* name);
+
+ /// After calling StartObject(), check if a member presents
+ bool HasMember(const char* name) const;
+
+ /// Ends an object
+ Archiver& EndObject();
+
+ /// Starts an array
+ /// \param size If Archiver::IsReader is true, the size of array is written.
+ Archiver& StartArray(size_t* size = 0);
+
+ /// Ends an array
+ Archiver& EndArray();
+
+ /// Read/Write primitive types.
+ Archiver& operator&(bool& b);
+ Archiver& operator&(unsigned& u);
+ Archiver& operator&(int& i);
+ Archiver& operator&(double& d);
+ Archiver& operator&(std::string& s);
+
+ /// Write primitive types.
+ Archiver& SetNull();
+
+ //! Whether it is a reader.
+ static const bool IsReader;
+
+ //! Whether it is a writer.
+ static const bool IsWriter;
+};
+*/
+
+/// Represents a JSON reader which implements Archiver concept.
+class JsonReader {
+public:
+ /// Constructor.
+ /**
+ \param json A non-const source json string for in-situ parsing.
+ \note in-situ means the source JSON string will be modified after parsing.
+ */
+ JsonReader(const char* json);
+
+ /// Destructor.
+ ~JsonReader();
+
+ // Archive concept
+
+ operator bool() const { return !mError; }
+
+ JsonReader& StartObject();
+ JsonReader& Member(const char* name);
+ bool HasMember(const char* name) const;
+ JsonReader& EndObject();
+
+ JsonReader& StartArray(size_t* size = nullptr);
+ JsonReader& EndArray();
+
+ JsonReader& operator&(bool& b);
+ JsonReader& operator&(unsigned& u);
+ JsonReader& operator&(int& i);
+ JsonReader& operator&(double& d);
+ JsonReader& operator&(std::string& s);
+
+ JsonReader& SetNull();
+
+ static const bool IsReader = true;
+ static const bool IsWriter = !IsReader;
+
+private:
+ void Next();
+
+ // PIMPL
+ void* mDocument; ///< DOM result of parsing.
+ void* mStack; ///< Stack for iterating the DOM
+ bool mError; ///< Whether an error is occured.
+};
+
+class JsonWriter {
+public:
+ /// Constructor.
+ JsonWriter();
+
+ /// Destructor.
+ ~JsonWriter();
+
+ /// Obtains the serialized JSON string.
+ const char* GetString() const;
+
+ // Archive concept
+
+ operator bool() const { return true; }
+
+ JsonWriter& StartObject();
+ JsonWriter& Member(const char* name);
+ bool HasMember(const char* name) const;
+ JsonWriter& EndObject();
+
+ JsonWriter& StartArray(size_t* size = 0);
+ JsonWriter& EndArray();
+
+ JsonWriter& operator&(bool& b);
+ JsonWriter& operator&(unsigned& u);
+ JsonWriter& operator&(int& i);
+ JsonWriter& operator&(double& d);
+ JsonWriter& operator&(std::string& s);
+ JsonWriter& SetNull();
+
+ static const bool IsReader = false;
+ static const bool IsWriter = !IsReader;
+
+private:
+ // PIMPL idiom
+ void* mWriter; ///< JSON writer.
+ void* mStream; ///< Stream buffer.
+};
+
+#endif // ARCHIVER_H__
diff --git a/example/archiver/archivertest.cpp b/example/archiver/archivertest.cpp
new file mode 100644
index 00000000..788db36e
--- /dev/null
+++ b/example/archiver/archivertest.cpp
@@ -0,0 +1,281 @@
+#include "archiver.h"
+#include <iostream>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+// Test1: simple object
+
+struct Student {
+ std::string name;
+ unsigned age;
+ double height;
+ bool canSwim;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Student& s) {
+ ar.StartObject();
+ ar.Member("name") & s.name;
+ ar.Member("age") & s.age;
+ ar.Member("height") & s.height;
+ ar.Member("canSwim") & s.canSwim;
+ return ar.EndObject();
+}
+
+std::ostream& operator<<(std::ostream& os, const Student& s) {
+ return os << s.name << " " << s.age << " " << s.height << " " << s.canSwim;
+}
+
+void test1() {
+ std::string json;
+
+ // Serialize
+ {
+ Student s = { "Lua", 9, 150.5, true };
+
+ JsonWriter writer;
+ writer & s;
+ json = writer.GetString();
+ std::cout << json << std::endl;
+ }
+
+ // Deserialize
+ {
+ Student s;
+ JsonReader reader(json.c_str());
+ reader & s;
+ std::cout << s << std::endl;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Test2: std::vector <=> JSON array
+//
+// You can map a JSON array to other data structures as well
+
+struct Group {
+ std::string groupName;
+ std::vector<Student> students;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Group& g) {
+ ar.StartObject();
+
+ ar.Member("groupName");
+ ar & g.groupName;
+
+ ar.Member("students");
+ size_t studentCount = g.students.size();
+ ar.StartArray(&studentCount);
+ if (ar.IsReader)
+ g.students.resize(studentCount);
+ for (size_t i = 0; i < studentCount; i++)
+ ar & g.students[i];
+ ar.EndArray();
+
+ return ar.EndObject();
+}
+
+std::ostream& operator<<(std::ostream& os, const Group& g) {
+ os << g.groupName << std::endl;
+ for (std::vector<Student>::const_iterator itr = g.students.begin(); itr != g.students.end(); ++itr)
+ os << *itr << std::endl;
+ return os;
+}
+
+void test2() {
+ std::string json;
+
+ // Serialize
+ {
+ Group g;
+ g.groupName = "Rainbow";
+
+ Student s1 = { "Lua", 9, 150.5, true };
+ Student s2 = { "Mio", 7, 120.0, false };
+ g.students.push_back(s1);
+ g.students.push_back(s2);
+
+ JsonWriter writer;
+ writer & g;
+ json = writer.GetString();
+ std::cout << json << std::endl;
+ }
+
+ // Deserialize
+ {
+ Group g;
+ JsonReader reader(json.c_str());
+ reader & g;
+ std::cout << g << std::endl;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Test3: polymorphism & friend
+//
+// Note that friendship is not necessary but make things simpler.
+
+class Shape {
+public:
+ virtual ~Shape() {}
+ virtual const char* GetType() const = 0;
+ virtual void Print(std::ostream& os) const = 0;
+
+protected:
+ Shape() {}
+ Shape(double x, double y) : x_(x), y_(y) {}
+
+ template <typename Archiver>
+ friend Archiver& operator&(Archiver& ar, Shape& s);
+
+ double x_, y_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Shape& s) {
+ ar.Member("x") & s.x_;
+ ar.Member("y") & s.y_;
+ return ar;
+}
+
+class Circle : public Shape {
+public:
+ Circle() {}
+ Circle(double x, double y, double radius) : Shape(x, y), radius_(radius) {}
+ ~Circle() {}
+
+ const char* GetType() const { return "Circle"; }
+
+ void Print(std::ostream& os) const {
+ os << "Circle (" << x_ << ", " << y_ << ")" << " radius = " << radius_;
+ }
+
+private:
+ template <typename Archiver>
+ friend Archiver& operator&(Archiver& ar, Circle& c);
+
+ double radius_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Circle& c) {
+ ar & static_cast<Shape&>(c);
+ ar.Member("radius") & c.radius_;
+ return ar;
+}
+
+class Box : public Shape {
+public:
+ Box() {}
+ Box(double x, double y, double width, double height) : Shape(x, y), width_(width), height_(height) {}
+ ~Box() {}
+
+ const char* GetType() const { return "Box"; }
+
+ void Print(std::ostream& os) const {
+ os << "Box (" << x_ << ", " << y_ << ")" << " width = " << width_ << " height = " << height_;
+ }
+
+private:
+ template <typename Archiver>
+ friend Archiver& operator&(Archiver& ar, Box& b);
+
+ double width_, height_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Box& b) {
+ ar & static_cast<Shape&>(b);
+ ar.Member("width") & b.width_;
+ ar.Member("height") & b.height_;
+ return ar;
+}
+
+class Canvas {
+public:
+ Canvas() {}
+ ~Canvas() { Clear(); }
+
+ void Clear() {
+ for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr)
+ delete *itr;
+ }
+
+ void AddShape(Shape* shape) { shapes_.push_back(shape); }
+
+ void Print(std::ostream& os) {
+ for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr) {
+ (*itr)->Print(os);
+ std::cout << std::endl;
+ }
+ }
+
+private:
+ template <typename Archiver>
+ friend Archiver& operator&(Archiver& ar, Canvas& c);
+
+ std::vector<Shape*> shapes_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Shape*& shape) {
+ std::string type = ar.IsReader ? "" : shape->GetType();
+ ar.StartObject();
+ ar.Member("type") & type;
+ if (type == "Circle") {
+ if (ar.IsReader) shape = new Circle;
+ ar & static_cast<Circle&>(*shape);
+ }
+ else if (type == "Box") {
+ if (ar.IsReader) shape = new Box;
+ ar & static_cast<Box&>(*shape);
+ }
+ return ar.EndObject();
+}
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Canvas& c) {
+ size_t shapeCount = c.shapes_.size();
+ ar.StartArray(&shapeCount);
+ if (ar.IsReader) {
+ c.Clear();
+ c.shapes_.resize(shapeCount);
+ }
+ for (size_t i = 0; i < shapeCount; i++)
+ ar & c.shapes_[i];
+ return ar.EndArray();
+}
+
+void test3() {
+ std::string json;
+
+ // Serialize
+ {
+ Canvas c;
+ c.AddShape(new Circle(1.0, 2.0, 3.0));
+ c.AddShape(new Box(4.0, 5.0, 6.0, 7.0));
+
+ JsonWriter writer;
+ writer & c;
+ json = writer.GetString();
+ std::cout << json << std::endl;
+ }
+
+ // Deserialize
+ {
+ Canvas c;
+ JsonReader reader(json.c_str());
+ reader & c;
+ c.Print(std::cout);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int main() {
+ test1();
+ test2();
+ test3();
+}