cjson库解析json文件

利用cJSON库来解析json文件

cJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。

#include <cJSON.h>使用前需要引用这个库

同时将csjon.c放到工程目录

JSON语法规则

JSON对象是一个无序的”名称/值”键值对的集合:

  • 以”{“开始,以”}“结束,允许嵌套使用;
  • 每个名称和值成对出现,名称和值之间使用”:“分隔;
  • 键值对之间用”,“分隔
  • 在这些字符前后允许存在无意义的空白符;

json常见对象类型

1
2
3
4
5
6
7
8
9
10
11
cjson对应json对象给出的类型宏

#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */

对于键值,可以有如下值:)

  • 一个新的json对象(cJSON_Object
  • 数组:使用”[“和”]“表示(cJSON_Array
  • 数字:直接表示,可以是整数,也可以是浮点数(cJSON_Number
  • 字符串:使用引号"表示(cJSON_String
  • 字面值:false、null、true中的一个(必须是小写)( cJSON_Invalid,cJSON_False,cJSON_True,cJSON_NULL)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "mculover666", //键为name,值为string
"age": 22, //值为number
"weight": 55.5,
"address": //address是一个对象object,里面有俩个属性
{
"country": "China",
"zip-code": 111111
},
"skill": ["c", "Java", "Python"], //skill是一个数组对象,里面是string
"student": false //这就是一个字面值false
}


cJson结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct cJSON
{

struct cJSON *next;
struct cJSON *prev;
struct cJSON *child;

int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
} cJSON;

首先,它不是将一整段JSON数据抽象出来,而是将其中的一条JSON数据抽象出来,也就是一个键值对,用上面的结构体 strcut cJSON 来表示,其中用来存放值的成员列表如下:

  • String:用于表示该键值对的名称;就是对象的名称

  • type:用于表示该键值对中值的类型;

  • valuestring:如果键值类型(type)是字符串,则将该指针指向键值;

  • valueint:如果键值类型(type)是整数,则将该指针指向键值;

  • valuedouble:如果键值类型(type)是浮点数,则将该指针指向键值;

其次,一段完整的JSON数据中由很多键值对组成,并且涉及到键值对的查找、删除、添加,所以使用链表来存储整段JSON数据,如上面的代码所示:

  • next指针:指向下一个键值对
  • prev指针指向上一个键值对

最后,因为JSON数据支持嵌套,所以一个键值对的值会是一个新的JSON数据对象(一条新的链表),也有可能是一个数组,方便起见,在cJSON中,数组也表示为一个数组对象,用链表存储,所以:

在键值对结构体中,当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child指针指向该条新链表。


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
[{
"interval": 1,
"rewards": 100,
"spawn_list": [{
"interval": 1,
"point": 1,
"enemy": "Slim"
}, {
"interval": 1,
"point": 2,
"enemy": "KingSlim"
}, {
"interval": 1,
"point": 1,
"enemy": "Goblin"
}, {
"interval": 1,
"point": 2,
"enemy": "GoblinPriest"
}, {
"interval": 1,
"point": 1,
"enemy": "Skeleton"
}]
}, {
"interval": 3,
"rewards": 100,
"spawn_list": [{
"interval": 3,
"point": 2,
"enemy": "KingSlim"
}]
}]
//本身整体就是一个大数组,大数组里面有俩个{}{}对象其实是波次,波次里面还有三个对象,间隔,奖励,敌人列表(又是数组)
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
bool load_level_config(const std::string& path)
{
std::fstream file(path);
if (!file.good())return false;

std::stringstream str_stream;
str_stream << file.rdbuf();
file.close();

cJSON* json_root = cJSON_Parse(str_stream.str().c_str());
if (!json_root) return false;


if (json_root->type != cJSON_Array)
{
cJSON_Delete(json_root);
return false;
}
cJSON* json_wave = nullptr;

cJSON_ArrayForEach(json_wave, json_root)
{
if (json_wave->type != cJSON_Object)
continue;

wave_list.emplace_back();
Wave& wave = wave_list.back();


cJSON* json_wave_rewards = cJSON_GetObjectItem(json_wave, "rewards");
if (json_wave_rewards && json_wave_rewards->type == cJSON_Number)
wave.rewards = json_wave_rewards->valuedouble;

cJSON* json_wave_interval = cJSON_GetObjectItem(json_wave, "interval");
if (json_wave_interval && json_wave_interval->type == cJSON_Number)
wave.interval = json_wave_interval->valuedouble;


cJSON* json_wave_spawn_list = cJSON_GetObjectItem(json_wave, "spawn_list");
if (json_wave_spawn_list && json_wave_spawn_list->type == cJSON_Array)
{
cJSON* json_spawn_event = nullptr;
cJSON_ArrayForEach(json_spawn_event, json_wave_spawn_list)
{
if (json_spawn_event->type != cJSON_Object)
continue;


wave.spawn_event_list.emplace_back();
Wave::SpawnEvent& spawn_event = wave.spawn_event_list.back();

cJSON* json_spawn_event_interval = cJSON_GetObjectItem(json_spawn_event, "interval");

if (json_spawn_event_interval && json_spawn_event_interval->type == cJSON_Number)
spawn_event.interval = json_spawn_event_interval->valuedouble;

cJSON* json_spawn_event_spawn_point = cJSON_GetObjectItem(json_spawn_event, "point");
if (json_spawn_event_spawn_point && json_spawn_event_spawn_point->type == cJSON_Number)
spawn_event.spawn_point = json_spawn_event_spawn_point->valueint;

cJSON* json_spawn_event_enemy_type = cJSON_GetObjectItem(json_spawn_event, "enemy");
if (json_spawn_event_enemy_type && json_spawn_event_enemy_type->type == cJSON_String)
{
const std::string str_enemy_type = json_spawn_event_enemy_type->valuestring;

if (str_enemy_type == "Slim")
spawn_event.enemy_type = EnemyType::Slim;
else if (str_enemy_type == "KingSlim")
spawn_event.enemy_type = EnemyType::KingSlim;
else if (str_enemy_type == "Skeleton")
spawn_event.enemy_type = EnemyType::Skeleton;
else if (str_enemy_type == "Goblin")
spawn_event.enemy_type = EnemyType::Goblin;
else if (str_enemy_type == "GoblinPriest")
spawn_event.enemy_type = EnemyType::GoblinPriest;


}
}
if (wave.spawn_event_list.empty())
wave_list.pop_back();
}
}
cJSON_Delete(json_root);
if (wave_list.empty())
{
return false;
}


return true;
}
  1. 首先利用fstream打开文件,利用stringstream读取文件的内容,然后转换为c语言风格的字符串格式,利用其转化为cjson形式的对象,并返回指针。首先获取的就是数组对象,波次数组

1. cJSON json_root = cJSON_Parse(str_stream.str().c_str());(将c风格char的字符串转换为cjon格式并返回指针)

image-20240723191330390
  1. 然后再创建一个cjosn指针遍历这个数组,有专门的遍历函数
    2.cJSON_ArrayForEach(json_wave, json_root)(类似for(int &tmp:(vector object))

  2. 然后在读取的又是一个对象{}

1
if (json_wave->type != cJSON_Object)//检查读取的是否正确	
  1. 进一步读取对应的键值

3.cJSON json_wave_rewards = cJSON_GetObjectItem(json_wave, “rewards”);*

image-20240723192241146
1
2
3
4
5
6
cJSON* json_wave_rewards = cJSON_GetObjectItem(json_wave, "rewards");
if (json_wave_rewards && json_wave_rewards->type == cJSON_Number)
wave.rewards = json_wave_rewards->valuedouble;
//显示第一个波次对象{},获取键为“rewards”的对象指针,然后检查其是否存在值的类型是否为数值
//然后数值存在valuedouble变量里面

  1. 后面读取敌人生成序列的逻辑也是一样的

cJSON使用过程中的内存问题

内存及时释放
cJSON的所有操作都是基于链表的,所以cJSON在使用过程中大量的使用malloc从堆中分配动态内存的,所以在使用完之后,应当及时调用下面的函数,清空cJSON指针所指向的内存,该函数也可用于删除某一条数据:

1
2
3
cJSON_Delete(cJSON *item);
//本段删除最初的节点
cJSON_Delete(json_root);

注意:该函数删除一条JSON数据时,如果有嵌套,会连带删除。

img
作者

yang

发布于

2024-07-23

更新于

2024-07-23

许可协议

评论