企业备案网站名称要求/国内广告联盟平台
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 <iostream>void print(rapidjson::Value& value) {rapidjson::StringBuffer buffer;rapidjson::Writer<rapidjson::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::variant<T...>& 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_t<decltype(value)>;if constexpr (std::is_same_v<ValueType, 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_v<ValueType> ||std::is_floating_point_v<ValueType>) {if (!it->value.Is<ValueType>()) {std::cerr << "integer not found: " << name << std::endl;return false; // 类型不匹配}*value = it->value.Get<ValueType>();} 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::vector<std::tuple<const char*, std::variant<int*, 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
相关文章:

Rapidjson 实战
Rapidjson 是一款 C 的 json 库. 支持处理 json 格式的文档. 其设计风格是头文件库, 包含头文件即可使用, 小巧轻便并且性能强悍. 本文结合样例来介绍 Rapidjson 一些常见的用法. 环境要求 有如何的几种方法可以将 Rapidjson 集成到您的项目中. Vcpkg安装: 使用 vcpkg instal…...

【React】受控组件和非受控组件
目录 受控组件非受控组件基于ref获取DOM元素1、在标签中使用2、在组件中使用 受控组件 表单元素的状态(值)由 React 组件的 state 完全控制。组件的 state 保存了表单元素的值,并且每次用户输入时,React 通过事件处理程序来更新 …...

Ollama+deepseek+Docker+Open WebUI实现与AI聊天
1、下载并安装Ollama 官方网址:Ollama 安装好后,在命令行输入, ollama --version 返回以下信息,则表明安装成功, 2、 下载AI大模型 这里以deepseek-r1:1.5b模型为例, 在命令行中,执行&…...

DEEPSEKK GPT等AI体的出现如何重构工厂数字化架构:从设备控制到ERP MES系统的全面优化
随着深度学习(DeepSeek)、GPT等先进AI技术的出现,工厂的数字化架构正在经历前所未有的变革。AI的强大处理能力、预测能力和自动化决策支持,将大幅度提升生产效率、设备管理、资源调度以及产品质量管理。本文将探讨AI体(…...

阿莱(arri)mxf文件变0字节的恢复方法
阿莱(arri)是专业级的影视产品软硬件供应商,很多影视作品都是使用阿莱(arri)的设备拍摄出来的。总体上来讲阿莱(arri)的文件格式有mov和mxf两种,这次恢复的是阿莱(arri)的mxf,机型是arri mini,素材保存在一个8t的硬盘上,使用的是e…...

初识 Node.js
在当今快速发展的互联网技术领域,Node.js 已经成为了一个非常流行且强大的平台。无论是构建高性能的网络应用、实时协作工具还是微服务架构,Node.js 都展示了其独特的优势。本文将带您走进 Node.js 的世界,了解它的基本概念、核心特性以及如何…...

debug-vscode调试方法
debug - vscode gdb调试指南 文章目录 debug - vscode gdb调试指南前言一、调试代码二、命令查看main反汇编查看寄存器打印某个变量打印寄存器,如pc打印当前函数栈信息(当前执行位置)打印程序栈局部变量x命令的语法如下所示:打印某…...

Cypher进阶(函数、索引)
文章目录 Cypher进阶Aggregationcount()函数统计函数collect()函数 unwindforeachmergeunionload csvcall 函数断言函数all()any()~~exists()~~is not nullnone()single() 标量函数coalesce()startNode()/endNode()id()length()size() 列表函数nodes()keys()range()reduce() 数…...

XML Schema 数值数据类型
XML Schema 数值数据类型 引言 XML Schema 是一种用于描述 XML 文档结构的语言。它定义了 XML 文档中数据的有效性和结构。在 XML Schema 中,数值数据类型是非常重要的一部分,它定义了 XML 文档中可以包含的数值类型。本文将详细介绍 XML Schema 中常用的数值数据类型,以及…...

Window获取界面空闲时间
GetLastInputInfo是一种Windows API函数,用于获取上次输入操作的时间。 该函数通过LASTINPUTINFO结构返回最后一次输入事件的时间。 原型如下 BOOL WINAPI GetLastInputInfo(PLASTINPUTINFO plii);那么可以利用GetLastInputInfo来得到界面没有操作的时长 uint…...

Java进阶(vue基础)
目录 1.vue简单入门 ?1.1.创建一个vue程序 1.2.使用Component模板(组件) 1.3.引入AXOIS ?1.4.vue的Methods(方法) 和?compoted(计算) 1.5.插槽slot 1.6.创建自定义事件? 2.Vue脚手架安装? 3.Element-UI的…...

Mac电脑上好用的压缩软件
在Mac电脑上,有许多优秀的压缩软件可供选择,这些软件不仅支持多种压缩格式,还提供了便捷的操作体验和强大的功能。以下是几款被广泛推荐的压缩软件: BetterZip 功能特点:BetterZip 是一款功能强大的压缩和解压缩工具&a…...

Ubuntn24.04安装
1.镜像下载 https://cn.ubuntu.com/download Ubuntu 24.04.1 (Noble Numbat) 进入下载即可 2.安装系统 打开虚拟机 选择语言 输入用户名和密码 安装ssh 安装完成重启即可。 3.可能出现的问题 关于Ubuntu系统虚拟机出现频繁闪屏,移动和屏幕适应大小问题_vmware安…...

基于ansible部署elk集群
ansible部署 ELK部署 ELK常见架构 (1)ElasticsearchLogstashKibana:这种架构是最常见的一种,也是最简单的一种架构,这种架构通过Logstash收集日志,运用Elasticsearch分析日志,最后通过Kibana中…...

解锁.NET Fiddle:在线编程的神奇之旅
在.NET 开发的广袤领域中,快速验证想法、测试代码片段以及便捷地分享代码是开发者们日常工作中不可或缺的环节。而.NET Fiddle 作为一款卓越的在线神器,正逐渐成为众多.NET 开发者的得力助手。它打破了传统开发模式中对本地开发环境的依赖,让…...

记录pve中使用libvirt创建虚拟机
pve中创建虚拟机 首先在pve网页中创建一个linux虚拟机,我用的是debian系统,过程省略 注意虚拟机cpu类型要设置为host 检查是否支持虚拟化 ssh分别进入pve和debian虚拟机 检查cpu是否支持虚拟化 egrep --color vmx|svm /proc/cpuinfo # 结果高亮显示…...

【HTML性能优化】提升网站加载速度:GZIP、懒加载与资源合并
系列文章目录 01-从零开始学 HTML:构建网页的基本框架与技巧 02-HTML常见文本标签解析:从基础到进阶的全面指南 03-HTML从入门到精通:链接与图像标签全解析 04-HTML 列表标签全解析:无序与有序列表的深度应用 05-HTML表格标签全面…...

三维空间全局光照 | 及各种扫盲
Lecture 6 SH for diffuse transport Lecture 7关于 SH for glossy transport 三维空间全局光照 diffuse case和glossy case的区别 在Lambertian模型中,BRDF是一个常数 diffuse case 跟outgoing point无关 glossy case 跟outgoing point有关 (Gloss…...

数据库开发常识(10.6)——SQL性能判断标准及索引误区(1)
10.6. 数据库开发常识 作为一名专业数据库开发人员,不但需要掌握数据库开发相关的语法和功能实现,还要掌握专业数据库开发的常识。这样,才能在保量完成工作任务的同时,也保质的完成工作任务,避免了为应用的日后维护埋…...

网络爬虫js逆向之某音乐平台案例
【注意!!!】 前言: - 本章主要讲解某音乐平台的js逆向知识 - 使用关键字搜定位加密入口 - 通过多篇文章【文字案例】的形式系统化进行描述 - 本文章全文进行了脱敏处理 - 详细代码不进行展示,需要则私聊作者 爬虫js逆向…...

Spark--算子执行原理
一、sortByKey SortByKey是一个transformation算子,但是会触发action,因为在sortByKey方法内部,会对每个分区进行采样,构建分区规则(RangePartitioner)。 内部执行流程 1、创建RangePartitioner part&…...

事件驱动架构(EDA)
事件驱动架构(Event-Driven Architecture, EDA)是一种软件架构模式,其中系统的行为由事件的产生和处理驱动。在这种架构中,系统的组件通过事件进行交互,而不是通过直接的调用或者请求响应方式。 关键概念 事件&#x…...

C++ 入门速通-第5章【黑马】
内容来源于:黑马 集成开发环境:CLion 先前学习完了C第1章的内容: C 入门速通-第1章【黑马】-CSDN博客 C 入门速通-第2章【黑马】-CSDN博客 C 入门速通-第3章【黑马】-CSDN博客 C 入门速通-第4章【黑马】-CSDN博客 下面继续学习第5章&…...

2025春招,深度思考MyBatis面试题
大家好,我是V哥,2025年的春招马上就是到来,正在准备求职的朋友过完年,也该收收心,好好思考一下自己哪些技术点还需要补一补了,今天 V 哥要跟大家聊的是MyBatis框架的问题,站在一个高级程序员的角…...

排序算法--冒泡排序
冒泡排序虽然简单,但在实际应用中效率较低,适合小规模数据或教学演示。 // 冒泡排序函数 void bubbleSort(int arr[], int n) {for (int i 0; i < n - 1; i) { // 外层循环控制排序轮数for (int j 0; j < n - i - 1; j) { // 内层循环控制每轮比…...

简易C语言矩阵运算库
参考网址: 异想家纯C语言矩阵运算库 - Sandeepin - 博客园 这次比opencv快⑥倍!!! 参考上述网址,整理了一下代码: //main.c#include <stdio.h> #include <stdlib.h> #include <string.h…...

通过C/C++编程语言实现“数据结构”课程中的链表
引言 链表(Linked List)是数据结构中最基础且最重要的线性存储结构之一。与数组的连续内存分配不同,链表通过指针将分散的内存块串联起来,具有动态扩展和高效插入/删除的特性。本文将以C/C++语言为例,从底层原理到代码实现,手把手教你构建完整的链表结构,并深入探讨其应…...

【分布式架构理论3】分布式调用(2):API 网关分析
文章目录 一、API 网关的作用1. 业务层面:简化调用复杂性2. 系统层面:屏蔽客户端调用差异3. 其他方面: 二、API 网关的技术原理1. 协议转换2. 链式处理3. 异步请求机制1. Zuul1:同步阻塞处理2. Zuul2:异步非阻塞处理 三…...

基于Kamailio、MySQL、Redis、Gin、Vue.js的微服务架构
每个服务使用一台独立的服务器的可行部署方案,尤其是在高并发、高可用性要求较高的场景中。这种方案通常被称为分布式部署或微服务架构。以下是针对您的VoIP管理系统(基于Kamailio、MySQL、Redis、Gin、Vue.js)的详细分析和建议。 1. 分布式部…...

6S模型的编译问题解决
使用python处理遥感光谱数据,免不了进行大气校正,基本上免费的就是使用Py6s,而py6s库只是一个接口,还需要自己配置6S模型,可以查到很多资料,6S模型是古老的fortran语言写的,基本配置流程就是安装…...