一.功能
二.cJOSN使用规则
1.cJOSN介绍
JSON对象是一个无序的"名称/值"键值对的集合:
- 以"{“开始,以”}"结束,允许嵌套使用;
- 每个名称和值成对出现,名称和值之间使用":"分隔;
- 键值对之间用","分隔
- 在这些字符前后允许存在无意义的空白符;
对于键值,可以有如下值:
- 一个新的json对象
- 数组:使用"[“和”]"表示
- 数字:直接表示,可以是整数,也可以是浮点数
- 字符串:使用引号"表示
- 字面值:false、null、true中的一个(必须是小写)
-
示例如下:
{
"name": "mculover666",
"age": 22,
"weight": 55.5
"address":
{
"country": "China",
"zip-code": 111111
},
-
"skill": ["c", "Java", "Python"],
"student": false
}
2.cJOSN的输出
一段完整的JSON数据就是一条长长的链表,那么,如何打印出这段JSON数据呢?
cJSON提供了一个API,可以将整条链表中存放的JSON信息输出到一个字符串中:
(char *) cJSON_Print(const cJSON *item);
使用的时候,只需要接收该函数返回的指针地址即可。
封装数据和打印数据示例
单纯的讲述方法还不够,下面用一个例子来说明,封装出开头给出的那段JSON数据:
#include <stdio.h>
#include "cJSON.h"
int main(void)
{
cJSON* cjson_test = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_skill = NULL;
char* str = NULL;
/* 创建一个JSON数据对象(链表头结点) */
cjson_test = cJSON_CreateObject();
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(cjson_test, "name", "mculover666");
/* 添加一条整数类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "age", 22);
/* 添加一条浮点类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "weight", 55.5);
/* 添加一个嵌套的JSON数据(添加一个链表节点) */
cjson_address = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_address, "country", "China");
cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);
cJSON_AddItemToObject(cjson_test, "address", cjson_address);
/* 添加一个数组类型的JSON数据(添加一个链表节点) */
cjson_skill = cJSON_CreateArray();
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));
cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);
/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddFalseToObject(cjson_test, "student");
/* 打印JSON对象(整条链表)的所有数据 */
str = cJSON_Print(cjson_test);
printf("%s\n", str);
return 0;
}
3.cJOSN的解析方法
解析JSON数据的过程,其实就是剥离一个一个链表节点(键值对)的过程。
解析方法如下:
- ① 创建链表头指针:
cJSON* cjson_test = NULL;
- ② 解析整段JSON数据,并将链表头结点地址返回,赋值给头指针:
解析整段数据使用的API只有一个:
(cJSON *) cJSON_Parse(const char *value);
- ③ 根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
- ④ 如果JSON数据的值是数组,使用下面的两个API提取数据:
(int) cJSON_GetArraySize(const cJSON *array);
(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
解析示例
下面用一个例子来说明如何解析出开头给出的那段JSON数据:
#include <stdio.h>
#include "cJSON.h"
char *message =
"{ \
\"name\":\"mculover666\", \
\"age\": 22, \
\"weight\": 55.5, \
\"address\": \
{ \
\"country\": \"China\",\
\"zip-code\": 111111\
}, \
\"skill\": [\"c\", \"Java\", \"Python\"],\
\"student\": false \
}";
int main(void)
{
cJSON* cjson_test = NULL;
cJSON* cjson_name = NULL;
cJSON* cjson_age = NULL;
cJSON* cjson_weight = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_address_country = NULL;
cJSON* cjson_address_zipcode = NULL;
cJSON* cjson_skill = NULL;
cJSON* cjson_student = NULL;
int skill_array_size = 0, i = 0;
cJSON* cjson_skill_item = NULL;
/* 解析整段JSO数据 */
cjson_test = cJSON_Parse(message);
if(cjson_test == NULL)
{
printf("parse fail.\n");
return -1;
}
/* 依次根据名称提取JSON数据(键值对) */
cjson_name = cJSON_GetObjectItem(cjson_test, "name");
cjson_age = cJSON_GetObjectItem(cjson_test, "age");
cjson_weight = cJSON_GetObjectItem(cjson_test, "weight");
printf("name: %s\n", cjson_name->valuestring);
printf("age:%d\n", cjson_age->valueint);
printf("weight:%.1f\n", cjson_weight->valuedouble);
/* 解析嵌套json数据 */
cjson_address = cJSON_GetObjectItem(cjson_test, "address");
cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");
cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");
printf("address-country:%s\naddress-zipcode:%d\n", cjson_address_country->valuestring, cjson_address_zipcode->valueint);
/* 解析数组 */
cjson_skill = cJSON_GetObjectItem(cjson_test, "skill");
skill_array_size = cJSON_GetArraySize(cjson_skill);
printf("skill:[");
for(i = 0; i < skill_array_size; i++)
{
cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i);
printf("%s,", cjson_skill_item->valuestring);
}
printf("\b]\n");
/* 解析布尔型数据 */
cjson_student = cJSON_GetObjectItem(cjson_test, "student");
if(cjson_student->valueint == 0)
{
printf("student: false\n");
}
else
{
printf("student:error\n");
}
return 0;
}
编译:
gcc cJSON.c example2.c -o example2.exe
运行结果如图:
三.函数代码
1.getch函数
-
获取当前终端属性:使用
tcgetattr(STDIN_FILENO, &oldt)
获取当前终端的属性,并存储在oldt
结构体中。 -
复制旧属性:将
oldt
的内容复制到newt
中,以便修改。 -
修改终端属性:通过
newt.c_lflag &= ~(ICANON | ECHO)
修改newt
结构体中的c_lflag
字段,去除ICANON
和ECHO
标志。ICANON
表示规范模式(canonical mode),在这种模式下,输入是缓冲的,直到按下回车键才会读取。去除ICANON
标志后,输入将不被缓冲,可以立即读取。ECHO
标志表示是否回显输入到终端,去除ECHO
标志后,输入的字符不会显示在终端上。 -
设置新的终端属性:使用
tcsetattr(STDIN_FILENO, TCSANOW, &newt)
立即应用新的终端属性。 -
读取字符:使用
getchar()
函数从标准输入读取一个字符,存储在变量ch
中。 -
恢复终端属性:使用
tcsetattr(STDIN_FILENO, TCSANOW, &oldt)
恢复原来的终端属性,确保终端状态不会因为这个函数的调用而永久改变。 -
返回读取的字符:函数返回读取的字符。
这个函数通常用于需要从键盘读取单个字符而不显示输入的应用中,例如在编写简单的文本编辑器或游戏时。
2.一级菜单打印函数(VCT100)
使用vct100
使用频率高的总结:
VT100控制码归类如下。
\33[nA 光标上移n行
\33[nB 光标下移n行
\33[nC 光标右移n行
\33[nD 光标左移n行
\33[1m 设置高亮度
背景颜色范围:40----49
40:黑
41:深红
42:绿
43:黄色
44:蓝色
45:紫色
46:深绿
47:白色
前景字符颜色:30-----------39
30:黑
31:红
32:绿
33:黄
34:蓝色
35:紫色
36:深绿
37:白色
3.功能选择函数(VCT100)
4.解析实时天气cJOSN函数
【1】进入www.api.k780.com实时天气网站
【2】选择功能,通过测试实例进入查询
【3】F12相当于wireshark跟踪tcp数据获得返回数据头
【1】函数中传入socket的fd
【2】将传入的的地址和请求头发送给服务器
(1)注意格式为字符串数组,每个字符串以\r\n结尾,最后一个字符串由\r\n\r\n结尾
(2)第一行字符串为weaid=%s,%s为需要查询的地址名
appkey和sign为服务器提供给用户的
【3】将读入的数据存入buf中
【4】数据以键值对的形式存储,使用cJOSN解析
【1】strstr:
- 使用
strstr
函数在buf
(可能是一个包含响应数据的字符串)中查找第一个{
字符,这通常表示JSON对象的开始。 - 如果找不到
{
,则打印错误消息 "No JSON found in respon
【2】解析JSON:
-
使用
cJSON_Parse
函数解析找到的JSON字符串。 -
如果解析失败(即
root
为NULL
),则打印错误消息 "JSON解析失败"
【3】检查success字段
-
从解析后的JSON对象中获取
success
字段。 -
如果
success
字段不存在或其值不是字符串 "1",则打印错误消息 "请求失败",并删除解析后的JSON对象,然后退出函数。
【4】获取result对象
-
从解析后的JSON对象中获取
result
对象。 -
如果
result
对象不存在,则打印错误消息 "无天气数据",并删除解析后的JSON对象
【5】获得天气数据
-
定义一个
cJSON *
类型的数组items
,用于存储从result
对象中获取的各个天气数据项。 -
使用
cJSON_GetObjectItem
函数从result
对象中获取以下天气数据项:-
citynm
:城市名称 -
week
:星期 -
temperature
:日温 -
temperature_curr
:时温 -
humidity
:湿度 -
aqi
:空气质量指数(AQI) -
weather
:天气状况 -
wind
:风向 -
winp
:风力 -
days
:日期
-
【6】各个部分的作用
-
strstr(buf, "{")
:在buf
中查找第一个{
字符,表示JSON对象的开始。 -
cJSON_Parse(json_str)
:解析JSON字符串,生成一个cJSON
对象。 -
cJSON_GetObjectItem(root, "success")
:从JSON对象中获取success
字段,用于检查请求是否成功。 -
cJSON_GetObjectItem(result, ...)
:从result
对象中获取各种天气数据项。 -
cJSON_Delete(root)
:删除解析后的JSON对象,释放内存。
【7】打印表格
5.解析未来天气cJOSN函数
-
提示用户输入日期:
-
定义一个字符数组
date
,用于存储用户输入的日期。 -
使用
printf
函数提示用户输入日期,格式为YYYY-MM-DD
。 -
使用
scanf
函数从标准输入读取用户输入的日期,并存储在date
数组中。
-
-
遍历result数组,查找对应日期的天气信息:
-
定义一个整型变量
found
,用于标记是否找到匹配的日期,初始值为0(未找到)。 -
使用
cJSON_GetArraySize(result)
获取result
数组的大小,即包含的天气信息条目数。 -
使用
for
循环遍历result
数组中的每个条目。 -
对于每个条目,使用
cJSON_GetArrayItem(result, i)
获取对应的JSON对象,并存储在day
指针中。 -
如果
day
为NULL
,则跳过当前条目,继续下一个条目。
-
-
提取日期并检查是否匹配:
-
从
day
对象中获取days
字段,该字段包含日期信息。 -
如果
days
为NULL
,则跳过当前条目,继续下一个条目。 -
打印调试信息,显示JSON中的日期。
-
使用
strcmp
函数比较JSON中的日期和用户输入的日期是否匹配。 -
如果日期匹配,则将
found
变量设置为1(找到匹配的日期),并继续提取天气信息。
-
-
提取天气信息:
-
如果找到匹配的日期,则从
day
对象中提取以下天气信息:-
week
:星期 -
citynm
:城市名称 -
temperature
:温度 -
humidity
:湿度 -
weather
:天气状况 -
wind
:风向 -
winp
:风力
-
-
5.各个部分的作用
-
char date[20];
:定义一个字符数组,用于存储用户输入的日期。 -
printf("请输入要查询的日期(格式:YYYY-MM-DD):");
:提示用户输入日期。 -
scanf("%s", date);
:从标准输入读取用户输入的日期。 -
int found = 0;
:定义一个整型变量,用于标记是否找到匹配的日期。 -
int num_days = cJSON_GetArraySize(result);
:获取result
数组的大小。 -
for (int i = 0; i < num_days; i++)
:遍历result
数组中的每个条目。 -
cJSON_GetArrayItem(result, i)
:获取result
数组中的第i
个条目。 -
cJSON_GetObjectItem(day, "days")
:从day
对象中获取days
字段,包含日期信息。 -
strcmp(days->valuestring, date) == 0
:比较JSON中的日期和用户输入的日期是否匹配。 -
cJSON_GetObjectItem(day, ...)
:从day
对象中提取各种天气信息字段。