最近在一个需要高并发的项目中使用到json,最开始使用jsoncpp进行解析和序列化,但是在json数据比较大时jsoncpp会抛异常,在多线程的情况下抛异常的概率更加频繁!后来发现是因为jsoncpp版本比较老,老版本的jsoncpp不是多线程安全的(参考 https://github.com/open-source-parsers/jsoncpp/issues/823)。更新到最新的版本后,处理性能达不到预期,于是查找原因,经过hotspots分析,发现大部分时间消耗在jsoncpp的readValue函数,在一些存在大量float数值的json中解析数值的操作耗时较大。
于是找到了rapidjson,根据benchmark结果看起来不错,经过实际测试发现在40线程的压力测试下相比jsoncpp性能提升10倍左右,一些存在大量float数值的case性能提升更明显!
如下,整理一下rapidjson使用的方法:
#ifndef rapidjson_utils_h__ #define rapidjson_utils_h__ #include "rapidjson/rapidjson.h" #include "rapidjson/document.h" #include "rapidjson/pointer.h" #include "rapidjson/writer.h" #include "rapidjson/prettywriter.h" #include "rapidjson/error/en.h" #include <string> namespace utils { using std::string; static int get_rapidjson_string(const rapidjson::Value& js_in, std::string key, std::string& value) { rapidjson::Pointer pointer(key.c_str()); if (!pointer.IsValid()) { return -1; } const rapidjson::Value *pValue = rapidjson::GetValueByPointer(js_in, pointer); if (NULL == pValue || !pValue->IsString()) { return -1; } value = pValue->GetString(); return 0; } static int get_rapidjson_int(const rapidjson::Value& js_in, std::string key, int& value) { rapidjson::Pointer pointer(key.c_str()); if (!pointer.IsValid()) { return -1; } const rapidjson::Value *pValue = rapidjson::GetValueByPointer(js_in, pointer); if (NULL == pValue || !pValue->IsNumber()) { return -1; } value = pValue->GetInt(); return 0; } ///< get float number static int get_rapidjson_number(const rapidjson::Value& js_in, std::string key, float& value) { rapidjson::Pointer pointer(key.c_str()); if (!pointer.IsValid()) { return -1; } const rapidjson::Value *pValue = rapidjson::GetValueByPointer(js_in, pointer); if (NULL == pValue || !pValue->IsNumber()) { return -1; } rapidjson::Type valueType = pValue->GetType(); switch (valueType) { case rapidjson::Type::kNumberType: value = pValue->GetDouble(); break; default: return -1; } return 0; } static int get_rapidjson_bool(const rapidjson::Value& js_in, std::string key, bool& value) { rapidjson::Pointer pointer(key.c_str()); if (!pointer.IsValid()) { return -1; } const rapidjson::Value *pValue = rapidjson::GetValueByPointer(js_in, pointer); if (NULL == pValue || !pValue->IsBool()) { return -1; } value = pValue->GetBool(); return 0; } static void add_member_string(rapidjson::Value& node, string key, string value, rapidjson::Document::AllocatorType &alloc) { node.AddMember(rapidjson::Value(key.c_str(), key.length(), alloc).Move(), rapidjson::Value(value.c_str(), value.length(), alloc).Move(), alloc); } static void add_member_int(rapidjson::Value& node, string key, int value, rapidjson::Document::AllocatorType &alloc) { node.AddMember(rapidjson::Value(key.c_str(), key.length(), alloc).Move(), rapidjson::Value(value).Move(), alloc); } static void add_member_float(rapidjson::Value& node, string key, double value, rapidjson::Document::AllocatorType &alloc) { node.AddMember(rapidjson::Value(key.c_str(), key.length(), alloc).Move(), rapidjson::Value(value).Move(), alloc); } static void add_member_bool(rapidjson::Value& node, string key, bool value, rapidjson::Document::AllocatorType &alloc) { node.AddMember(rapidjson::Value(key.c_str(), key.length(), alloc).Move(), rapidjson::Value(value).Move(), alloc); } static string to_string(const rapidjson::Value& v) { rapidjson::StringBuffer sb; rapidjson::Writer<rapidjson::StringBuffer> writer(sb); v.Accept(writer); std::string sResult = sb.GetString(); return sResult; } static string to_styled_string(const rapidjson::Value& v) { rapidjson::StringBuffer sb; rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb); v.Accept(writer); std::string s = sb.GetString(); return s; } } //namespace utils #endif // rapidjson_utils_h__
#include <iostream> #include <fstream> #include <sstream> #include <string> #include "rapidjson_utils.hpp" using std::string; int main(int argc, char** argv) { string data = "{\"user\":\"hello\",\"movies\":[{\"name\":\"TENET\",\"like\":10000}],\"song\":{\"暗里着迷\":\"已经播放了3遍\"},\"ratio\":0.98}"; rapidjson::Document doc; rapidjson::ParseResult ok = doc.Parse(data.c_str()); if (!ok) { printf("parse json failure id=%d offset=%d msg=%s\n", doc.GetParseError(), doc.GetErrorOffset(), rapidjson::GetParseError_En(doc.GetParseError())); return -1; } string user; utils::get_rapidjson_string(doc, "/user", user); printf("user=%s\n", user.c_str()); ///< 使用迭代器遍历数组 const rapidjson::Value* pMovies = rapidjson::GetValueByPointer(doc, "/movies"); if (nullptr != pMovies && pMovies->IsArray() && !pMovies->Empty()) { for (rapidjson::Value::ConstValueIterator iter = pMovies->Begin(); iter != pMovies->End(); iter++) { string name; int like; utils::get_rapidjson_string(*iter, "/name", name); utils::get_rapidjson_int(*iter, "/like", like); printf("movie=%s like=%d\n", name.c_str(), like); } } const rapidjson::Value* pSong = rapidjson::GetValueByPointer(doc, "/song"); if (nullptr != pSong && pSong->IsObject() && !pSong->ObjectEmpty()) { ///< 使用迭代器遍历object const rapidjson::Value& song = *pSong; for (rapidjson::Value::ConstMemberIterator miter = song.MemberBegin(); miter != song.MemberEnd(); miter++) { const rapidjson::Value& name = miter->name; const rapidjson::Value& value = miter->value; printf("song=%s message=%s\n", name.GetString(), value.GetString()); } } float ratio; utils::get_rapidjson_number(doc, "/ratio", ratio); printf("ratio=%f\n", ratio); ///< 生成一个新的json rapidjson::Document out(rapidjson::kObjectType); rapidjson::Document::AllocatorType &a = out.GetAllocator(); out.CopyFrom(doc, a); rapidjson::Value* pNewMovies = rapidjson::GetValueByPointer(out, "/movies"); rapidjson::Value movie(rapidjson::kObjectType); utils::add_member_string(movie, "name", "Memento", a); utils::add_member_int(movie, "like", 12366, a); utils::add_member_float(movie, "score", 0.95, a); pNewMovies->PushBack(movie.Move(), a); ///< SetValueByPointer可以新增节点 rapidjson::SetValueByPointer(out, "/version", 2.0, a); string s = utils::to_styled_string(out); printf("after modify json=%s\n", s.c_str()); return 0; }
参考:
rapidjson官网:http://rapidjson.org/zh-cn/
git仓库:https://github.com/Tencent/rapidjson.git
一些json解析器的性能对比:https://github.com/miloyip/nativejson-benchmark
jsoncpp和rapidjson哪个好用?:https://www.zhihu.com/question/23654513