使用RapidJson解析Json数据

  1. 1. 基于RapidJson解析Json数据
    1. 1.1. RapidJson简介
    2. 1.2. DOM风格API使用一览
    3. 1.3. C++对象序列化
    4. 1.4. Json文件的输入与输出

基于RapidJson解析Json数据

RapidJson简介

RapidJson是腾讯开源的一个C++的JSON解析器及生成器,它的灵感来自RapidXml。RapidJson性能快且独立,不依赖外部库甚至不依赖STL。它是跨平台的,可以在MSVC(2008/2010/2013),GNU C++(3.8)以及Clang(3.4)平台运行。

RapidJson使用起来也很方便。它是只有头文件的C++库,只需要把include/rapidjson目录复制到系统或项目中即可使用。RapidJson源码下载地址:RapidJson下载

DOM风格API使用一览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

using namespace rapidjson;

// 1. Parse a JSON string into DOM.
const char* json = R"({"project":"rapidjson", "stars":10})";
Document d;
d.Parse(json);

// 2. Modify it by DOM.
Value& s = d["stars"];
s.SetInt(s.GetInt() + 1);

// 3. Stringify the DOM
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);

// Output {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl;

以上代码是将字符串与DOM相互转化的过程,其执行过程为:
过程

C++对象序列化

首先创建几个class,这些class最重要的就是要实现自己的序列化方法,为了统一,所有类的序列化方法都为Serialize

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//serial.h
#include "../../RapidJson/prettywriter.h"
#include <cstdio>
#include <string>
#include <vector>

using namespace rapidjson;

class Person {
public:
Person(const std::string &name, unsigned age) : name_(name), age_(age) {}

Person(const Person &rhs) : name_(rhs.name_), age_(rhs.age_) {}

virtual ~Person();

Person &operator=(const Person &rhs) {
name_ = rhs.name_;
age_ = rhs.age_;
return *this;
}

protected:
template<typename Writer>
void Serialize(Writer &writer) const {
//这是一个基类 该Serialize方法不会直接被外界调用,因为只需要一个最简单的实现,而不必以对象的形式实现,可以如下其它类的实现作比较
writer.String("name");
#if RAPIDJSON_HAS_STDSTRING
writer.String(name_);
#else
writer.String(name_.c_str(), static_cast<SizeType>(name_.length())); // Supplying length of string is faster.
#endif
writer.String("age");
writer.Uint(age_);
}

private:
std::string name_;
unsigned age_;
};


class Education {
public:
Education(const std::string &school, double GPA) : school_(school), GPA_(GPA) {}

Education(const Education &rhs) : school_(rhs.school_), GPA_(rhs.GPA_) {}

Education &operator=(const Education &rhs) {
school_ = rhs.school_;
GPA_ = rhs.GPA_;
return *this;
}

template<typename Writer>
void Serialize(Writer &writer) const {
writer.StartObject();

writer.String("school");
#if RAPIDJSON_HAS_STDSTRING
writer.String(school_);
#else
writer.String(school_.c_str(), static_cast<SizeType>(school_.length()));
#endif

writer.String("GPA");
writer.Double(GPA_);

writer.EndObject();
}

private:
std::string school_;
double GPA_;
};

class Dependent : public Person {
public:
Dependent(const std::string &name, unsigned age, Education *education = 0) : Person(name, age),
education_(education) {}

Dependent(const Dependent &rhs) : Person(rhs), education_(nullptr) {
education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_);
}

~Dependent();

Dependent &operator=(const Dependent &rhs) {
if (this == &rhs)
return *this;
delete education_;
education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_);
return *this;
}

template<typename Writer>
void Serialize(Writer &writer) const {
writer.StartObject();

Person::Serialize(writer);

writer.String("education");
if (education_)
education_->Serialize(writer);
else
writer.Null();

writer.EndObject();
}

private:
Education *education_;
};


class Employee : public Person {
public:
Employee(const std::string &name, unsigned age, bool married) : Person(name, age), married_(married) {}

Employee(const Employee &rhs) : Person(rhs), dependents_(rhs.dependents_), married_(rhs.married_) {}

virtual ~Employee();

Employee &operator=(const Employee &rhs) {
if (this == &rhs)
return *this;
static_cast<Person &>(*this) = rhs;
dependents_ = rhs.dependents_;
married_ = rhs.married_;
return *this;
}

void AddDependent(const Dependent &dependent) {
dependents_.push_back(dependent);
}

template<typename Writer>
void Serialize(Writer &writer) {
writer.StartObject();
Person::Serialize(writer);
writer.String("married");
writer.Bool(married_);

writer.String("dependents");
writer.StartArray();
for (const auto &item: dependents_)
item.Serialize(writer);
writer.EndArray();

writer.EndObject();

}

private:
std::vector<Dependent> dependents_;
bool married_;
};

int testSerial();

//serial.cpp

#include "../../../Header/example/serial/serial.h"

Dependent::~Dependent() {
delete education_;
}

Person::~Person() {}

Employee::~Employee() {}

//测试方法
int testSerial() {
std::vector<Employee> employees;
employees.push_back(Employee("Lihua", 34, true));
employees.back().AddDependent(Dependent("Lua", 3, new Education("Happy Kindergarten", 3.5)));
employees.back().AddDependent(Dependent("Mio", 1));

employees.push_back(Employee("Percy", 30, false));

StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
writer.StartArray();
for (auto &item: employees)
//将employees的内容序列化到sb中
item.Serialize(writer);
writer.EndArray();
puts(sb.GetString());
return 0;
}

调用testSerial方法,输出为:

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
[
{
"name": "Lihua",
"age": 34,
"married": true,
"dependents": [
{
"name": "Lua",
"age": 3,
"education": {
"school": "Happy Kindergarten",
"GPA": 3.5
}
},
{
"name": "Mio",
"age": 1,
"education": null
}
]
},
{
"name": "Percy",
"age": 30,
"married": false,
"dependents": []
}
]

Json文件的输入与输出

上文介绍了如何将C++对象序列化,接下来会以此为基础,将json字符串写到文件中,并从文件中读取,解析,看以下示例:

  1. 将封装的一些函数声明在头文件中;
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
//fileReadAndOutput.h
#include "../../../Header/RapidJson/filereadstream.h"
#include "../../../Header/RapidJson/filewritestream.h"
#include "../../RapidJson/document.h"
#include "../../../Header/RapidJson/writer.h"
#include <cstdio>

using namespace rapidjson;

/*!
* 修改要写入/读取的文件名
* @param name 要写入/读取的文件名
*/
void setFileName(char *name);

/*!
* 读取Json文件并返回一个Document对象
*/
Document readJsonFromFile();

/*!
* 将Document写入文件中
* @param document 目标对象
*/
void writeJsonToFile(const Document &document);

///测试方法
void testReadWriteJsonWithFile();
  1. 函数实现;
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//fileReadAndOutput.cpp
#include <vector>
#include <iostream>
#include "../../../Header/example/file/fileReadAndOutput.h"
#include "../../../Header/example/serial/serial.h"

using namespace rapidjson;

char *fileName = "JsonData.json";

void setFileName(char *name) {
fileName = name;
}

Document readJsonFromFile() {
FILE *fp = fopen(fileName, "rb");
char readBuffer[65536];
FileReadStream is(fp, readBuffer, sizeof(readBuffer));

Document d;
d.ParseStream(is);

fclose(fp);
return d;
}

void writeJsonToFile(const Document &document) {

FILE *fp = fopen(fileName, "wb");

char writeBuffer[65536];
FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer));

//写入的更规范,但性能会降低
PrettyWriter<FileWriteStream> writer(os);
document.Accept(writer);

fclose(fp);
}

void testReadWriteJsonWithFile() {
setFileName("demo.json");
std::vector<Employee> employees;
employees.push_back(Employee("Lihua", 34, true));
employees.back().AddDependent(Dependent("Lua", 3, new Education("Happy Kindergarten", 3.5)));
employees.back().AddDependent(Dependent("Mio", 1));

employees.push_back(Employee("Percy", 50, false));

StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
writer.StartArray();
for (auto &item: employees)
item.Serialize(writer);
writer.EndArray();
puts(sb.GetString());

//以上 JSON数据放入sb中

std::cout << "解析数据到Document开始...." << std::endl;
Document doc;
doc.Parse(sb.GetString());
std::cout << "解析数据到Document完成...." << std::endl;

std::cout << "写数据到文件开始...." << std::endl;
writeJsonToFile(doc);
std::cout << "写数据到文件完成...." << std::endl;

std::cout << "读文件数据开始...." << std::endl;
auto d = readJsonFromFile();
std::cout << "读文件数据完成...." << std::endl;

std::cout << "输出所读数据...." << std::endl;
StringBuffer readBuffer;
PrettyWriter<StringBuffer> readBuffer1(readBuffer);
d.Accept(readBuffer1);
puts(readBuffer.GetString());
}
  1. 最后,调用testReadWriteJsonWithFile进行测试;

结语,RapidJson还有很多特性可以使用,本文只是浅显的介绍了用法,更深入学习请访问开篇的下载地址。