Add archiver example
A simple (de)serialization framework using DOM and SAX API
This commit is contained in:
parent
87d4e07ffd
commit
f2a28ee472
@ -32,6 +32,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_executable(archivertest archiver/archiver.cpp archiver/archivertest.cpp)
|
||||||
|
|
||||||
foreach (example ${EXAMPLES})
|
foreach (example ${EXAMPLES})
|
||||||
add_executable(${example} ${example}/${example}.cpp)
|
add_executable(${example} ${example}/${example}.cpp)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
292
example/archiver/archiver.cpp
Normal file
292
example/archiver/archiver.cpp
Normal file
@ -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
|
139
example/archiver/archiver.h
Normal file
139
example/archiver/archiver.h
Normal file
@ -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__
|
281
example/archiver/archivertest.cpp
Normal file
281
example/archiver/archivertest.cpp
Normal file
@ -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();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user