当前位置: 首页 > article >正文

Rapidjson 实战

Rapidjson 是一款 C++ 的 json 库. 支持处理 json 格式的文档. 其设计风格是头文件库, 包含头文件即可使用, 小巧轻便并且性能强悍. 本文结合样例来介绍 Rapidjson 一些常见的用法.

环境要求

有如何的几种方法可以将 Rapidjson 集成到您的项目中.

  1. Vcpkg安装: 使用 vcpkg install rapidjson即可. 如果不熟悉 vcpkg 请参考我的文章: [C++包管理工具-Vcpkg 简介]({{< relref “2024-07-29-cpp-package-management.md” >}}).

  2. CMakeFetchContent_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)
    
  3. 源码安装: 下载源码并将其路径加入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::Documentrapidjson::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…...

DeepSeek的多模态AI模型-Janus-pro,可生图,可读图

简介 Janus-Pro 是由 DeepSeek 开发的一款多模态理解与生成模型&#xff0c;是 Janus 模型的升级版。它能够同时处理文本和图像&#xff0c;既能理解图像内容&#xff0c;又能根据文本描述生成高质量图像。Janus-Pro 的核心目标是通过解耦视觉编码路径&#xff0c;解决多模态理…...

Python爬虫实战:一键采集电商数据,掌握市场动态!

电商数据分析是个香饽饽&#xff0c;可市面上的数据采集工具要不贵得吓人&#xff0c;要不就是各种广告弹窗。干脆自己动手写个爬虫&#xff0c;想抓啥抓啥&#xff0c;还能学点技术。今天咱聊聊怎么用Python写个简单的电商数据爬虫。 打好基础&#xff1a;搞定请求头 别看爬虫…...

最短木板长度

最短木板长度 真题目录: 点击去查看 E 卷 100分题型 题目描述 小明有 n 块木板&#xff0c;第 i ( 1 ≤ i ≤ n ) 块木板长度为 ai。 小明买了一块长度为 m 的木料&#xff0c;这块木料可以切割成任意块&#xff0c;拼接到已有的木板上&#xff0c;用来加长木板。 小明想让最…...

【人工智能】掌握图像风格迁移:使用Python实现艺术风格的自动化迁移

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 图像风格迁移(Image Style Transfer)是一种基于深度学习的计算机视觉技术,通过将一张图像的内容与另一张图像的艺术风格结合,生成一幅具…...

git submodules

当代码仓库中包含 .gitmodules 文件时&#xff0c;这意味着该仓库使用了 Git 子模块&#xff08;Git Submodules&#xff09;。.gitmodules 文件记录了子模块的相关信息&#xff0c;如子模块的仓库地址、路径等。若要在下载代码时一并同步子模块&#xff0c;可以按照以下几种常…...

7 与mint库对象互转宏(macros.rs)

macros.rs代码定义了一个Rust宏mint_vec&#xff0c;它用于在启用mint特性时&#xff0c;为特定的向量类型实现与mint库中对应类型的相互转换。mint库是一个提供基本数学类型&#xff08;如点、向量、矩阵等&#xff09;的Rust库&#xff0c;旨在与多个图形和数学库兼容。这个宏…...

游戏引擎 Unity - Unity 下载与安装

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…...

[openwrt]openwrt slaac only模式下部分终端无法获取到IPv6 DNS

问题描述 OpenWrt 中,如果启用了 RA 单播(ra_unicast),但部分终端无法获取到 DNS 信息 问题分析 RA 单播的局限性 并非所有终端都完全支持通过单播接收 RA 消息。部分终端可能无法正确解析单播 RA 中的 RDNSS(Recursive DNS Server)选项,从而导致无法获取 DNS 信息。终…...

Java 面试真题

本题适合一到三年 Java 开发 &#xff0c;以下问题都是按照原面试官提问记录 文章目录 我要进大厂系列面试题二面 我要进大厂系列面试题 全部真题&#xff0c;欢迎投稿你的面试经验。 本篇涉及基础较多&#xff0c;但要耐性看完。 JVM内存模型垃圾回收器用的哪个gc各个算法…...

验证工具:GVIM和VIM

一、定义与关系 gVim&#xff1a;gVim是Vim的图形界面版本&#xff0c;提供了更多的图形化功能&#xff0c;如菜单栏、工具栏和鼠标支持。它使得Vim的使用更加直观和方便&#xff0c;尤其对于不习惯命令行界面的用户来说。Vim&#xff1a;Vim是一个在命令行界面下运行的文本编…...

理解 C 与 C++ 中的 const 常量与数组大小的关系

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;数组大小的常量要求&#x1f4af;C 语言中的数组大小要求&#x1f4af;C 中的数组大小要求&#x1f4af;为什么 C 中 const 变量可以作为数组大小&#x1f4af;进一步的…...

孟加拉国_行政边界省市边界arcgis数据shp格式wgs84坐标

这篇内容将深入探讨孟加拉国的行政边界省市边界数据&#xff0c;该数据是以arcgis的shp格式提供的&#xff0c;并采用WGS84坐标系统。ArcGIS是一款广泛应用于地理信息系统&#xff08;GIS&#xff09;的专业软件&#xff0c;它允许用户处理、分析和展示地理空间数据。在GIS领域…...

安心即美的生活方式

如果你的心是安定的&#xff0c;那么&#xff0c;外界也就安静了。就像陶渊明说的&#xff1a;心远地自偏。不是走到偏远无人的边荒才能得到片刻清净&#xff0c;不需要使用洪荒之力去挣脱生活的枷锁&#xff0c;这是陶渊明式的中国知识分子的雅量。如果你自己是好的男人或女人…...

APT (Advanced Package Tool) 安装与使用-linux014

APT (Advanced Package Tool) APT (Advanced Package Tool) 是一个用于管理 Debian 和 Ubuntu 系列 Linux 发行版上的软件包的工具。它简化了软件的安装、升级、配置和删除过程。APT 为用户提供了一个统一的命令行接口&#xff0c;使得管理和安装软件变得更加简单。 APT 主要…...

深度学习篇---深度学习中的超参数张量转换模型训练

文章目录 前言第一部分&#xff1a;深度学习中的超参数1. 学习率&#xff08;Learning Rate&#xff09;定义重要性常见设置 2. 批处理大小&#xff08;Batch Size&#xff09;定义重要性常见设置 3. 迭代次数&#xff08;Number of Epochs&#xff09;定义重要性常见设置 4. 优…...

Java设计模式:行为型模式→状态模式

Java 状态模式详解 1. 定义 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为。状态模式通过将状态需要的行为封装在不同的状态类中&#xff0c;实现对象行为的动态改变。该模式的核心思想是分离不同状态…...

快速幂,错位排序笔记

​ 记一下刚学明白的快速幂和错位排序的原理和代码 快速幂 原理&#xff1a; a^b (a^&#xff08;b/2&#xff09;) ^ 2&#xff08;b为偶数&#xff09; a^b a*&#xff08;a^&#xff08; (b-1)/2&#xff09;&#xff09;^2&#xff08;b为奇数&#xff09; 指数为偶数时…...

机器人基础深度学习基础

参考&#xff1a; &#xff08;1&#xff09;【具身抓取课程-1】机器人基础 &#xff08;2&#xff09;【具身抓取课程-2】深度学习基础 1 机器人基础 从平面二连杆理解机器人学 正运动学&#xff1a;从关节角度到末端执行器位置的一个映射 逆运动学&#xff1a;已知末端位置…...

Java语法进阶

目录&#xff1a; Object类、常用APICollection、泛型List、Set、数据结构、CollectionsMap与斗地主案例异常、线程线程、同步等待与唤醒案例、线程池、Lambda表达式File类、递归字节流、字符流缓冲流、转换流、序列化流、Files网络编程 十二、函数式接口Stream流、方法引用 一…...

探索 paraphrase-MiniLM-L6-v2 模型在自然语言处理中的应用

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;将文本数据转换为机器学习模型可以处理的格式是至关重要的。近年来&#xff0c;sentence-transformers 库因其在文本嵌入方面的卓越表现而受到广泛关注。本文将深入探讨 paraphrase-MiniLM-L6-v2 模型&#xff0c;这…...

《chatwise:DeepSeek的界面部署》

ChatWise&#xff1a;DeepSeek的界面部署 摘要 本文详细描述了DeepSeek公司针对其核心业务系统进行的界面部署工作。从需求分析到技术实现&#xff0c;再到测试与优化&#xff0c;全面阐述了整个部署过程中的关键步骤和解决方案。通过本文&#xff0c;读者可以深入了解DeepSee…...

论计算机网络技术专业如何?创新

计算机网络技术专业是顺应数字化时代发展的朝阳专业,前景十分广阔。它聚焦于计算机网络的规划、建设、维护与管理,从基础的网络布线、设备配置,到复杂的网络安全防护、云计算架构搭建,都在专业学习范畴内。该专业毕业生就业面广,可在互联网企业从事网络工程师岗位,负责搭…...

2. 【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--什么是微服务--微服务概述与演变

在软件架构不断演进的今天&#xff0c;微服务架构已成为许多企业构建现代化应用的首选方案。本文将深入探讨微服务的基本概念、演变历程及其出现的背景和推动因素&#xff0c;同时分析当前微服务在业界的应用现状和未来趋势&#xff0c;为读者提供一个全面的视角&#xff0c;理…...

单节锂电池外部供电自动切换的电路学习

文章目录 前言一、原理分析&#xff1a;①当VBUS处有外部电源输入时②当VBUS处无外部电源输入时 二、器件选择1、二极管2、MOS管3、其他 总结 前言 学习一种广泛应用的锂电池供电自动切换电路 电路存在外部电源时&#xff0c;优先使用外部电源供电&#xff0c;并为电池供电&…...

数据结构-堆和PriorityQueue

1.堆&#xff08;Heap&#xff09; 1.1堆的概念 堆是一种非常重要的数据结构&#xff0c;通常被实现为一种特殊的完全二叉树 如果有一个关键码的集合K{k0,k1,k2,...,kn-1}&#xff0c;把它所有的元素按照完全二叉树的顺序存储在一个一维数组中&#xff0c;如果满足ki<k2i…...

如何打造一个更友好的网站结构?

在SEO优化中&#xff0c;网站的结构往往被忽略&#xff0c;但它其实是决定谷歌爬虫抓取效率的关键因素之一。一个清晰、逻辑合理的网站结构&#xff0c;不仅能让用户更方便地找到他们需要的信息&#xff0c;还能提升搜索引擎的抓取效率 理想的网站结构应该像一棵树&#xff0c;…...

每日Attention学习20——Group Shuffle Attention

模块出处 [MICCAI 24] [link] LB-UNet: A Lightweight Boundary-Assisted UNet for Skin Lesion Segmentation 模块名称 Group Shuffle Attention (GSA) 模块作用 轻量特征学习 模块结构 模块特点 使用分组(Group)卷积降低计算量引入External Attention机制更好的学习特征S…...

VUE之组件通信(二)

1、v-model v-model的底层原理&#xff1a;是:value值和input事件的结合 $event到底是啥&#xff1f;啥时候能.target 对于原生事件&#xff0c;$event就是事件对象 &#xff0c;能.target对应自定义事件&#xff0c;$event就是触发事件时&#xff0c;所传递的数据&#xff…...

[x86 ubuntu22.04]进入S4失败

目录 1 问题描述 2 解决过程 2.1 查看内核日志 2.2 新建一个交换分区 2.3 指定交换分区的位置 1 问题描述 CPU&#xff1a;G6900E OS&#xff1a;ubuntu22.04 Kernel&#xff1a;6.8.0-49-generic 使用“echo disk > /sys/power/state”命令进入 S4&#xff0c;但是无法…...