jsoncpp与rapidjson易用性与性能评测
1. 概述
jsoncpp和rapidjson是两款常用C++11编写的第三方开源JSON序列化与反序列化库, 两者都基于MIT协议发布,对商用较友好,以下从使用上和性能上对两者做出评价,方便不同应用场景选择。
2. 易用性
2.1 jsoncpp
- 头文件源文件并存: 一般根据平台编译成动态库引入项目;
- 中间对象JSON value:构造较容易,使用较方便。
2.2 rapidjson
- 纯头文件: rapidjson只包含头文件,不包含源文件,非常方便集成到项目中;
- 中间对象JSON value: 构造较复杂, 涉及到内存分配器;
- 自包含:rapidjson完全是一个独立的项目,不依赖与第三个库,比如不依赖BOOST,甚至可以不依赖STL;
- 速度快:性能类似于strlen()。
在使用上jsoncpp更胜一筹,性能上rapidjson更好(见3.性能):
jsoncpp中,根节点和子节点都是用Json::Value, 且很方便使用[]操作符操作子节点,节点不存在则新建,存在则引用,而rapidjson只能在节点存在的情况下才能使用[], 否则断言失败,简单举例如下:
序列化_serialize
// jsoncpp
Json::Value root;
Json::Value value;
value["string"] = "hello world";
root.append(std::move(value));
Json::FastWriter writer;
std::string json = writer.write(root);
//rapidjson
rapidjson::Document doc;
Document::AllocatorType &alloc = doc.GetAllocator();
doc.SetArray(); //必须,否则断言错误
rapidjson::Value value(kObjectType); //或者value.SetObject();
value.AddMember("string", "hello world", alloc); //不能使用value["string"],会断言失败
doc.PushBack(value, alloc);
rapidjson::StringBuffer buffer; //对buffer内存的释放只能析构,使用clear方法只能改变size,不能改变capacity, 类似于std::vector
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
doc.Accept(writer);
std::string json = buffer.GetString();
//json 输出
[
{
"string": "hello world"
}
]
特别地,rapidjson::Document为文档对象,即根节点,rapidjson::Value为一般节点,归属于根节点;虽然rapidjson::Document继承自rapidjson::Value,但存在两个不同的节点表示和使用方式,并且全文涉及内存分配器,使用难度稍大一点,一不小心容易内存泄漏,千万记住doc一定要释放, 否则由doc的分配器分配出来的内存是不会自动释放的,如果应用一直服用一个doc,则top会清晰地观察到内存不断增长, 如果不关心中间对象,rapidjson提供了另一种直接的方式进行序列化,直接序列化,不经过JSON value, 如下:
//rapidjson
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
writer.StartArray(); //"["
writer.StartObject();// "{"
writer.key("string");// "string":
writer.String("hello world"); //"hello world"
writer.EndObject(); //"}"
writer.EndArray(); //"]"
std::string json = buffer.GetString();
反序列化_deserialize
//json 输入
[
{
"string": "hello world"
}
]
//jsoncpp
std::string str;
Json::Value root;
Reader reader(Json::Features::strictMode());
if (reader.parse(str, root)) {
//解析成功
if (root.isArray() && !root.empty()) {
auto size = root.size();
for (uint32_t i = 0; i < size; i++)
{
if (root[i].isObject && root[i].isMember("string")) {
str = root[i]["string"].isString() ? root[i]["string"].asString() : "";
}
}
}
}
//rapidjson
std::string str;
rapidjosn::Document doc;
if (!doc.Parse(data, size).HasParseError()) {
//解析成功
if (doc.IsArray()) {
auto arr = root.GetArray();
for (auto &val : arr)
{
if (val.IsObject()) {
auto it = val.FindMember("string");
if (it != val.MemberEnd()) {
if (it->value.IsString()) { //等价于val["string"]
str = std::string(it->value.GetString(), it->value.GetStringLength);
}
}
}
}
}
}
#if 0 //原位解析-更快
rapidjson::InsituStringStream iss(const_cast<char *>(json.c_str());
if (!doc.ParseStream<rapidjson::kParseDefaultFlags | rapidjson::kParseStopWhenDoneFlag | rapidjson::kParseInsituFlag>(iss).HasParseError()) {
//解析成功
}
#endif
以上,jsoncpp和rapidjson反序列化流程基本一致,操作也很接近,但是rapidjson解析速度更快,辅以原位解析(in situ parsing), 性能可以做到极致。
3. 性能
测试版本:
- rapidjson: 1.1.0
- jsoncpp: 1.8.0
- test.json
[{“map_key”:1,“id”:1,“desc”:“配置方案1”,“cycle”:100,“offset”:0,“coordPhase”:1,“timing”:[{“phase”:1,“time”:10,“status”:0,“barrier”:1},{“phase”:2,“time”:10,“status”:0,“barrier”:1},{“phase”:3,“time”:10,“status”:0,“barrier”:1},{“phase”:4,“time”:10,“status”:0,“barrier”:1},{“phase”:5,“time”:15,“status”:0,“barrier”:1},{“phase”:6,“time”:15,“status”:0,“barrier”:1},{“phase”:7,“time”:15,“status”:0,“barrier”:1},{“phase”:8,“time”:15,“status”:0,“barrier”:1},{“phase”:9,“time”:12,“status”:0,“barrier”:1},{“phase”:10,“time”:13,“status”:0,“barrier”:1},{“phase”:11,“time”:12,“status”:0,“barrier”:1},{“phase”:12,“time”:13,“status”:0,“barrier”:1},{“phase”:13,“time”:12,“status”:0,“barrier”:1},{“phase”:14,“time”:13,“status”:0,“barrier”:1},{“phase”:15,“time”:12,“status”:0,“barrier”:1},{“phase”:16,“time”:13,“status”:0,“barrier”:1},{“phase”:17,“time”:12,“status”:0,“barrier”:1},{“phase”:18,“time”:13,“status”:0,“barrier”:1},{“phase”:19,“time”:12,“status”:0,“barrier”:1},{“phase”:20,“time”:13,“status”:0,“barrier”:1},{“phase”:21,“time”:12,“status”:0,“barrier”:1},{“phase”:22,“time”:13,“status”:0,“barrier”:1},{“phase”:23,“time”:12,“status”:0,“barrier”:1},{“phase”:24,“time”:13,“status”:0,“barrier”:1},{“phase”:25,“time”:12,“status”:0,“barrier”:1},{“phase”:26,“time”:13,“status”:0,“barrier”:1},{“phase”:27,“time”:12,“status”:0,“barrier”:1},{“phase”:28,“time”:13,“status”:0,“barrier”:1},{“phase”:29,“time”:12,“status”:0,“barrier”:1},{“phase”:30,“time”:9,“status”:0,“barrier”:1},{“phase”:31,“time”:10,“status”:0,“barrier”:1},{“phase”:32,“time”:19,“status”:0,“barrier”:1},{“phase”:33,“time”:6,“status”:0,“barrier”:1},{“phase”:34,“time”:94,“status”:0,“barrier”:1},{“phase”:35,“time”:50,“status”:0,“barrier”:1},{“phase”:36,“time”:50,“status”:0,“barrier”:1},{“phase”:37,“time”:50,“status”:0,“barrier”:1},{“phase”:38,“time”:50,“status”:0,“barrier”:1},{“phase”:39,“time”:100,“status”:0,“barrier”:1}],“turn”:[[1,2,3,4,5,6,7,8],[9,10,11,12,13,14,15,16],[17,18,19,20,21,22,23,24],[25,26,27,28,29,30,31,32],[33,34],[35,36],[37,38],[39]]},…+64(重复64次) 126KB
测试主机arm-linux, 单核, 128M, 400MHZ, at91sam9x25(arm926)平台
arm-at91-linux-gnueabi-g++ -std=c++11 -Wall
- | JSON -> Value | Value -> T | JSON -> T | T -> Value | Value -> JSON | T -> JSON |
---|---|---|---|---|---|---|
jsoncpp | 1s 778ms 593us | 0s 426ms 634us | 2s 262ms 984us | 1s 375ms 353us | 1s 376ms 462us | 2s 867ms 207us |
rapidjson | 0s 255ms 710us | 0s 165ms 277us | 0s 426ms 507us | 0s 180ms 312us | 0s 146ms 775us | 0s 249ms 457us |
rapidjson(in situ) | 0s 199ms 849us | - | 0s 365ms 60us | - | - | - |
arm-at91-linux-gnueabi-g++ -std=c++11 -Wall -O3
- | JSON -> Value | Value -> T | JSON -> T | T -> Value | Value -> JSON | T -> JSON |
---|---|---|---|---|---|---|
jsoncpp | 0s 570ms 701us | 0s 80ms 454us | 0s 661ms 160us | 0s 417ms 534us | 0s 716ms 88us | 1s 131ms 268us |
rapidjson | 0s 33ms 829us | 0s 20ms 865us | 0s 56ms 863us | 0s 40ms 434us | 0s 21ms 870us | 0s 59ms 594us |
rapidjson(in situ) | 0s 31ms 48us | - | 0s 55ms 364us | - | - | - |
T: 结构体
JSON: 字符串
Value: json对象
4.总结
- 优先考虑使用性,选择jsoncpp;
- 优先考虑性能,速度,选择rapidjson;
- 均使用-O3优化。