首页 前端知识 详细介绍如何使用rapidjson读取json文件

详细介绍如何使用rapidjson读取json文件

2025-03-01 12:03:39 前端知识 前端哥 256 959 我要收藏

本文主要详细介绍如何使用rapidjson库来实现.json文件的读取,分为相关基础介绍、结合简单示例进行基础介绍、结合复杂示例进行详细的函数实现介绍等三部分。

在这里插入图片描述


一、相关基础


1、Json文件中的{}[]

在 JSON 文件中,{}[] 分别表示不同的数据结构:


(1) {} 表示对象(Object)
  • 语义{} 表示键值对的集合。
  • 结构:每个键是一个字符串,键与值之间用冒号(:)分隔,键值对之间用逗号(,)分隔。
  • 特点
    • 键是唯一的,值可以是任意类型(如字符串、数字、布尔值、数组、对象等)。
    • 对象类似于 C++ 中的 std::map 或 Python 中的字典(dict)。

示例:

{
"name": "John",
"age": 30,
"married": true
}
复制
  • 解析:
    • name 是键,值为字符串 "John"
    • age 是键,值为整数 30
    • married 是键,值为布尔值 true

(2) [] 表示数组(Array)
  • 语义[] 表示有序的值列表。
  • 结构值之间用逗号(,)分隔,值可以是任意类型(如字符串、数字、布尔值、数组、对象等)。
  • 特点
    • 数组中的值没有键,只有索引(从 0 开始)。
    • 数组类似于 C++ 中的 std::vector 或 Python 中的列表(list)。

示例:

[10, 20, 30, 40]
复制
  • 解析:
    • 数组包含 4 个整数值:10203040

(3)综合示例

一个 JSON 文件可以同时包含对象和数组的嵌套:

{
"person": {
"name": "Alice",
"age": 25,
"hobbies": ["reading", "cycling", "traveling"]
},
"scores": [85, 90, 78]
}
复制
  • 解析
    • person 是一个对象,包含键值对:
      • name: "Alice"(字符串)
      • age: 25(整数)
      • hobbies: 一个数组,包含 3 个字符串。
    • scores 是一个数组,包含 3 个整数。

(4)总结

  • {}:用于表示对象(键值对的集合)。
  • []:用于表示数组(有序值列表)。

2、常用函数

JSON 文件中常用的函数分类及详细列表如下所示,基于 RapidJSON 库进行了分类整理。

注:下面示例中的doc是用来存储解析后的JSON数据的主要对象,可通过rapidjson::Document doc声明


(1) 文件处理相关
函数名函数用途使用示例
ParseStream解析输入流中的 JSON 数据doc.ParseStream(isw);
Parse解析字符串中的 JSON 数据doc.Parse(jsonStr.c_str());
HasParseError检查解析是否出错if (doc.HasParseError()) { /* 错误处理 */ }

(2)解析与验证相关
函数名函数用途使用示例
HasMember检查对象是否包含指定键if (doc.HasMember("key")) { /* 存在键 */ }
IsObject检查 JSON 节点是否为对象if (doc["key"].IsObject()) { /* 是对象 */ }
IsArray检查 JSON 节点是否为数组if (doc["key"].IsArray()) { /* 是数组 */ }
IsString检查 JSON 节点是否为字符串if (doc["key"].IsString()) { /* 是字符串 */ }
IsInt检查 JSON 节点是否为整数if (doc["key"].IsInt()) { /* 是整数 */ }
IsDouble检查 JSON 节点是否为浮点数if (doc["key"].IsDouble()) { /* 是浮点数 */ }
IsBool检查 JSON 节点是否为布尔值if (doc["key"].IsBool()) { /* 是布尔值 */ }
IsNull检查 JSON 节点是否为空if (doc["key"].IsNull()) { /* 是空值 */ }
函数名函数用途使用示例
IsNumber检查 JSON 节点是否为数字(整数或浮点数)if (doc["key"].IsNumber()) { /* 是数字 */ }
IsUint检查 JSON 节点是否为无符号整数if (doc["key"].IsUint()) { /* 是无符号整数 */ }
IsUint64检查 JSON 节点是否为 64 位无符号整数if (doc["key"].IsUint64()) { /* 是64位无符号整数 */ }
IsInt64检查 JSON 节点是否为 64 位整数if (doc["key"].IsInt64()) { /* 是64位整数 */ }
IsLosslessDouble检查浮点数是否能无损转换为整数if (doc["key"].IsLosslessDouble()) { /* 无损转换 */ }

(3) 对象操作相关
函数名函数用途使用示例
operator[]获取对象中指定键的值auto value = doc["key"];
MemberBegin获取对象的第一个键值对迭代器for (auto itr = obj.MemberBegin(); itr != obj.MemberEnd(); ++itr) { std::cout << itr->name.GetString(); }
MemberEnd获取对象的最后一个键值对迭代器同上

(4)数组操作相关
函数名函数用途使用示例
operator[]获取数组中指定索引的值auto value = doc["key"][0];
Begin获取数组的第一个元素迭代器for (auto itr = array.Begin(); itr != array.End(); ++itr) { std::cout << itr->GetInt(); }
End获取数组的最后一个元素迭代器同上
Size获取数组大小size_t len = doc["key"].Size();

(5) 获取操作相关
函数名函数用途使用示例
GetString获取字符串值std::string str = doc["key"].GetString();
GetInt获取整数值int val = doc["key"].GetInt();
GetDouble获取浮点数值(包括 float 类型)double val = doc["key"].GetDouble();
GetBool获取布尔值bool flag = doc["key"].GetBool();
GetArray获取数组值(作为数组对象)const auto& arr = doc["key"].GetArray();
GetObject获取对象值(作为对象)const auto& obj = doc["key"].GetObject();
函数名函数用途使用示例
GetUint获取无符号整数值unsigned int val = doc["key"].GetUint();
GetInt64获取 64 位整数值int64_t val = doc["key"].GetInt64();
GetUint64获取 64 位无符号整数值uint64_t val = doc["key"].GetUint64();
关于浮点数
  • 基础的RapidJSON 中没有 GetFloat 函数。

  • 浮点数统一通过 GetDouble 获取。即使是 JSON 文件中的浮点数值可以存储为单精度(float),也会被解释为双精度(double)。

  • 若需要 float 类型的值,可以使用强制类型转换:

    float val = static_cast<float>(doc["key"].GetDouble());
    复制
  • 即使在某些RapidJSON中,对 GetFloat 函数也进行了封装,比如我使用的这个版本的document.h文件中,进行了以下封装,所以也可以直接用 GetFloat 函数。但其本质还是通过GetDouble 函数获取,然后进行类型转换后返回的。

[[_resources/详细介绍如何使用rapidjson读取json文件/bc2fcd9db0921f81d29d3018268dc70e_MD5.jpeg|Open: Pasted image 20241206092810.png]]
![[_resources/详细介绍如何使用rapidjson读取json文件/bc2fcd9db0921f81d29d3018268dc70e_MD5.jpeg]]

float GetFloat() const {
return static_cast<float>(GetDouble());
}
复制

综合示例

示例JSON文件如下:

{
"Info": {
"author": "JZX-MY",
"number": 7,
},
"regions": [
{
"name": "Area 1",
"points": [
{
"x": 1.0,
"y": 2.0
},
{
"x": 3.0,
"y": 4.0
}
]
},
{
"name": "Area 2",
"points": [
{
"x": 5.0,
"y": 6.0
},
{
"x": 7.0,
"y": 8.0
}
]
}
]
}
复制

读取该示例JSON文件的代码片段如下:

#include <iostream>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <fstream>
int main() {
// 读取 JSON 文件
std::ifstream ifs("data.json");
rapidjson::IStreamWrapper isw(ifs);
// 解析 JSON 文件
rapidjson::Document doc;
doc.ParseStream(isw);
// 检查是否存在错误
if (doc.HasParseError()) {
std::cerr << "解析失败!" << std::endl;
return -1;
}
// 获取对象中的值
std::string author = doc["Info"]["author"].GetString();
int number = doc["Info"]["number"].GetInt();
// 遍历数组
const auto& regions = doc["regions"].GetArray();
for (const auto& region : regions) {
std::cout << "Region Name: " << region["name"].GetString() << std::endl;
const auto& points = region["points"].GetArray();
for (const auto& point : points) {
std::cout << " Point: (" << point["x"].GetDouble() << ", " << point["y"].GetDouble() << ")" << std::endl;
}
}
return 0;
}
复制

二、结合简单示例进行详细的基础介绍

0. 示例

本部分内容以读取如下所示非常简单的test.json文件为例

{
"points": [-17, -15, 0],
"cost": 10,
"rate": 0.6,
}
复制

对应的读取示例程序如下:

bool readJson(std::string& file_path, int& cost, double& occ_th, double& free_th) {
std::string read_path = file_path + "/test.json";
std::cout << "Read Json: " << read_path << std::endl;
// 加载 JSON 文件
std::ifstream ifs(read_path);
if (!ifs.is_open()) {
std::cout << " could not open " << read_path << std::endl;
return false;
}
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document doc;
doc.ParseStream(isw);
if (doc.HasParseError()) {
std::cerr << "Error JSON file " << read_path << std::endl;
return false;
}
// 解析 points 数组
if (doc.HasMember("points") && doc["points"].IsArray() && doc["points"].Size() == 3) {
points_.x() = doc["points"][0].GetDouble();
points_.y() = doc["points"][1].GetDouble();
std::cout << "points x: " << points_.x() << " points y: " << points_.y()
<< " points th: " << doc["points"][2].GetDouble() << std::endl;
} else {
std::cerr << "Invalid points data in map_.json" << std::endl;
return false;
}
// 读取 cost
if (doc.HasMember("cost") && doc["cost"].IsInt()) {
cost = doc["cost"].GetInt();
std::cout << "cost: " << cost << std::endl;
} else {
std::cerr << "The JSON does not contain a cost tag or it is invalid." << std::endl;
return false;
}
// 读取 rate
if (doc.HasMember("rate") && doc["rate"].IsDouble()) {
occ_th = doc["rate"].GetDouble();
std::cout << "rate: " << occ_th << std::endl;
} else {
std::cerr << "The JSON does not contain an rate tag or it is invalid." << std::endl;
return false;
}
return true;
}
复制

在上述代码中,函数 readJson 使用了 RapidJSON 读取和解析一个 JSON 文件。以下是分步骤的详细介绍,以及如何正确使用 RapidJSON 读取 JSON 文件并提取数据:


1. 准备工作

  • 路径声明:std::string read_path = file_path + “/test.json”;
    • 定义要解析的 JSON 文件路径。

示例如下:

std::string read_path = file_path + "/test.json";
std::cout << "Read Json: " << read_path << std::endl;
复制
  • 加载文件:通过 std::ifstream 打开文件流,确保文件可用。
    • 如果文件无法打开,返回错误。

示例如下:

std::ifstream ifs(read_path);
if (!ifs.is_open()) {
std::cout << "test.json could not open " << read_path << std::endl;
return false;
}
复制

2. 创建 RapidJSON 的流包装器

RapidJSON 使用 IStreamWrapper 将标准的 C++ 输入流包装为可供 RapidJSON 解析的流。

rapidjson::IStreamWrapper isw(ifs);
复制
  • 文档对象rapidjson::Document doc 是用来存储解析后 JSON 数据的主要对象。
  • 解析 JSON 流:通过 doc.ParseStream(isw) 解析 JSON 文件内容。
rapidjson::Document doc;
doc.ParseStream(isw);
if (doc.HasParseError()) {
std::cerr << "Error JSON file " << read_path << std::endl;
return false;
}
复制

3. 验证和提取 JSON 数据

(1)验证 JSON 数据 【非必要】
  • 可以使用 doc.HasParseError() 检查 JSON 文件的解析是否出错。
  • 对每个 JSON 字段,使用 doc.HasMember("key") 检查该字段是否存在,并验证字段类型,例如 IsArray()IsDouble() 等。

(2)解析 JSON 字段
{
"points": [-17, -15, 0],
"cost": 10,
"rate": 0.6,
}
复制
数组字段解析示例——points 字段解析

已知points 是一个含有3个数字的数组字段,这里代码验证json文件中是否含有points成员、它是否为数组,长度是否为 3,若验证成功,则提取数据,并保存至合适的变量中(示例中保存到了类内变量中)。

对于名为points的数组字段,且数据类型为double,可以使用 doc[ "points" ][ 0 ].GetDouble();来读取其第一个元素,以此类推

if (doc.HasMember("points") && doc["points"].IsArray() && doc["points"].Size() == 3) {
points_.x() = doc["points"][0].GetDouble();
points_.y() = doc["points"][1].GetDouble();
std::cout << "points x: " << points_.x() << " points y: " << points_.y()
<< " points th: " << doc["points"][2].GetDouble() << std::endl;
} else {
std::cerr << "Invalid points data in map_.json" << std::endl;
return false;
}
复制
整数字段解析示例——cost 字段解析

cost 是一个整数字段,代码doc.HasMember(“cost”) 验证是否含有成员cost, 使用 IsInt() 验证其类型是否为int,并通过 GetInt() 获取值。

if (doc.HasMember("cost") && doc["cost"].IsInt()) {
cost = doc["cost"].GetInt();
std::cout << "cost: " << cost << std::endl;
} else {
std::cerr << "The JSON does not contain a cost tag or it is invalid." << std::endl;
return false;
}
复制
浮点数字段解析示例——rate 字段解析

rate 是一个浮点数字段,代码使用 IsDouble() 验证类型,并通过 GetDouble() 获取值。

if (doc.HasMember("rate") && doc["rate"].IsDouble()) {
occ_th = doc["rate"].GetDouble();
std::cout << "rate: " << occ_th << std::endl;
} else {
std::cerr << "The JSON does not contain an rate tag or it is invalid." << std::endl;
return false;
}
复制

4. 返回解析结果

在所有字段都成功解析后,函数返回 true,否则返回 false


5. 总结 RapidJSON 的使用

  • 文件读取:通过 std::ifstreamrapidjson::IStreamWrapper 将文件内容传递给 RapidJSON。
  • 文档解析:通过 rapidjson::DocumentParseStream 将 JSON 数据加载到内存中。
  • 字段验证:使用 HasMember 和字段类型检查确保数据完整性。
  • 字段提取:通过 Get<Type>() 函数提取具体数据。

这种方法适用于处理结构明确的 JSON 文件,提供高效且安全的解析能力。

三、结合复杂示例进行详细的函数实现介绍

本部分以如下所示的示例为例子展开介绍

{
"Info": {
"author": "JZX_MY",
"version": "v1.0.96",
"number": 66,
"position": [-9, 60.0, 10.0],
"rate": 0.99
},
"scale": 77,
"regions": [
{
"index": 0,
"name": "free_regions",
"points": [
{
"x": -2.03,
"y": 2.25
},
{
"x": 1.39,
"y": 5.82
},
{
"x": 7.47,
"y": 2.35
},
{
"x": 5.50,
"y": -1.36
}
]
},
{
"index": 1,
"name": "occupy_regions",
"points": [
{
"x": -2.03,
"y": 27.25
},
{
"x": 10.39,
"y": 5.82
},
{
"x": 78.47,
"y": 2.35
}
]
}
],
"param": {
"max_threshold": 0.05,
"max_num" : 10240,
"success" : true
},
"robot": {
"width": 1510,
"length": 5180,
"radius": 3420
},
"other": []
}
复制

1、编写通用的基本读取框架

根据第二部分的介绍,我们先编写读取一个JSON所需的一些基本框架,如下所示,

/**
* 使用rapidJSON解析JSON文件的函数
* @param directory_path 文件所在的目录路径
* @param file_name 文件名
*/
bool readJSON(const std::string& directory_path, const std::string& file_name) {
// (1)拼接完整的文件路径
std::string full_file_path = directory_path + "/" + file_name;
std::cout << "Read Json file: " << full_file_path << std::endl;
// (2)打开JSON文件流
std::ifstream ifs(full_file_path);
if (!ifs.is_open()) {
// 如果文件无法打开,输出错误信息并返回
std::cerr << "could not open: " << full_file_path << std::endl;
return false;
}
// (3)使用rapidJSON的IStreamWrapper包装文件流,方便解析
rapidjson::IStreamWrapper isw(ifs);
// (4)定义rapidJSON的Document对象,用于存储解析后的JSON数据
rapidjson::Document doc;
doc.ParseStream(isw);
// (5)检查JSON文件解析是否成功
if (doc.HasParseError()) {
// 如果解析失败,输出错误信息并返回
std::cerr << "Error JSON file!" << std::endl;
return false;
}
// ------------------------开始读取------------------------------
// 开始逐个解析读取JSON字段 [后面的步骤补充]
...
...
...
...
...
...
// ------------------------读取完成------------------------------
// 关闭文件流
ifs.close();
return true
}
复制

其详细介绍如下:

(1)为了方便对多个类似结构的文件进行读取,下面的示例采用了将文件所在路径directory_path和文件名file_name以形参传入的方式,将其拼接后得到完整的要读取的JSON文件的路径full_file_path

// (1)拼接完整的文件路径
std::string full_file_path = directory_path + "/" + file_name;
std::cout << "Read Json file: " << full_file_path << std::endl;
复制

(2)使用std::ifstream(输入文件流)创建一个文件流对象ifs。传入full_file_path作为参数,表示要打开的文件路径。std::ifstream会尝试打开指定路径的文件,并准备从文件中读取数据。【注:文件路径需要是完整路径,只有当路径正确且文件存在时,文件才能成功打开,没有指定模式时,默认以只读模式打开文件(std::ios::in)】

// (2)打开JSON文件流
std::ifstream ifs(full_file_path);
if (!ifs.is_open()) {
// 如果文件无法打开,输出错误信息并返回
std::cerr << "could not open: " << full_file_path << std::endl;
return false;
}
复制

(3)使用RapidJSON 的IStreamWrapper 将标准的 C++ 输入流对象ifs包装为可供 RapidJSON 解析的流isw。

// (3)使用rapidJSON的IStreamWrapper包装文件流,方便解析
rapidjson::IStreamWrapper isw(ifs);
复制

(4)定义rapidJSON的Document对象doc,用于存储解析后的JSON数据文档对象,然后通过 doc.ParseStream(isw) 解析 JSON 文件内容。

// (4)定义rapidJSON的Document对象,用于存储解析后的JSON数据
rapidjson::Document doc;
doc.ParseStream(isw);
复制

(5)检查JSON文件解析是否成功

// (5)检查JSON文件解析是否成功
if (doc.HasParseError()) {
// 如果解析失败,输出错误信息并返回
std::cerr << "Error JSON file!" << std::endl;
return false;
}
复制

(6)若解析成功,则开始根据JSON文件的结构内容,依次进行读取【在下一节中介绍】

(7)成功读取完成后,关闭C++文件流ifs,并返回true

到这里读取一个JSON文件的常用框架就介绍完了,下面根据示例JSON文件的结构补充读取部分

2、在通用框架的基础上补充实际要读取的内容

(1)示例中的Info部分

首先回顾第一部分中的内容:{} 是对象,表示键值对的集合。每个键是一个字符串,键与值之间用冒号(:)分隔,键值对之间用逗号(,)分隔。键是唯一的,值可以是任意类型(如字符串、数字、布尔值、数组、对象等)。[] 是数组,表示有序的值列表。值之间用逗号(,)分隔,值可以是任意类型(如字符串、数字、布尔值、数组、对象等)。 数组中的值没有键,只有索引(从 0 开始)。对象和数组可以互相嵌套,产生一些比较复杂的结构

本部分内容的结构相对简单,Info作为总的JSON文件对象的一个键值对中的键,值是一个对象,该对象中又包含了author、version、number、position、rate这五个键值对,除了position之外,其他键值对的值都是单个数据,position的值又是一个数组,数组中每个元素也都是普通的单个数据,没有再嵌套下去。

{
"Info": {
"author": "JZX_MY",
"version": "v1.0.96",
"number": 66,
"position": [-9, 60.0, 10.0],
"rate": 0.99
}
}
复制
我们先来看Info对象的第一个键值对"author": “JZX_MY”,常见的读取方式有以下三种
第一种:通过最简化的直接链式访问

可以直接通过链式访问来读取,如下所示,根据第一部分中常用函数的介绍,读取字符串类型用GetString()函数即可

std::string author = doc["Info"]["author"].GetString();
std::cout << "Author: " << author << std::endl;
复制

在确定 JSON 的结构是固定的,并且不会缺少任何字段的情况下,可以直接使用类似于 doc["Info"]["author"]的链式访问方式,代码更简洁。缺点是,如果 Info 不存在、不是对象,或者 author 不存在、不是字符串,则会导致程序崩溃。

第二种:通过分层安全访问

这种方式的核心是进行逐步检查,确保每一步的操作都符合预期的条件,然后再执行具体的逻辑。不会因为 JSON 结构问题导致程序崩溃。 能够在异常情况(例如字段缺失或类型不匹配)时,自定义处理逻辑(如打印错误信息)。但代码更长,显得繁琐。如下所示:

// 检查 JSON 对象是否包含名为 "Info" 的成员,并且该成员是一个对象
if (doc.HasMember("Info") && doc["Info"].IsObject()) {
// 获取 "Info" 成员,并存储为一个常量引用
const rapidjson::Value& info = doc["Info"];
// 检查 "Info" 对象是否包含名为 "author" 的成员,并且该成员是一个字符串
if (info.HasMember("author") && info["author"].IsString()) {
// 获取 "author" 成员的字符串值,并存储到变量 author 中
std::string author = info["author"].GetString();
// 输出 "author" 的值到控制台
std::cout << "Author: " << author << std::endl;
}
}
复制
第三种:通过链式安全访问

可以将前两种结合一下,先检查,然后再通过链式访问,省去了临时中间变量’const rapidjson::Value& info = doc[“Info”];’

// 检查 JSON 对象是否包含名为 "Info" 的成员,并且该成员是一个对象
if (doc.HasMember("Info") && doc["Info"].IsObject()) {
// 直接链式访问检查 "Info" 对象中的 "author" 成员
if (doc["Info"].HasMember("author") && doc["Info"]["author"].IsString()) {
// 直接链式获取 "author" 的字符串值
std::string author = doc["Info"]["author"].GetString();
// 输出 "author" 的值到控制台
std::cout << "Author: " << author << std::endl;
}
}
复制

该方式虽然看起来稍微简洁了一些,但是 每次链式访问都会重新解析路径,因此可能会稍微影响性能,尤其在大规模或复杂 JSON 时。使用临时中间变量(如 info)会在逻辑上缓存某个子节点的引用,从而提高性能。如果后续多次访问 "Info" 的内容,建议保留 const rapidjson::Value& info 方式以减少重复解析。【即第二种方式】

综合来看,第二种分层安全访问的方式是最值得推荐的,version、number、rate项与author项类似(换成对应类型的函数即可),这里就不展开介绍了,我们再来看一下position项,给出带检查与不带检查的写法示例
第一种:通过最简化的直接链式访问

如果完全确定数据结构正确,并希望最简化代码,可以直接链式访问:

float x = doc["Info"]["position"][0].GetFloat();
float y = doc["Info"]["position"][1].GetFloat();
float z = doc["Info"]["position"][2].GetFloat();
std::cout << "Position: (" << x << ", " << y << ", " << z << ")" << std::endl;
复制

缺点

  • 缺乏安全性检查,一旦 JSON 数据结构有误(例如 position 缺失、不是数组或者长度不足),会导致程序崩溃。

第二种:通过分层安全访问

这种方法逐层检查 JSON 的结构,确保安全性:

if (doc.HasMember("Info") && doc["Info"].IsObject()) {
const rapidjson::Value& info = doc["Info"]; // 获取 "Info" 对象
if (info.HasMember("position") && info["position"].IsArray()) {
const rapidjson::Value& position = info["position"]; // 获取 "position" 数组
// 检查数组长度并逐个读取元素
if (position.Size() == 3) { // 假设数组有3个元素
float x = position[0].GetFloat(); // 获取第一个元素
float y = position[1].GetFloat(); // 获取第二个元素
float z = position[2].GetFloat(); // 获取第三个元素
// 输出读取结果
std::cout << "Position: (" << x << ", " << y << ", " << z << ")" << std::endl;
} else {
std::cerr << "Position array size is not 3!" << std::endl;
}
} else {
std::cerr << "Position does not exist or is not an array!" << std::endl;
}
}
复制

优点

  • 每一步都进行了显式检查,适用于不确定 JSON 数据结构是否完全符合预期的情况。
  • 对数组的操作更清晰,容易扩展到更复杂的逻辑。

带检查的Info部分读取示例(仅打印未存储):
// 解析Info部分
if (document.HasMember("Info")) { // 检查是否包含"Info"字段
const rapidjson::Value& info = doc["Info"];
if (info.IsObject()) { // 确保"Info"是一个对象
std::cout << "Info 部分解析结果:" << std::endl;
if (info.HasMember("author") && info["author"].IsString()) {
std::cout << "作者: " << info["author"].GetString() << std::endl; // 输出作者名称
}
if (info.HasMember("version") && info["version"].IsString()) {
std::cout << "版本: " << info["version"].GetString() << std::endl; // 输出版本号
}
if (info.HasMember("number") && info["number"].IsInt()) {
std::cout << "编号: " << info["number"].GetInt() << std::endl; // 输出编号
}
if (info.HasMember("position") && info["position"].IsArray()) {
std::cout << "位置: ";
for (auto& pos : info["position"].GetArray()) {
std::cout << pos.GetFloat() << " "; // 输出位置坐标
}
std::cout << std::endl;
}
if (info.HasMember("rate") && info["rate"].IsDouble()) {
std::cout << "速率: " << info["rate"].GetDouble() << std::endl; // 输出速率
}
}
}
复制
(2)示例中的scale部分

本部分结构很简单,scale作为总的JSON文件对象的一个键值对中的键,值是一个普通的单个数据,没有嵌套。

{
"scale": 77
}
复制
直接访问:
int scale = doc["scale"].GetInt();
std::cout << "Scale: " << scale << std::endl;
复制
安全的访问:
if (doc.HasMember("scale") && doc["scale"].IsInt()) {
int scale = doc["scale"].GetInt(); // 读取 "scale" 的值
std::cout << "Scale: " << scale << std::endl; // 输出结果
} else {
std::cerr << "Scale is not present or not an integer!" << std::endl;
}
复制
(3)示例中的regions部分

本部分结构相对复杂一点,regions作为总的JSON文件对象的一个键值对中的键,值是一个数组,数组中每个成员又是一个对象,该对象中含有index、name、points三个成员,其中points的值又是一个数组,数组中每个成员又是一个对象,该对象包含x和y两个键值对,值为普通变量,嵌套结束。

{
"regions": [
{
"index": 0,
"name": "free_regions",
"points": [
{
"x": -2.03,
"y": 2.25
},
{
"x": 1.39,
"y": 5.82
},
{
"x": 7.47,
"y": 2.35
},
{
"x": 5.50,
"y": -1.36
}
]
},
{
"index": 1,
"name": "occupy_regions",
"points": [
{
"x": -2.03,
"y": 27.25
},
{
"x": 10.39,
"y": 5.82
},
{
"x": 78.47,
"y": 2.35
}
]
}
]
}
复制
第一种:最简洁的无检查写法
// 遍历regions数组中每个region
for (const auto& region : doc["regions"].GetArray()) {
std::cout << "Region index: " << region["index"].GetInt() << std::endl;
std::cout << "Region name: " << region["name"].GetString() << std::endl;
// 遍历points数组中每个point
for (const auto& point : region["points"].GetArray()) {
std::cout << " Point: (x: " << point["x"].GetDouble()
<< ", y: " << point["y"].GetDouble() << ")" << std::endl;
}
}
复制
第二种:逐层检查的最安全的写法
if (doc.HasMember("regions") && doc["regions"].IsArray()) {
const rapidjson::Value& regions = doc["regions"];
for (rapidjson::SizeType i = 0; i < regions.Size(); ++i) {
const rapidjson::Value& region = regions[i];
if (region.HasMember("index") && region["index"].IsInt()) {
int index = region["index"].GetInt();
std::cout << "Region index: " << index << std::endl;
}
if (region.HasMember("name") && region["name"].IsString()) {
std::string name = region["name"].GetString();
std::cout << "Region name: " << name << std::endl;
}
if (region.HasMember("points") && region["points"].IsArray()) {
const rapidjson::Value& points = region["points"];
for (rapidjson::SizeType j = 0; j < points.Size(); ++j) {
const rapidjson::Value& point = points[j];
if (point.HasMember("x") && point["x"].IsDouble() &&
point.HasMember("y") && point["y"].IsDouble()) {
double x = point["x"].GetDouble();
double y = point["y"].GetDouble();
std::cout << " Point: (x: " << x << ", y: " << y << ")" << std::endl;
}
}
}
}
}
复制
第三种:仅针对顶层结构检查的的写法
if (doc.HasMember("regions") && doc["regions"].IsArray()) {
for (const auto& region : doc["regions"].GetArray()) {
int index = region["index"].GetInt();
std::cout << "Region index: " << index << std::endl;
std::string name = region["name"].GetString();
std::cout << "Region name: " << name << std::endl;
for (const auto& point : region["points"].GetArray()) {
double x = point["x"].GetDouble();
double y = point["y"].GetDouble();
std::cout << " Point: (x: " << x << ", y: " << y << ")" << std::endl;
}
}
}
复制
(4)示例中的param部分
{
"param": {
"max_threshold": 0.05,
"max_num" : 10240,
"success" : true
}
}
复制
第一种:无检查写法
const rapidjson::Value& param = doc["param"];
double max_threshold = param["max_threshold"].GetDouble();
int max_num = param["max_num"].GetInt();
bool success = param["success"].GetBool();
std::cout << "Max Threshold: " << max_threshold << std::endl;
std::cout << "Max Num: " << max_num << std::endl;
std::cout << "Success: " << (success ? "true" : "false") << std::endl;
复制

double max_threshold = doc["param"]["max_threshold"].GetDouble();
int max_num = doc["param"]["max_num"].GetInt();
bool success = doc["param"]["success"].GetBool();
std::cout << "Max Threshold: " << max_threshold << std::endl;
std::cout << "Max Num: " << max_num << std::endl;
std::cout << "Success: " << (success ? "true" : "false") << std::endl;
复制
第二种:带检查写法
if (doc.HasMember("param") && doc["param"].IsObject()) {
const rapidjson::Value& param = doc["param"];
if (param.HasMember("max_threshold") && param["max_threshold"].IsDouble()) {
double max_threshold = param["max_threshold"].GetDouble();
std::cout << "Max Threshold: " << max_threshold << std::endl;
}
if (param.HasMember("max_num") && param["max_num"].IsInt()) {
int max_num = param["max_num"].GetInt();
std::cout << "Max Num: " << max_num << std::endl;
}
if (param.HasMember("success") && param["success"].IsBool()) {
bool success = param["success"].GetBool();
std::cout << "Success: " << (success ? "true" : "false") << std::endl;
}
}
复制
(5)示例中的robot部分
{
"robot": {
"width": 1510,
"length": 5180,
"radius": 3420
}
}
复制
第一种:无检查写法
const rapidjson::Value& robot = doc["robot"];
int width = robot["width"].GetInt();
int length = robot["length"].GetInt();
int radius = robot["radius"].GetInt();
std::cout << "Width: " << width << std::endl;
std::cout << "Length: " << length << std::endl;
std::cout << "Radius: " << radius << std::endl;
复制

int width = doc["robot"]["width"].GetInt();
int length = doc["robot"]["length"].GetInt();
int radius = doc["robot"]["radius"].GetInt();
std::cout << "Width: " << width << std::endl;
std::cout << "Length: " << length << std::endl;
std::cout << "Radius: " << radius << std::endl;
复制
第二种:带检查写法
if (doc.HasMember("robot") && doc["robot"].IsObject()) {
const rapidjson::Value& robot = doc["robot"];
if (robot.HasMember("width") && robot["width"].IsInt()) {
int width = robot["width"].GetInt();
std::cout << "Width: " << width << std::endl;
}
if (robot.HasMember("length") && robot["length"].IsInt()) {
int length = robot["length"].GetInt();
std::cout << "Length: " << length << std::endl;
}
if (robot.HasMember("radius") && robot["radius"].IsInt()) {
int radius = robot["radius"].GetInt();
std::cout << "Radius: " << radius << std::endl;
}
}
复制
(6)示例中的other部分
{
"other": []
}
复制
第一种:仅检查数组是否为空
const rapidjson::Value& other = doc["other"];
if (other.Empty()) {
std::cout << "The 'other' array is empty." << std::endl;
} else {
for (rapidjson::SizeType i = 0; i < other.Size(); ++i) {
std::cout << "Element " << i << ": " << other[i].GetString() << std::endl;
}
}
复制
第二种:逐层检查写法
if (doc.HasMember("other") && doc["other"].IsArray()) {
const rapidjson::Value& other = doc["other"];
// 检查数组是否为空
if (other.Empty()) {
std::cout << "The 'other' array is empty." << std::endl;
} else {
// 遍历数组元素(如果有的话)
for (rapidjson::SizeType i = 0; i < other.Size(); ++i) {
std::cout << "Element " << i << ": " << other[i].GetString() << std::endl;
}
}
}
复制

3、完整的程序示例 & 运行

(1)将以下内容存储为main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include "rapidjson/document.h"
#include "rapidjson/istreamwrapper.h"
/**
* 使用rapidJSON解析JSON文件的函数
* @param directory_path 文件所在的目录路径
* @param file_name 文件名
*/
void parseJSON(const std::string& directory_path, const std::string& file_name) {
// 拼接完整的文件路径,判断 directory_path 是否为空,
std::string full_file_path = directory_path.empty()
? file_name //若为空,说明要读取的json文件与当前可执行文件在同一目录下,则直接将file_name作为路径
: (directory_path.back() == '/' // 判断 directory_path 的最后是否有 '/',若有则直接拼接 file_name,否则加上 '/'
? directory_path + file_name
: directory_path + "/" + file_name);
// 打开JSON文件流
std::ifstream ifs(full_file_path);
if (!ifs.is_open()) {
// 如果文件无法打开,输出错误信息并返回
std::cerr << "无法打开文件: " << full_file_path << std::endl;
return;
}
// 使用rapidJSON的IStreamWrapper包装文件流,方便解析
rapidjson::IStreamWrapper isw(ifs);
// 定义rapidJSON的Document对象,用于存储解析后的JSON数据
rapidjson::Document document;
document.ParseStream(isw);
// 检查JSON文件解析是否成功
if (document.HasParseError()) {
// 如果解析失败,输出错误信息并返回
std::cerr << "JSON解析错误!" << std::endl;
return;
}
// 开始逐个解析JSON字段
// 解析"Info"部分
if (document.HasMember("Info")) { // 检查是否存在"Info"字段
const rapidjson::Value& info = document["Info"];
if (info.IsObject()) { // 确保"Info"是一个对象类型
std::cout << "Info 部分解析结果:" << std::endl;
// 读取作者信息
if (info.HasMember("author") && info["author"].IsString()) {
std::cout << "作者: " << info["author"].GetString() << std::endl; // 输出作者名称
}
// 读取版本号
if (info.HasMember("version") && info["version"].IsString()) {
std::cout << "版本: " << info["version"].GetString() << std::endl; // 输出版本号
}
// 读取编号
if (info.HasMember("number") && info["number"].IsInt()) {
std::cout << "编号: " << info["number"].GetInt() << std::endl; // 输出编号
}
// 读取位置数组
if (info.HasMember("position") && info["position"].IsArray()) {
std::cout << "位置: ";
for (auto& pos : info["position"].GetArray()) {
std::cout << pos.GetFloat() << " "; // 遍历并输出位置坐标
}
std::cout << std::endl;
}
// 读取速率
if (info.HasMember("rate") && info["rate"].IsDouble()) {
std::cout << "速率: " << info["rate"].GetDouble() << std::endl; // 输出速率
}
}
}
// 解析"scale"部分
if (document.HasMember("scale") && document["scale"].IsInt()) {
std::cout << "缩放比例: " << document["scale"].GetInt() << std::endl; // 输出缩放比例
}
// 解析"regions"部分
if (document.HasMember("regions") && document["regions"].IsArray()) {
std::cout << "区域解析:" << std::endl;
const rapidjson::Value& regions = document["regions"];
for (const auto& region : regions.GetArray()) { // 遍历区域数组
// 输出区域索引
if (region.HasMember("index") && region["index"].IsInt()) {
std::cout << "区域索引: " << region["index"].GetInt() << std::endl;
}
// 输出区域名称
if (region.HasMember("name") && region["name"].IsString()) {
std::cout << "区域名称: " << region["name"].GetString() << std::endl;
}
// 输出区域点坐标
if (region.HasMember("points") && region["points"].IsArray()) {
std::cout << "区域点坐标:" << std::endl;
for (const auto& point : region["points"].GetArray()) {
if (point.HasMember("x") && point["x"].IsDouble() &&
point.HasMember("y") && point["y"].IsDouble()) {
std::cout << "x: " << point["x"].GetDouble() << ", y: " << point["y"].GetDouble() << std::endl;
}
}
}
}
}
// 解析"param"部分
if (document.HasMember("param") && document["param"].IsObject()) {
std::cout << "参数解析:" << std::endl;
const rapidjson::Value& param = document["param"];
// 输出最大阈值
if (param.HasMember("max_threshold") && param["max_threshold"].IsDouble()) {
std::cout << "最大阈值: " << param["max_threshold"].GetDouble() << std::endl;
}
// 输出最大数量
if (param.HasMember("max_num") && param["max_num"].IsInt()) {
std::cout << "最大数量: " << param["max_num"].GetInt() << std::endl;
}
// 输出成功状态
if (param.HasMember("success") && param["success"].IsBool()) {
std::cout << "是否成功: " << (param["success"].GetBool() ? "是" : "否") << std::endl;
}
}
// 解析"robot"部分
if (document.HasMember("robot") && document["robot"].IsObject()) {
std::cout << "机器人参数:" << std::endl;
const rapidjson::Value& robot = document["robot"];
// 输出机器人宽度
if (robot.HasMember("width") && robot["width"].IsInt()) {
std::cout << "宽度: " << robot["width"].GetInt() << std::endl;
}
// 输出机器人长度
if (robot.HasMember("length") && robot["length"].IsInt()) {
std::cout << "长度: " << robot["length"].GetInt() << std::endl;
}
// 输出机器人半径
if (robot.HasMember("radius") && robot["radius"].IsInt()) {
std::cout << "半径: " << robot["radius"].GetInt() << std::endl;
}
}
// 解析"other"部分
if (document.HasMember("other") && document["other"].IsArray()) {
std::cout << "其他信息: " << (document["other"].Empty() ? "空" : "存在内容") << std::endl;
}
// 关闭文件流
ifs.close();
}
int main() {
// 调用解析函数,传入JSON文件所在目录路径和文件名
parseJSON("", "test.json");
return 0;
}
复制
(2)在与main.cpp的同一目录下,将以下内容存储为test.json
{
"Info": {
"author": "JZX_MY",
"version": "v1.0.96",
"number": 66,
"position": [-9, 60.0, 10.0],
"rate": 0.99
},
"scale": 77,
"regions": [
{
"index": 0,
"name": "free_regions",
"points": [
{
"x": -2.03,
"y": 2.25
},
{
"x": 1.39,
"y": 5.82
},
{
"x": 7.47,
"y": 2.35
},
{
"x": 5.50,
"y": -1.36
}
]
},
{
"index": 1,
"name": "occupy_regions",
"points": [
{
"x": -2.03,
"y": 27.25
},
{
"x": 10.39,
"y": 5.82
},
{
"x": 78.47,
"y": 2.35
}
]
}
],
"param": {
"max_threshold": 0.05,
"max_num" : 10240,
"success" : true
},
"robot": {
"width": 1510,
"length": 5180,
"radius": 3420
},
"other": []
}
复制
(3)在存储以上两个文件的目录下的终端运行以下指令进行编译
g++ main.cpp -o main
复制
(4)继续在该终端下,运行以下指令,运行程序
./main
复制
(5)可以看到如下所示的运行结果
Info 部分解析结果:
作者: JZX_MY
版本: v1.0.96
编号: 66
位置: -9 60 10
速率: 0.99
缩放比例: 77
区域解析:
区域索引: 0
区域名称: free_regions
区域点坐标:
x: -2.03, y: 2.25
x: 1.39, y: 5.82
x: 7.47, y: 2.35
x: 5.5, y: -1.36
区域索引: 1
区域名称: occupy_regions
区域点坐标:
x: -2.03, y: 27.25
x: 10.39, y: 5.82
x: 78.47, y: 2.35
参数解析:
最大阈值: 0.05
最大数量: 10240
是否成功: 是
机器人参数:
宽度: 1510
长度: 5180
半径: 3420
其他信息: 空
复制

在这里插入图片描述

以上综合示例的相关文件,我已经放在了本文的绑定附件中,有需要可以自行获取。
转载请注明出处或者链接地址:https://www.qianduange.cn//article/22124.html
标签
评论
发布的文章

算法002——复写零

2025-03-02 13:03:05

github上传代码(自用)

2025-03-02 13:03:59

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!