使用C++解析json字符串

使用C++解析json字符串

项目github地址

解析思路

  • 定义枚举类型, 包括 null, bool, float, int, string, array, object
  • 顺序遍历字符串, 跳过所有的空白字段, 并根据遇到的第一个有效字符尝试各种解析方案
  • 当字符为n时, 尝试解析为null
  • 当字符为tf时, 尝试解析为bool
  • 当字符为-或者数字时, 尝试解析为数值
  • 当字符为"时, 尝试解析为字符串
  • 当字符为[时, 尝试解析为数组
  • 当字符为{时, 尝试解析为对象
  • 当做完一次完整的解析后, 重复这一步骤即可

定义数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enum class json_type {
JSON_NULL,
JSON_BOOL,
JSON_NUMBER,
JSON_INT,
JSON_STRING,
JSON_ARRAY,
JSON_OBJECT
};
struct json_value {
json_type type;
union {
bool b;
double n;
long long i;
string* s;
vector<json_value*>* a;
map<string, json_value*>* o;
};
};

判断类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 辅助函数 判断空格
bool is_space(char c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}
// 辅助函数 跳过空格字符
void skip_space() {
while (pos < len && is_space(input[pos])) pos++;
}
// 辅助函数 判断是否数字
bool is_digit(char c) {
return c >= '0' && c <= '9';
}
// 辅助函数 判断是否英文字母
bool is_alpha(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

尝试解析

首字母为n, 尝试解析为null
根据首字母的类型, 尝试不同的解析方案

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
// 尝试解析为null
json_value* parse_null() {
if (pos + 3 < len && input[pos] == 'n' && input[pos + 1] == 'u' && input[pos + 2] == 'l' && input[pos + 3] == 'l') {
pos += 4;
json_value* v = new json_value();
v->type = json_type::JSON_NULL;
return v;
}
else
return nullptr;
}
// 尝试解析为bool
json_value* parse_bool() {
if (pos + 3 < len && input[pos] == 't' && input[pos + 1] == 'r' && input[pos + 2] == 'u' && input[pos + 3] == 'e') {
pos += 4;
json_value* v = new json_value();
v->type = json_type::JSON_BOOL;
v->b = true;
return v;
}
else if (pos + 4 < len && input[pos] == 'f' && input[pos + 1] == 'a' && input[pos + 2] == 'l' && input[pos + 3] == 's' && input[pos + 4] == 'e') {
pos += 5;
json_value* v = new json_value();
v->type = json_type::JSON_BOOL;
v->b = false;
return v;
}
else
return nullptr;
}
// 尝试解析数值
json_value* parse_number() {
int start = pos; // 记录数字的起始位置
bool is_float = false; // 记录数字是否是浮点数
if (input[pos] == '-') // 跳过负号
pos++;
if (input[pos] == '0') // 跳过零
pos++;
else { // 跳过非零的整数部分
while (pos < len && is_digit(input[pos]))
pos++;
}
if (pos < len && input[pos] == '.') { // 跳过小数点和小数部分
is_float = true;
pos++;
while (pos < len && is_digit(input[pos]))
pos++;
}
if (pos < len && (input[pos] == 'e' || input[pos] == 'E')) { // 跳过指数部分
is_float = true;
pos++;
if (pos < len && (input[pos] == '+' || input[pos] == '-')) // 跳过指数符号
pos++;
while (pos < len && is_digit(input[pos])) // 跳
pos++;
}
if (pos > start) { // 如果解析到了一个数字
json_value* v = new json_value();
string num_str(input + start, pos - start); // 截取数字字符串
if (is_float) { // 如果是浮点数,转换为double
v->type = json_type::JSON_NUMBER;
v->n = stod(num_str);
}
else { // 如果是整数,转换为int
v->type = json_type::JSON_INT;
v->i = stoll(num_str);
}
return v;
}
else
return nullptr;
}
// 尝试解析字符串
json_value* parse_string() {
if (input[pos] == '"') { // 跳过双引号
pos++;
int start = pos; // 记录字符串的起始位置
while (pos < len && input[pos] != '"') // 跳过字符串内容,暂不处理转义字符
pos++;
if (pos < len && input[pos] == '"') { // 跳过双引号
pos++;
json_value* v = new json_value();
v->type = json_type::JSON_STRING;
v->s = new string(input + start, pos - start - 1); // 截取字符串内容
return v;
}
else
return nullptr;
}
else
return nullptr;
}
// 解析数组
json_value* parse_array() {
if (input[pos] == '[') { // 跳过左中括号
pos++;
skip_space(); // 跳过空白字符
json_value* v = new json_value();
v->type = json_type::JSON_ARRAY;
v->a = new vector<json_value*>();
if (input[pos] == ']') { // 处理空数组的情况
pos++;
return v;
}
while (true) { // 循环解析数组元素
json_value* elem = parse_value(); // 解析一个元素值
if (elem) { // 如果解析成功,将元素添加到数组中
v->a->push_back(elem);
skip_space(); // 跳过空白字符
if (input[pos] == ',') { // 如果遇到逗号,跳过并继续解析下一个元素
pos++;
skip_space();
}
else if (input[pos] == ']') { // 如果遇到右中括号,结束解析并返回数组值
pos++;
return v;
}
else { // 如果遇到其他字符,说明格式错误,返回空指针并释放内存
delete v;
return nullptr;
}
}
else { // 如果解析失败,返回空指针并释放内存
delete v;
return nullptr;
}
}
}
else
return nullptr;
}
// 尝试解析object
json_value* parse_object() {
if (input[pos] == '{') { // 跳过左大括号
pos++;
skip_space(); // 跳过空白字符
json_value* v = new json_value();
v->type = json_type::JSON_OBJECT;
v->o = new map<string, json_value*>();
if (input[pos] == '}') { // 处理空对象的情况
pos++;
return v;
}
while (true) { // 循环解析对象成员
json_value* key = parse_string(); // 解析一个键值,必须是字符串类型
if (key) { // 如果解析成功,获取键值对应的字符串内容,并释放内存
string key_str = *(key->s);
delete key;
skip_space(); // 跳过空白字符
if (input[pos] == ':') { // 如果遇到冒号,跳过并继续解析值部分
pos++;
skip_space();
json_value* value = parse_value();
if (value) { // 如果解析成功,将键值和值添加到对象中
v->o->insert({ key_str, value });
skip_space(); // 跳过空白字符
if (input[pos] == ',') { // 如果遇到逗号,跳过并继续解析下一个成员
pos++;
skip_space();
}
else if (input[pos] == '}') { // 如果遇到右大括号,结束解析并返回对象值
pos++;
return v;
}
else { // 如果遇到其他字符,说明格式错误,返回空指针并释放内存
delete v;
return nullptr;
}
}
else { // 如果解析失败,返回空指针并释放内存
delete v;
return nullptr;
}
}
else { // 如果遇到其他字符,说明格式错误,返回空指针并释放内存
delete v;
return nullptr;
}
}
else { // 如果解析失败,返回空指针并释放内存
delete v;
return nullptr;
}
}
}
else
return nullptr;
}

解析入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
json_value* parse_value() {
skip_space(); // 跳过空白字符
if (input[pos] == 'n') // 尝试解析null值
return parse_null();
else if (input[pos] == 't' || input[pos] == 'f') // 尝试解析布尔值
return parse_bool();
else if (input[pos] == '-' || is_digit(input[pos])) // 尝试解析数字值
return parse_number();
else if (input[pos] == '"') // 尝试解析字符串值
return parse_string();
else if (input[pos] == '[') // 尝试解析数组值
return parse_array();
else if (input[pos] == '{') // 尝试解析对象值
return parse_object();
else
return nullptr;// 无法解析,返回空指针
}

解析出一个字段, 就创建一个json_value, 并将其关联到根节点中, 遍历根节点, 就能得到整个json的所有字段及其值