rapidjson::Document 完全指南:从解析到序列化

rapidjson::Document 完全指南:从解析到序列化

RapidJSON 是一个高效的 C++ JSON 解析/生成库,而 rapidjson::Document 是其核心类。本文将详细介绍如何使用 Document 处理 JSON 数据。

什么是 rapidjson::Document?

rapidjson::Document 是 RapidJSON 中表示 JSON 文档的类,它继承自 rapidjson::Value,可以表示任何 JSON 类型(对象、数组、字符串、数字等)。它使用内存池管理内存,具有出色的性能。

1. 基本设置

首先,你需要包含 RapidJSON 头文件并设置命名空间:

1
2
3
4
5
6
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

using namespace rapidjson;

2. 解析 JSON 字符串

2.1 基本解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document document;
document.Parse(json);

if (document.HasParseError()) {
std::cerr << "解析错误! 错误码: " << document.GetParseError() << std::endl;
std::cerr << "错误偏移: " << document.GetErrorOffset() << std::endl;
return;
}

// 检查是否为对象类型
if (document.IsObject()) {
std::cout << "成功解析 JSON 对象!" << std::endl;
}

2.2 解析选项

1
2
3
4
5
6
// 设置解析选项
ParseResult result = document.Parse<kParseCommentsFlag | kParseTrailingCommasFlag>(json);
if (!result) {
std::cerr << "JSON 解析错误: " << GetParseError_En(result.Code())
<< " (偏移: " << result.Offset() << ")" << std::endl;
}

3. 访问 JSON 数据

3.1 访问对象成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 方法1: 使用 HasMember() 和 operator[]
if (document.HasMember("project") && document["project"].IsString()) {
std::cout << "项目名称: " << document["project"].GetString() << std::endl;
}

if (document.HasMember("stars") && document["stars"].IsInt()) {
std::cout << "星标数: " << document["stars"].GetInt() << std::endl;
}

// 方法2: FindMember() 更高效
Value::ConstMemberIterator itr = document.FindMember("stars");
if (itr != document.MemberEnd() && itr->value.IsInt()) {
std::cout << "星标数: " << itr->value.GetInt() << std::endl;
}

// 方法3: 使用 GetXXX 的快捷方法
std::cout << "项目名称: " << document["project"].GetString() << std::endl;
std::cout << "星标数: " << document["stars"].GetInt() << std::endl;

3.2 访问数组元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const char* jsonArray = "[1, 2, 3, 4, 5]";
Document docArray;
docArray.Parse(jsonArray);

if (docArray.IsArray()) {
// 方法1: 使用 Size() 和 operator[]
for (SizeType i = 0; i < docArray.Size(); i++) {
if (docArray[i].IsInt()) {
std::cout << "元素 " << i << ": " << docArray[i].GetInt() << std::endl;
}
}

// 方法2: 使用迭代器
for (Value::ConstValueIterator itr = docArray.Begin(); itr != docArray.End(); ++itr) {
if (itr->IsInt()) {
std::cout << "值: " << itr->GetInt() << std::endl;
}
}

// 方法3: 基于范围的 for 循环 (C++11)
for (auto& v : docArray.GetArray()) {
if (v.IsInt()) {
std::cout << "值: " << v.GetInt() << std::endl;
}
}
}

4. 创建和修改 JSON

4.1 创建 JSON 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Document d;
d.SetObject();

// 添加字符串成员
Value name("rapidjson");
d.AddMember("name", name, d.GetAllocator());

// 添加整数成员
Value version(1);
d.AddMember("version", version, d.GetAllocator());

// 添加布尔成员
Value isCool(true);
d.AddMember("is_cool", isCool, d.GetAllocator());

// 添加 double 类型
Value score(9.5);
d.AddMember("score", score, d.GetAllocator());

// 添加 null 值
Value nullValue(kNullType);
d.AddMember("null_field", nullValue, d.GetAllocator());

4.2 创建嵌套结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Document d(kObjectType);
Document::AllocatorType& allocator = d.GetAllocator();

// 创建数组
Value tags(kArrayType);
tags.PushBack("json", allocator);
tags.PushBack("parser", allocator);
tags.PushBack("c++", allocator);
d.AddMember("tags", tags, allocator);

// 创建嵌套对象
Value author(kObjectType);
author.AddMember("name", "Tencent", allocator);
author.AddMember("url", "https://github.com/Tencent/rapidjson", allocator);
d.AddMember("author", author, allocator);

// 创建复杂数组
Value contributors(kArrayType);
Value contributor1(kObjectType);
contributor1.AddMember("name", "Milo Yip", allocator);
contributor1.AddMember("commits", 1000, allocator);

Value contributor2(kObjectType);
contributor2.AddMember("name", "其他贡献者", allocator);
contributor2.AddMember("commits", 500, allocator);

contributors.PushBack(contributor1, allocator);
contributors.PushBack(contributor2, allocator);
d.AddMember("contributors", contributors, allocator);

4.3 修改现有 JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 假设已有 JSON
const char* originalJson = "{\"name\":\"test\",\"value\":10}";
Document doc;
doc.Parse(originalJson);

// 修改值
if (doc.HasMember("value")) {
doc["value"] = 20; // 修改整数值
}

// 添加新成员
doc.AddMember("new_field", "new value", doc.GetAllocator());

// 删除成员
doc.RemoveMember("name");

// 检查并修改嵌套值
if (doc.HasMember("author") && doc["author"].IsObject()) {
doc["author"]["name"] = "Updated Name";
}

5. 序列化 JSON

5.1 转换为字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 方法1: 使用 StringBuffer 和 Writer
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
document.Accept(writer);

std::string jsonStr = buffer.GetString();
std::cout << "JSON 字符串: " << jsonStr << std::endl;

// 方法2: 格式化输出(美化)
StringBuffer prettyBuffer;
PrettyWriter<StringBuffer> prettyWriter(prettyBuffer);
prettyWriter.SetIndent(' ', 2); // 设置缩进:2个空格
document.Accept(prettyWriter);

std::cout << "美化后的 JSON:\n" << prettyBuffer.GetString() << std::endl;

5.2 直接获取字符串值

1
2
3
4
5
6
7
8
9
10
// 对于简单类型,可以直接获取值
if (document.HasMember("name") && document["name"].IsString()) {
const char* name = document["name"].GetString();
// 注意:这里的指针在 document 销毁前有效
}

// 如果需要副本,可以创建 std::string
if (document.HasMember("name") && document["name"].IsString()) {
std::string nameStr = document["name"].GetString();
}

6. 实用技巧和最佳实践

6.1 类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Value& value = document["some_key"];

// 类型检查方法
if (value.IsNull()) { /* 处理 null */ }
if (value.IsBool()) { /* 处理布尔值 */ }
if (value.IsInt()) { /* 处理整数 */ }
if (value.IsUint()) { /* 处理无符号整数 */ }
if (value.IsInt64()) { /* 处理 64 位整数 */ }
if (value.IsUint64()) { /* 处理无符号 64 位整数 */ }
if (value.IsDouble()) { /* 处理浮点数 */ }
if (value.IsString()) { /* 处理字符串 */ }
if (value.IsArray()) { /* 处理数组 */ }
if (value.IsObject()) { /* 处理对象 */ }

// 获取类型名称
std::cout << "类型: " << value.GetType() << std::endl;

6.2 安全访问模式

1
2
3
4
5
6
7
8
9
10
11
// 使用指针访问,避免异常
const Value* namePtr = Pointer("/project").Get(document);
if (namePtr && namePtr->IsString()) {
std::cout << "项目: " << namePtr->GetString() << std::endl;
}

// 使用 GetValueByPointer
Value* authorName = GetValueByPointer(document, "/author/name");
if (authorName && authorName->IsString()) {
std::cout << "作者: " << authorName->GetString() << std::endl;
}

6.3 内存管理

1
2
3
4
5
6
7
8
9
10
11
12
13
// 重用 Document 对象
Document doc;
for (int i = 0; i < 100; i++) {
const char* json = "{\"id\":1,\"data\":\"test\"}";
doc.Parse(json);
// 处理数据...
doc.Clear(); // 清空内容,但保留分配器内存
}

// 使用栈分配器提高性能
char buffer[4096]; // 栈缓冲区
MemoryPoolAllocator<CrtAllocator> stackAllocator(buffer, sizeof(buffer));
Document docWithStackAlloc(&stackAllocator, sizeof(buffer));

7. 完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
#include <string>

int main() {
// 1. 解析 JSON
const char* json = R"({
"name": "RapidJSON",
"version": "1.1.0",
"features": ["fast", "small", "header-only"],
"author": {
"name": "Milo Yip",
"organization": "Tencent"
},
"stars": 12000
})";

rapidjson::Document doc;
doc.Parse(json);

if (doc.HasParseError()) {
std::cerr << "解析失败!" << std::endl;
return 1;
}

// 2. 读取数据
std::cout << "库名称: " << doc["name"].GetString() << std::endl;
std::cout << "版本: " << doc["version"].GetString() << std::endl;
std::cout << "星标数: " << doc["stars"].GetInt() << std::endl;

// 3. 修改数据
doc["stars"] = 12500;
doc.AddMember("license", "MIT", doc.GetAllocator());

// 4. 创建新对象
rapidjson::Value maintainers(rapidjson::kArrayType);

rapidjson::Value maintainer1(rapidjson::kObjectType);
maintainer1.AddMember("name", "Milo Yip", doc.GetAllocator());
maintainer1.AddMember("email", "miloyip@gmail.com", doc.GetAllocator());
maintainers.PushBack(maintainer1, doc.GetAllocator());

rapidjson::Value maintainer2(rapidjson::kObjectType);
maintainer2.AddMember("name", "Other Contributors", doc.GetAllocator());
maintainers.PushBack(maintainer2, doc.GetAllocator());

doc.AddMember("maintainers", maintainers, doc.GetAllocator());

// 5. 序列化
rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
writer.SetIndent(' ', 2);
doc.Accept(writer);

std::cout << "\n修改后的 JSON:\n" << buffer.GetString() << std::endl;

return 0;
}

8. 常见陷阱

  1. 内存管理: RapidJSON 使用移动语义,复制 Value 时可能转移所有权
  2. 引用有效性: 从 Document 获取的指针/引用在 Document 销毁后无效
  3. 类型安全: 始终检查类型后再调用 GetXXX() 方法
  4. 编码问题: RapidJSON 默认使用 UTF-8,确保输入数据正确编码

总结

rapidjson::Document 是 RapidJSON 的核心类,提供了完整的 JSON 处理能力。通过合理使用解析、访问、修改和序列化功能,你可以高效地处理 JSON 数据。记住始终进行错误检查和类型验证,这是编写健壮代码的关键。

希望这篇指南能帮助你更好地使用 RapidJSON!如有问题,请参考官方文档:https://rapidjson.org/


rapidjson::Document 完全指南:从解析到序列化
https://www.psnow.sbs/2025/12/25/rapidjson-Document-完全指南:从解析到序列化/
作者
Psnow
发布于
2025年12月26日
许可协议