背景
我要完成以 json 为数据媒介,来操作数据库和网络传输。查资料,发现 rapidjson 是比较流行的 json 库,并且速度快。但以我的使用方式,用起来非常麻烦,而且我的目的是数据交换。rapidjson 非常普通看起来应该是值传输的操作,其实都是内存移动。这虽然能达到高效率的目的,但一不小心就会出错,而且写出来看着非常丑陋,所以我写了个代理类,来达到我的需求。
设计思想
只是增加一些方便的操作方法,提供多种构造函数来方便的创建对象,提供复制构造函数,提供方便的增加元素的接口,提供对象的遍历方法等等。内部实际上是一个 rapidjson 对象,所有的实际操作都在基于 rapidjson 的,Rjson 只是一个代理,改变了使用的方式,提供了值传输,会有一定的性能下降,但这是适应我的需求所作的必要改变。
具体实现
类结构
Document * json 是实际的 json 对象,我进行封装,提供新的访问接口,对外不暴露其它细节,比如:Value 对象。因此 Rjson 对象的遍历设计就比较麻烦,最终我选择了类似 ES6 的方式,先提供一个 GetAllKeys 方法,再逐一访问每个 value 的方式来进行遍历。
class Rjson {
private:
Document* json;
public:
Rjson();
Rjson(const char* jstr);
Rjson ExtendObject(Rjson& obj);
void AddValueInt(string k, int v);
void AddValueString(string k, string v) ;
...
}
默认构造函数
Rjson() {json = new Document(); //rapidjosn 建立 Document 对象后,必须要增加 Value 或调用 SetObject()来形成一个空 json,不然就会报错
json->SetObject(); // 我合并两个操作,建立一个空 json}
构造函数,接受 char* 参数
Rjson(const char* jstr) {// 注意这里是 const char *,不然从 string.c_str()传递过来就要强转,不然会被匹配也 string 参数的重载构造函数上去。json = new Document(); // 合并两步操作,简单的处理,可以方便很多创建代码的编写
json->Parse(jstr);
}
构造函数,接受 string 参数
Rjson(string jstr) { // 注意构造函数中调用重载构造函数的方法
new (this)Rjson(jstr.c_str());
}
复制构造函数
Rjson(const Rjson& origin) {json = new Document(); // 使用 CopyFrom 实现复制
json->CopyFrom(*(origin.json), json->GetAllocator());
}
赋值操作
Rjson& operator = (const Rjson& origin) {new (this)Rjson(origin); // 利用复制构造函数来实现
return(*this);
}
重载 [] 运算符
string operator[](string key) { // 值都以字符串形式返回
string rs = "";
if (json->HasMember(key.c_str())) {
int vType;
GetValueAndTypeByKey(key.c_str(), &rs, &vType);
}
return rs;
}
增加数值类型
void AddValueInt(string k, int v) {string* newK = new string(k); // 必须新建
Value aInt(kNumberType);
aInt.SetInt(v);
json->AddMember(StringRef(newK->c_str()), aInt, json->GetAllocator()); //addMember 方法是地址传递
}
增加字符串类型
void AddValueString(string k, string v) {string* newK = new string(k);
Value aStr(kStringType); // 必须新建
aStr.SetString(v.c_str(), json->GetAllocator());
json->AddMember(StringRef(newK->c_str()), aStr, json->GetAllocator());
}
增加对象数组
void AddValueArray(string k, vector<string>& arr) {string* newK = new string(k);
int len = arr.size();
Value rows(kArrayType);
for (int i = 0; i < len; i++) {Value al(kStringType); // 必须新建
al.SetString(arr.at(i).c_str(),json->GetAllocator());
rows.PushBack(al, json->GetAllocator());
}
json->AddMember(StringRef(newK->c_str()), rows, json->GetAllocator());
}
取得所有键
vector<string> GetAllKeys() {// 不想显露 Value 对象,使用这个接口加 [] 运算符来完成遍历,若需要值类型,则与 GetValueAndTypeByKey 方法配合。vector<string> keys;
for (auto iter = json->MemberBegin(); iter != json->MemberEnd(); ++iter)
{keys.push_back((iter->name).GetString());
}
return keys;
}
取得指定值及其类型
rapidjson 一共定义了七种值类型,需要一一对应处理
enum Type {
kNullType = 0, //!< null
kFalseType = 1, //!< false
kTrueType = 2, //!< true
kObjectType = 3, //!< object
kArrayType = 4, //!< array
kStringType = 5, //!< string
kNumberType = 6 //!< number
};
暂时只处理了数值、字符串及数组类型
void GetValueAndTypeByKey(string key, string* v, int* vType) {Value::ConstMemberIterator iter = json->FindMember(key.c_str());
if (iter != json->MemberEnd()) {*vType = (int)(iter->value.GetType());
if (iter->value.IsInt()) {
std::stringstream s;
s << iter->value.GetInt();
*v = s.str();}
else if (iter->value.IsString()) {*v = iter->value.GetString();
}
else if (iter->value.IsArray()) {*v = GetJsonString((Value&)iter->value);
}
else {*v = "";}
}
else {
*vType = kStringType;
*v = "";
}
}
获取 json 字符串
string GetJsonString() {
StringBuffer strBuffer;
Writer<StringBuffer> writer(strBuffer);
json->Accept(writer);
return strBuffer.GetString();}
扩展 json 对象
Rjson ExtendObject(Rjson& obj) {Document* src = obj.GetOriginRapidJson();
for (auto iter = src->MemberBegin(); iter != src->MemberEnd(); ++iter)
{if (json->HasMember(iter->name)) { // 键存在,更新
Value& v = (*json)[iter->name];
v.CopyFrom(iter->value, json->GetAllocator());
//v = (Value&)std::move(vTmp);
}
else { // 键不存在,新增
string* newK = new string(iter->name.GetString());
Value vTmp;
vTmp.CopyFrom(iter->value, json->GetAllocator());
json->AddMember(StringRef(newK->c_str()), vTmp, json->GetAllocator());
}
}
return *(this);
}
使用示例
Rjson obj;
obj.AddValueString("username", "插入测试");
obj.AddValueInt("password", 3245);
cout << obj.GetJsonString() << endl;
Rjson obj2(obj);
Rjson obj3("{\"user\":\"bill\",\"age\":12}");
项目地址
https://github.com/zhoutk/Jorm
系列文章规划
- c++ 操作关系数据库通用接口设计(JSON-ORM c++ 版)
- Rjson — rapidjson 代理的设计与实现
- sqlit3 数据库操作的实现与解析
- mysql 数据库操作的实现与解析
- postgres 数据库操作的实现与解析
- oracle 数据库操作的实现与解析
- mssql 数据库操作的实现与解析
- 总结(如果需要的话)
感受
这是封装数据库通用访问接口的第一步,提供方便的 json 对象操作,不然写个 json 对象得郁闷死。很怀念在 javascript 中操作 json 的感觉,如丝般润滑 ……