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

本文主要详细介绍如何使用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;}// 读取 costif (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;}// 读取 rateif (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数组中每个pointfor (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
其他信息: 空

在这里插入图片描述

以上综合示例的相关文件,我已经放在了本文的绑定附件中,有需要可以自行获取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/495334.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TGRS | 可变形傅里叶卷积用于遥感道路分割

题目&#xff1a;Fourier-Deformable Convolution Network for Road Segmentation From Remote Sensing Images 期刊&#xff1a;IEEE Transactions on Geoscience and Remote Sensing 论文&#xff1a;https://ieeexplore.ieee.org/document/10707598/ 代码&#xff1a;htt…

Linux复习4——shell与文本处理

认识vim编辑器 #基本语法格式&#xff1a; vim 文件名 •如果文件存在&#xff0c;进入编辑状态对其进行编辑 •如果文件不存在&#xff0c;创建文件并进入编辑状态 例&#xff1a; [rootlocalhosttest]# vim practice.txt #Vim 编辑器三种模式&#xff1a; 命令模式&a…

GIT与github的链接(同步本地与远程仓库)

1.官网下载GIT Git - 安装 Git 2.GIT生成密钥 2.1 打开gitbash配置邮箱与用户名&#xff08;非初次使用GIT跳过这一步&#xff09; git config --global user.name "你的用户名" git config --global user.email "你的邮箱" 2.2 生成ssh密匙 1&#xff0…

小程序租赁系统开发指南与实现策略

内容概要 在如今这个快节奏的时代&#xff0c;小程序租赁系统的开发正逐渐成为许多商家提升服务质量与效率的重要选择。在设计这样一个系统时&#xff0c;首先要明白它的核心目标&#xff1a;便捷、安全。用户希望在最短的时间内找到需要的物品&#xff0c;而商家则希望通过这…

深度学习之超分辨率算法——FRCNN

– 对之前SRCNN算法的改进 输出层采用转置卷积层放大尺寸&#xff0c;这样可以直接将低分辨率图片输入模型中&#xff0c;解决了输入尺度问题。改变特征维数&#xff0c;使用更小的卷积核和使用更多的映射层。卷积核更小&#xff0c;加入了更多的激活层。共享其中的映射层&…

vue3项目history路由模式部署上线405、刷新404问题(包括部分页面刷新404问题)

一、找不到js模块 解决方法&#xff1a;配置Nginx配置文件&#xff1a; // root /your/program/path/dist root /www/wwwroot/my_manage_backend_v1/dist;二、刷新页面导致404问题(Not found) 经过一系列配置后发现进入页面一切正常&#xff0c;包括路由前进和回退&#xff0…

微服务篇-深入了解 XXL-JOB 分布式任务调度的具体使用(XXL-JOB 的工作流程、框架搭建)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 XXL-JOB 调度中心概述 1.2 XXL-JOB 工作流程 1.3 Cron 表达式调度 2.0 XXL-JOB 框架搭建 2.1 XXL-JOB 调度中心的搭建 2.2 XXL-JOB 执行器的搭建 2.3 使用调度中心…

JS中若干相似特性的区别

Object.is与的区别? 其他时候都相等 字符串concat()和号有什么区别? 数组at和直接索引区别 at里是负值,计算方法是:数组的长度加这个负值,得到的数作为索引值 substring与slice的区别 substring是负值,则视为0,等于全部复制 slice是负值,则从后往前复制,-2就是复制最后2个字…

Fuel库实战:下载失败时的异常处理策略

Fuel库作为一个轻量级的Kotlin HTTP客户端库&#xff0c;因其简洁的API和强大的功能而受到开发者的青睐。然而&#xff0c;网络请求总是伴随着失败的风险&#xff0c;比如网络不稳定、服务器错误、资源不存在等。因此&#xff0c;合理地处理这些异常情况对于提升用户体验和应用…

vscode插件更新特别慢的问题

点击插件标题去网页查看 命令行安装 D:\Software\VSCode\Code.exe --extensions-dir "D:\Software\VSCode\extendions" --install-extension Vue.volar-2.2.0.vsix安装完成之后重启vs code即可 参考 https://www.cnblogs.com/yiquanfeng/p/18218722

2.利用docker进行gitlab服务器迁移

一、Docker安装 安装Ubuntu 22.04.3 LTS \n \l 1、旧版本安装包清理 sudo apt-get remove docker docker-engine docker.io containerd runc当你卸载Docker时&#xff0c;存储在/var/lib/docker/中的图像、容器、卷和网络不会自动删除。如果你想从一个干净的安装开始&#x…

大型语言模型(LLMs)演化树 Large Language Models

大型语言模型&#xff08;LLMs&#xff09;演化树 Large Language Models flyfish 下面的图来自论文地址 Transformer 模型&#xff08;如 BERT 和 GPT-3&#xff09;已经给自然语言处理&#xff08;NLP&#xff09;领域带来了革命性的变化。这得益于它们具备并行化能力&…

springboot477基于vue技术的农业设备租赁系统(论文+源码)_kaic

摘 要 使用旧方法对农业设备租赁系统的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在农业设备租赁系统的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发的农…

如何在 Ubuntu 22.04 上安装和使用 Composer

简介 如果你是一名 PHP 开发者&#xff0c;想要简化你的项目依赖管理&#xff0c;那么 Composer 是一个必不可少的工具。Composer 可以简化包管理&#xff0c;并允许你轻松地将外部库集成到你的项目中。 本教程将向你展示如何在 Ubuntu 22.04 操作系统上安装 Composer&#x…

16_HTML5 语义元素 --[HTML5 API 学习之旅]

HTML5 引入了许多新的语义元素&#xff0c;这些元素有助于创建结构更清晰、更具描述性的网页。语义化 HTML 不仅改善了代码的可读性&#xff0c;还增强了搜索引擎优化&#xff08;SEO&#xff09;&#xff0c;提高了无障碍访问性&#xff0c;并使得开发者更容易理解和维护代码。…

国标GB28181视频监控平台与Liveweb视频监控汇聚平台对接方案

应急管理部门以“以信息化推动应急管理能力现代化”为总体目标&#xff0c;加快现代信息技术与应急管理业务深度融合&#xff0c;全面支持现代应急管理体系建设&#xff0c;这不仅是国家加强和改进应急管理工作的关键举措&#xff0c;也是应对日益严峻的应急管理形势和满足公众…

内部知识库的未来展望:技术融合与用户体验的双重升级

在当今数字化飞速发展的时代&#xff0c;企业内部知识库作为知识管理的关键载体&#xff0c;正站在变革的十字路口&#xff0c;即将迎来技术融合与用户体验双重升级的崭新时代&#xff0c;这一系列变化将深度重塑企业知识管理的格局。 一、技术融合&#xff1a;开启知识管理新…

EasyGBS国标GB28181公网平台P2P远程访问故障诊断:云端服务端排查指南

随着信息技术的飞速发展&#xff0c;视频监控领域正经历从传统安防向智能化、网络化安防的深刻转变。EasyGBS平台&#xff0c;作为基于国标GB28181协议的视频流媒体平台&#xff0c;为用户提供了强大的视频监控直播功能。然而&#xff0c;在实际应用中&#xff0c;P2P远程访问可…

HW护网分析研判思路,流量告警分析技巧

《网络安全自学教程》 这篇文章&#xff0c;写给每一个「护网黑奴」&#xff0c;为初次护网的小伙伴普及一下护网工作内容&#xff0c;提供一些简单的分析思路。 护网分析研判思路 1、护网组织架构和责任划分1.1、安全监控1.2、分析研判1.3、应急处置 2、分析研判2.1、判断告警…

springBoot发布https服务及调用

一、服务端发布https服务 1、准备SSL证书 &#xff08;1&#xff09;自签名证书&#xff1a;如果你只是用于开发或测试环境&#xff0c;可以生成一个自签名证书。 &#xff08;2&#xff09;CA 签名证书&#xff1a;对于生产环境&#xff0c;应该使用由受信任的证书颁发机构 …