轻量化解析

作为轻量化解析器,目标是从 JSON 字符串中提取键值对、嵌套对象 ({}) 和数组 ([]),同时维持层级和嵌套关系。实现递归解析格式规整的 JSON 字符串。

项目地址: floraison-io/json-cpp: A minimal JSON parser

支持:

  • 嵌套 JSON 对象(多层花括号)

  • 数组嵌套(数组中可以包含数组或对象)

  • 字符串和基本类型

不支持:

  • 处理转义字符,如 \"

  • 键值之间的语法校验,错误恢复。

JSON格式分析

一段标准JSON数据结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"person": {
"name": "Alice",
"age": 25,
"address": {
"city": "Phoenix",
"zip": "10001"
}
},
"primitive": "triangle",
"employee": {
"name": "Bob",
"array": [8,2,4,0,3],
"age": 22,
"address": {
"city": "Paris",
"zip": "11111"
}
}
}

  • **{}** 双括号表示对象;
  • **[]** 中括号表示数组;
  • **""** 双引号内是属性或值;
  • **:** 冒号表示后者是前者的值(这个值可以是字符串、数字、也可以是另一个数组或对象)

类成员和结构概览

1
2
3
4
5
6
class JSON {
std::string jsonStr; // 原始 JSON 字符串
std::map<std::string, std::any> key_value; // 存储解析后的键值对
std::vector<JSON*> subJsons; // 存储嵌套 JSON 对象的指针,便于析构
};

  • 使用了 std::any 存储值,因为 JSON 的 value 类型可以是多种类型(string、array、object、number 等)。

  • 嵌套的 JSON 对象用 subJsons 保存,以便于析构时释放内存。

JSON对象的构造函数

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
JSON::JSON(std::string m_jsonStr)
{
jsonStr = m_jsonStr;
normalize(jsonStr);
jsonStr = jsonStr.substr(1, jsonStr.length() - 2); //remove {}
while (jsonStr.find_first_of(":") != std::string::npos) {
jsonStr = jsonStr.substr(jsonStr.find_first_of("\"") + 1);
std::string key = jsonStr.substr(0, jsonStr.find_first_of("\""));
jsonStr = jsonStr.substr(jsonStr.find_first_of("\"") + 2);
if (jsonStr[0] == '{') { //next JSON Layer
int leftBrace = 1;
int rightBrace = 0;
int i = 1;
for (i = 1; leftBrace != rightBrace; i++) {
if (jsonStr[i] == '{') {
leftBrace++;
}
if (jsonStr[i] == '}') {
rightBrace++;
}
}
std::string subJsonStr = jsonStr.substr(0, i);
jsonStr = jsonStr.substr(i);
JSON* subJson = new JSON(subJsonStr);
subJsons.push_back(subJson);
key_value[key] = subJson;
}
else if (jsonStr[0] == '[') { //next Array Layer
int leftBracket = 1;
int rightBracket = 0;
int i = 1;
for (i = 1; leftBracket != rightBracket; i++) {
if (jsonStr[i] == '[') {
leftBracket++;
}
if (jsonStr[i] == ']') {
rightBracket++;
}
}
std::string subArrStr = jsonStr.substr(0, i);
std::any subArr = parseArray(subArrStr);
key_value[key] = subArr;
jsonStr = jsonStr.substr(i + 1);
}
else if (jsonStr[0] == '\"') { //string
jsonStr = jsonStr.substr(1);
int i = jsonStr.find_first_of('\"');
std::string value = jsonStr.substr(0, i);
key_value[key] = value;
jsonStr = jsonStr.substr(i + 1);
}
else { //others
int i = jsonStr.find_first_of(',');
std::string value = jsonStr.substr(0, i);
key_value[key] = value;
jsonStr = jsonStr.substr(i);
}
}
}

去除空格与换行

1
2
3
4
5
void JSON::normalize(std::string& str) {
str.erase(std::remove_if(str.begin(), str.end(),
[](unsigned char c) { return c == ' ' || c == '\n' || c == '\r'; }),
str.end());
}

解析键值对:以冒号 : 为标志,持续提取 key,然后根据 value 的起始字符判断类型:

  • " 开头:字符串

  • { 开头:子 JSON 对象(递归处理)

  • [ 开头:数组(调用 parseArray

  • 否则:数字、布尔值、null(统统作为字符串处理)

处理嵌套对象/数组的括号匹配:分别对左右括号计数,保证处理的是完整的 JSON 子串/数组。然后递归调用JSON对象构造函数/parseArray函数。

parseArray 函数

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
std::vector<std::any> JSON::parseArray(std::string m_arrStr)
{
std::vector<std::any> arr;
std::string arrStr = m_arrStr.substr(1, m_arrStr.length() - 2);
arrStr += ',';
while (arrStr.find_first_of(",") != std::string::npos) {
if (arrStr[0] == '[') { //sub Array Layer
int leftBracket = 1;
int rightBracket = 0;
int i = 1;
for (i = 1; leftBracket != rightBracket; i++) {
if (arrStr[i] == '[') {
leftBracket++;
}
if (arrStr[i] == ']') {
rightBracket++;
}
}

std::string nextArr = arrStr.substr(0, i);
arrStr = arrStr.substr(i + 1);
arr.push_back(parseArray(nextArr));
continue;
}

if (arrStr[0] == '{') { //sub JSON Layer
int leftBrace = 1;
int rightBrace = 0;
int i = 1;
for (i = 1; leftBrace != rightBrace; i++) {
if (arrStr[i] == '{') {
leftBrace++;
}
if (arrStr[i] == '}') {
rightBrace++;
}
}

std::string subJsonStr = arrStr.substr(0, i);
arrStr = arrStr.substr(i + 1);
JSON* subJson = new JSON(subJsonStr);
subJsons.push_back(subJson);
arr.push_back(subJson);
continue;
}

std::string element = arrStr.substr(0, arrStr.find_first_of(","));
element.erase(std::remove_if(element.begin(), element.end(), [](unsigned char c) { return c == '\"'; }), element.end());
arr.push_back(element);
arrStr = arrStr.substr(arrStr.find_first_of(",") + 1);
}
return arr;
}

为了方便统一处理每个元素,解析过程中临时加了一个 , 到数组末尾。

  • **去除数组的 []**。

  • 循环处理数组中的元素

    • 如果元素是数组:递归调用 parseArray

    • 如果元素是对象:用 new JSON(...) 构造

    • 否则:去除引号并直接作为字符串保存