1. 程式人生 > >C++物件Json序列化最佳實踐(基於Rapidjson庫):C++記憶體物件和Json字串互相轉換

C++物件Json序列化最佳實踐(基於Rapidjson庫):C++記憶體物件和Json字串互相轉換

介紹:Rapidjson

Rapidjson庫是C++物件序列化到Json字串的非常好的工具,以效率著稱,騰訊的人寫的。

這個庫的缺點(個人拙見):

1 暴露的細節相對較多:容器,迭代器,型別,成員函式,序列化,反序列化,都有非常細緻的操作。這個給使用者帶來記憶負擔較重。至少需要同時暴露Value型別和Document型別才能完整的實現Object內部包含Object型別。但這個包裝起來很麻煩。

2 使用移動語義和自動記憶體回收:這個是記憶體佔用小的保障,但是光是移動語義就給使用者造成很多不便。移動語義當然提高了效率,但是和使用者的程式程式碼風格不能很好的融合。移動語義使得Document的管理的記憶體資料在Document釋放的時候就被析構,Object內包含Object的時候不好像C++基礎型別語義那樣去賦值操作(賦值就是副本,不擔心原物件釋放)。為了既能夠使用移動語義和Document自動管理記憶體,又能夠使用子物件的資料賦值,本文實現的CJsonObject物件返回和設定子物件均使用std::shared_ptr<CJsonObject>來實現。

3 序列化的思路好,但是沒有和反序列化很好的結合:

官網上的序列化實現的思路是仿照STL的思路,使用類似輸出操作符過載的方式給類增加序列化的函式(而不是輸出操作符過載),從而使得複雜的型別(包括類型別成員的)可以藉助成員型別的序列化來實現自己的序列化。

這種方式好處是明顯的,程式結構清晰,Rapidjson對現有程式碼的衝擊最小,想序列化哪一個就實現個序列化的成員函式就行了。但是卻沒有發現反序列化的例子。反序列化就是將json字串通過Parse來建立一個C++記憶體物件,從而使用這個記憶體物件。

其實和序列化的場景一樣,大多數人使用json都是想讓原有型別支援序列化和反序列化,而不是拋棄原有型別(原有型別存在於大量的已有程式碼中,這樣衝擊太大)

還有一種思路是:將Rapidjson視為反序列化的工具,需要反序列化的時候臨時建立Document物件來實現。但是資料在Document中,要想讓現有C++類拿到這些資料,還是要一個一個的GetInt,GetBool,GetDouble等取出來再賦值給現有C++物件。這個是無法忍受的,只能再實現一套幫助類來做轉換。這樣增加了一批幫助類其實也是負擔,而且程式程式碼分散而不集中。

本文的思路:

1 借鑑Rapidjson序列化的思路,讓需要序列化的類自己實現統一的(通過實現基類CJsonBase來做到)序列化和反序列化成員函式。這樣對現有程式碼的衝擊最小,需要新寫的程式碼量最小,最大程度的保護的原有的C++業務類

2 為序列化和反序列化實現一個執行類:CJsonObject,該型別提供序列化和反序列化時的所有操作實現。包括:CreateFromJson,ToJson,Get/SetInt GetSetBool 等。

3 優點:對現有程式碼的衝擊最小(只需要包含新增CJsonObject類即可);只針對對現有程式碼的序列化和反序列化場景(比如C++物件儲存到Redis;公開介面API形式跟外部通訊,等等)

本文示例程式碼:借鑑了官網的serialize.cpp實現

#include "stdafx.h"
#include "JsonObject.h"
#include "Person.h"

#include <iostream>
#include <list>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	std::list<Employee> employees;
	std::list<Employee> employeesCopyFromJson;

	employees.push_back(Employee("Milo YIP", 34, true));
	employees.back().AddDependent(Dependent("Lua YIP", 3, new Education("Happy Kindergarten", 3.5)));
	employees.back().AddDependent(Dependent("Mio YIP", 1));
	
	employees.push_back(Employee("Percy TSE", 30, false));

	cout<<"C++物件->Json字串:"<<endl;
	for (auto itr = employees.begin(); itr != employees.end(); ++itr)
	{
		cout<< itr->ToJson()<<endl;
		Employee employee;
		employee.CreateFromJson(itr->ToJson().c_str());
		employeesCopyFromJson.push_back(employee);
	}
	cout<<endl;
	cout<<"Json字串->C++物件->Json字串:"<<endl;

	for (auto itr = employeesCopyFromJson.begin(); itr != employeesCopyFromJson.end(); ++itr)
	{
		cout<< itr->ToJson()<<endl;
	}

	 return 0;
}


程式輸出:

C++物件->Json字串:
{
    "name": "Milo YIP",
    "age": 34,
    "dependent": [
        {
            "name": "Lua YIP",
            "age": 3,
            "education": {
                "school": "Happy Kindergarten",
                "GPA": 3.5
            }
        },
        {
            "name": "Mio YIP",
            "age": 1
        }
    ],
    "married": true
}
{
    "name": "Percy TSE",
    "age": 30,
    "dependent": [],
    "married": false
}

Json字串->C++物件->Json字串:
{
    "name": "Milo YIP",
    "age": 34,
    "dependent": [
        {
            "name": "Lua YIP",
            "age": 3,
            "education": {
                "school": "Happy Kindergarten",
                "GPA": 3.5
            }
        },
        {
            "name": "Mio YIP",
            "age": 1
        }
    ],
    "married": true
}
{
    "name": "Percy TSE",
    "age": 30,
    "dependent": [],
    "married": false
}
請按任意鍵繼續. . .

附件:本文修改官網序列化示例程式碼的Person.h檔案

#include <list>
#include <memory>
#include <vector>
#include "JsonObject.h"

class Person : public CJsonObjectBase
{
public:
	Person(void){}
	Person(const std::string& name, unsigned age) : name_(name), age_(age) {}
	Person(const Person& rhs) : name_(rhs.name_), age_(rhs.age_) {}
	virtual ~Person();
	//序列化
	virtual std::string ToJson(void) const
	{
		CJsonObject json;
		json.SetStringValue("name", name_);
		json.SetULongValue("age", age_);

		return json.ToJson();
	}
	//反序列化
	virtual void CreateFromJson(const char* _json)
	{
		CJsonObject jsonObj;
		jsonObj.CreateFromJson(_json);
		name_ = jsonObj.GetStringValue("name");
		age_ = jsonObj.GetULongValue("age");
	}


	Person& operator=(const Person& rhs) {
		name_ = rhs.name_;
		age_ = rhs.age_;
		return *this;
	}

public:
	std::string name_;
	unsigned age_;
};

Person::~Person() {
}

class Education  : public CJsonObjectBase{
public:
	Education(void) {}
	Education(const std::string& school, double GPA) : school_(school), GPA_(GPA) {}
	Education(const Education& rhs) : school_(rhs.school_), GPA_(rhs.GPA_) {}
	//序列化
	virtual std::string ToJson(void) const
	{
		CJsonObject json;
		json.SetStringValue("school", school_);
		json.SetDoubleValue("GPA", GPA_);
		return json.ToJson();
	}
	//反序列化
	virtual void CreateFromJson(const char* _json)
	{
		CJsonObject jsonObj;
		jsonObj.CreateFromJson(_json);
		school_ = jsonObj.GetStringValue("school");
		GPA_ = jsonObj.GetDoubleValue("GPA");
	}

public:
	std::string school_;
	double GPA_;
};

class Dependent : public Person {
public:
	Dependent(void):education_(0){};
	Dependent(const std::string& name, unsigned age, Education* education = 0) : Person(name, age), education_(education) {}
	Dependent(const Dependent& rhs) : Person(rhs), education_(0) { education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_); }
	virtual ~Dependent();

	Dependent& operator=(const Dependent& rhs) {
		if (this == &rhs)
			return *this;
		delete education_;
		education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_);
		return *this;
	}

	//序列化
	virtual std::string ToJson(void) const
	{
		CJsonObject json;
		json.CreateFromJson(Person::ToJson().c_str());
		
		std::shared_ptr<CJsonObject> spEducationObj = std::make_shared<CJsonObject>();
		if (education_ != nullptr)
		{
			spEducationObj->CreateFromJson(education_->ToJson().c_str());
			json.SetObjectValue("education", spEducationObj);
		}
		else
		{
			//空指標的話,Json中根本就沒這個成員
		}

		return json.ToJson();
	}
	//反序列化
	virtual void CreateFromJson(const char* _json)
	{
		Person::CreateFromJson(_json);

		CJsonObject jsonObj;
		jsonObj.CreateFromJson(_json);

		if (education_ != nullptr)
		{
			delete education_;
			education_ = nullptr;
		}
		try
		{
			auto spEdution = jsonObj.GetObjectValue("education");
			education_ = new Education;
			education_->CreateFromJson(spEdution->ToJson().c_str());
		}
		catch (CJsonException e)
		{
			delete education_;
			education_ = nullptr;
		}
	}

public:
	Education *education_;
};

Dependent::~Dependent() {
	delete education_; 
}

class Employee : public Person {
public:
	Employee(void){};
	Employee(const std::string& name, unsigned age, bool married) : Person(name, age), dependents_(), married_(married) {}
	Employee(const Employee& rhs) : Person(rhs), dependents_(rhs.dependents_), married_(rhs.married_) {}
	virtual ~Employee();

	//序列化
	virtual std::string ToJson(void) const;
	//反序列化
	virtual void CreateFromJson(const char* _json);

	Employee& operator=(const Employee& rhs) {
		static_cast<Person&>(*this) = rhs;
		dependents_ = rhs.dependents_;
		married_ = rhs.married_;
		return *this;
	}

	void AddDependent(const Dependent& dependent) {
		dependents_.push_back(dependent);
	}

public:
	std::vector<Dependent> dependents_;
	bool married_;
};

Employee::~Employee() {
}

std::string Employee::ToJson(void) const
{
	std::list<std::shared_ptr<CJsonObject>> listJsonObj;
	for (auto itr = dependents_.begin(); itr != dependents_.end(); ++itr)
	{
		std::shared_ptr<CJsonObject> pJsonDependent = std::make_shared<CJsonObject>();
		pJsonDependent->CreateFromJson(itr->ToJson());
		listJsonObj.push_back(pJsonDependent);
		//listJsonObj.push_back(CJsonObject);//這種寫法:複製到容器的副本會導致原來的物件析構
	}

	CJsonObject json;
	json.CreateFromJson(Person::ToJson());
	json.SetObjectListValue("dependent", listJsonObj);
	json.SetBoolValue("married", married_);
	
	std::string str = json.ToJson();
	
	return std::move(str);
}

void Employee::CreateFromJson(const char* _json)
{
	Person::CreateFromJson(_json);

	CJsonObject jsonObj;
	jsonObj.CreateFromJson(_json);

	married_ = jsonObj.GetBoolValue("married");

	auto listDependents = jsonObj.GetObjectListValue("dependent");
	

	for (auto itr = listDependents.begin(); itr != listDependents.end(); ++itr)
	{
		Dependent dependent;
		dependent.CreateFromJson((*itr)->ToJson().c_str());
		this->dependents_.push_back(dependent);
	}
}

全部資源:

有需要的聯絡我頭像

有需要的掃描我頭像加我即可。(付費資源哦!)