手机网站建站平台,申请网页的注意事项,电子商务官方网站,济南制作网站软件Rapidjson 是一款 C 的 json 库. 支持处理 json 格式的文档. 其设计风格是头文件库, 包含头文件即可使用, 小巧轻便并且性能强悍. 本文结合样例来介绍 Rapidjson 一些常见的用法.
环境要求
有如何的几种方法可以将 Rapidjson 集成到您的项目中. Vcpkg安装: 使用 vcpkg instal…Rapidjson 是一款 C 的 json 库. 支持处理 json 格式的文档. 其设计风格是头文件库, 包含头文件即可使用, 小巧轻便并且性能强悍. 本文结合样例来介绍 Rapidjson 一些常见的用法.
环境要求
有如何的几种方法可以将 Rapidjson 集成到您的项目中. Vcpkg安装: 使用 vcpkg install rapidjson即可. 如果不熟悉 vcpkg 请参考我的文章: [C包管理工具-Vcpkg 简介]({{ relref “2024-07-29-cpp-package-management.md” }}). CMake 的FetchContent_Declare方法. # 引入 FetchContent 模块
include(FetchContent)# 设置 Rapidjson 编译选项
set(RAPIDJSON_BUILD_TESTS OFF CACHE INTERNAL )
set(RAPIDJSON_BUILD_DOC OFF CACHE INTERNAL )
set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE INTERNAL )
set(RAPIDJSON_BUILD_CXX20 ON CACHE INTERNAL )FetchContent_Declare(rapidjsonURL https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.zip
)
FetchContent_MakeAvailable(rapidjson)源码安装: 下载源码并将其路径加入include目录列表中: gcc -I /path/to/rapidjson
基础用法
解析 json
auto input R({name: 华安, id: 9527});
rapidjson::Document doc;doc.Parse(input);
if (doc.HasParseError()) {return -1;
}访问元素
检查并获取
HasMember 查询 key 是否存在, 然后使用Is方法来判断类型是否兼容, 最后用Get方法来获取对应的值.
if (doc.HasMember(name) doc[name].IsString()) {std::string name doc[name].GetString();std::cout name is: name std::endl;
}
if (doc.HasMember(id) doc[id].IsInt()) {int id doc[id].GetInt();std::cout id is: id std::endl;
}使用FindMember减少查询开销
上述示例中, doc[name]被使用了两次, 相当于创建了两个临时变量. 使用FindMember方法则可以减少这种额外开销.
if (auto it doc.FindMember(name);it ! doc.MemberEnd() it-value.IsString()) {std::string name it-value.GetString();std::cout name is: name std::endl;
}if (auto it doc.FindMember(id);it ! doc.MemberEnd() it-value.IsInt()) {auto id it-value.GetInt();std::cout id is: id std::endl;
}访问对象(Object)
查询方法与前面的基础类型相似. 需要注意的是, GetObject()方法返回的是一个const引用. Rapidjson 为了提高效率, 接口的设计上避免使用对象拷贝.
auto response R({code:200,data:{total:200,curr:[12345,23456,34564]}});rapidjson::Document doc;if (doc.Parse(response).HasParseError()) {return -1;
}if (auto it doc.FindMember(data);it ! doc.MemberEnd() it-value.IsObject()) {const auto data it-value.GetObject();// ...
}访问数组(Array)
我们用IsArray()和GetArray()来判断和获取对应的数据.
需要注意的是: json 中的数组是允许多个不同类型的, 如下是一个合法的 json:
{array: [string, true, null, [], {}, 123]
}但是 C 的数组或者容器vector仅支持相同的元素, 所以我们在获取数组元素时需要注意判断元素类型.
for (auto it curr.Begin(); it ! curr.End(); it) {if (it-IsInt()) {std::cout it-GetInt() std::endl;}
}由于 rapidjson 支持range based for, 我们可以这样写:
for (const auto item : curr) {if (item.IsInt()) {std::cout item.GetInt() std::endl;}
}生成 json 对象
基础类型
对于基础类型(整型, 布尔值, 浮点数)我们可以直接使用AddMember添加, 需要注意的是接口中需要指定一个Allocator.
rapidjson::Document doc(rapidjson::kObjectType);
doc.AddMember(name, 华安, doc.GetAllocator());
doc.AddMember(id, 9527, doc.GetAllocator());
doc.AddMember(is_intern, true, doc.GetAllocator());此时doc的内容为:
{ name: 华安, id: 9527, is_intern: true }为了减少对GetAllocator()的调用, 可以使用一个变量保存该结果, 见后续代码.
添加对象
一个 Object 对象可以用rapidjson::Value表示. 其添加成员的方法是AddMember(rapidjson::Document是rapidjson::Value的衍生类).
对于特殊值null, 我们可以使用SetNull()方法或者在构造函数中指定rapidjson::kNullType来实现.
rapidjson::Value contact(rapidjson::kObjectType);rapidjson::Value email;
email.SetNull(); // 设置为null
contact.AddMember(email, email, allocator);contact.AddMember(twitter, rapidjson::Value(rapidjson::kNullType),allocator);doc.AddMember(contact, contact, allocator);此时的doc为:
{name: 华安,id: 9527,is_intern: true,contact: { email: null, twitter: null }
}添加数组
Array 类型的创建和添加如下所示.
auto allocator doc.GetAllocator();rapidjson::Value friends(rapidjson::kArrayType);
friends.PushBack(祝枝山, allocator);
friends.PushBack(文征明, allocator);
friends.PushBack(徐祯卿, allocator);doc.AddMember(friends, friends, allocator);此时doc为:
{name: 华安,id: 9527,is_intern: true,contact: { email: null, twitter: null },friends: [祝枝山, 文征明, 徐祯卿]
}序列化 json 对象
#include rapidjson/document.h
#include rapidjson/filewritestream.h
#include rapidjson/writer.h#include iostreamvoid print(rapidjson::Value value) {rapidjson::StringBuffer buffer;rapidjson::Writerrapidjson::StringBuffer writer(buffer);value.Accept(writer);std::cout buffer.GetString() std::endl;
}进阶用法
使用函数模板简化解析
从前面解析的例子我们可以看到, 对每一个字段都要解析代码, 这样会存在很多的代码冗余.
可以通过模板函数来实现一个解析代码. 我们用std::variant来存储不同的解析类型, 比如:int*, double*, std::string*等.
接着我们用std::visit来访问std::variant, 针对不同类型做不同的解析, 对目前尚不支持的类型则报错.
template typename... T
bool Parse(rapidjson::Value data, const char* name,std::variantT... target) {auto it data.FindMember(name);if (it data.MemberEnd()) {std::cerr key not found: name std::endl;return false; // 字段不存在}// 使用 std::visit 处理 std::variantreturn std::visit([](auto value) {using ValueType std::remove_pointer_tdecltype(value);if constexpr (std::is_same_vValueType, std::string) {if (!it-value.IsString()) {std::cerr string not match: name std::endl;return false; // 类型不匹配}*value std::string(it-value.GetString(), it-value.GetStringLength());} else if constexpr (std::is_integral_vValueType ||std::is_floating_point_vValueType) {if (!it-value.IsValueType()) {std::cerr integer not found: name std::endl;return false; // 类型不匹配}*value it-value.GetValueType();} else {std::cerr unsupported type\n;return false; // 不支持的类型}return true; // 解析成功},target);
}如何使用呢? 此处以解析一个结构体为例:
struct Person {bool married false;int id 0;int age 0;double point 0;std::string name;std::string email;
};bool ParsePerson(Person* person, rapidjson::Value json) {std::vectorstd::tupleconst char*, std::variantint*, std::string*, double*, bool*list {{id, person-id}, {age, person-age},{name, person-name}, {email, person-email},{married, person-married}, {point, person-point},};for (auto [name, variant] : list) {if (!Parse(json, name, variant)) {return false; // 解析失败}}return true; // 解析成功
}完整示例请参考仓库代码: parse.cpp
处理多重嵌套
在工作中我们有时候会遇到嵌套很深的 json 文档. 比如给定这样一个 json 文档, 现在我们要获取/data/avatar/image/thumbnail如何操作?
{code: 200,data: {avatar: {image: {medium: https://image.com/hua.an.jpg,thumbnail: https://image.com/hua.an-thumbnail.jpg}}}
}如果按照之前的写法层层解析, 那么必然是个很深的嵌套. 但是现在有个更好的解决办法, 就是JSONPath, 在 rapidjson 里面就是 Pointer类, 参考如下写法:
rapidjson::Document doc;if (doc.Parse(response).HasParseError()) {std::cerr JSON parse error! std::endl;return -1;
}// 使用 RapidJSON 的 Pointer 解析 JSONPath
const char* jsonpath /data/avatar/image/thumbnail;
rapidjson::Pointer pointer(jsonpath);// 获取 JSONPath 对应的值
if (rapidjson::Value* value pointer.Get(doc)) {if (value-IsString()) {std::cout Thumbnail URL: value-GetString() std::endl;} else {std::cerr Thumbnail is not a string! std::endl;}
} else {std::cerr Thumbnail not found! std::endl;
}完整的代码请参考: jsonpath.cpp
总结
本文通过示例介绍了一些 rapidjson 的使用方法, 包括解析,生成,以及如何做代码优化. 希望能给读者带来一些帮助.
如果您觉得有用, 希望您点赞收藏关注, 感激不尽.
源码链接
源码链接Rapidjson 官网Rapidjson Pointer