• 高级时钟项目(2)Json文件解析学习---C语言版本


    笔者来介绍一下json文件解析

    1、背景介绍

    笔者在获取天气数据的时候,是通过MCU的WIFI去获取,但是获取到的数据json数据,需要解析,C语言没那么解析库,所以就需要找一些开源的解析库。
    在这里插入图片描述
    在这里插入图片描述

    笔者找到cjson这个适用于C语言的解析json库,一个.c,一个.h,非常简单,没有其他任何依赖,直接使用就好

    2、json数据解析学习使用

    2.1 解析使用

    介绍一下正常的json数据解析,一般就是三个接口,
    第一步,调用解析接口,将总的json数据进行解析,
    第二步,就是获取json的里面的字段,一级一级的去获取item数据,就可以获取到数据,一般都是字符串
    第三步,获取完成之后,记得delete,因为是通过堆来获取空间大小的。

    cJSON *cJSON_Parse(const char *value) ;
    cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
    void cJSON_Delete(cJSON *c);
    
    
    • 1
    • 2
    • 3
    • 4
    	char json2[] = "{\"success\":\"1\",\"result\":{\"timestamp\":\"1668090740\",\"datetime_1\":\"2022 - 11 - 10 22:32 : 20\",\"datetime_2\":\"2022年11月10日 22时32分20秒\",\"week_1\":\"4\",\"week_2\":\"星期四\",\"week_3\":\"周四\",\"week_4\":\"Thursday\"}}";
    
    cJSON *root = cJSON_Parse(json2);
    	if (root == 0)
    	{
    		printf("error\n");
    		return;
    	}
    	printf("%s\n", "有格式的方式打印Json:");
    	printf("%s\n\n", cJSON_Print(root));
    
    	cJSON *success = cJSON_GetObjectItem(root, "success");
    	if (success == 0)
    		return;
    	printf("success name:\n\t%s\nsuccess value:\n\t%s\n", success->string, success->valuestring);
    
    	
    	cJSON *result = cJSON_GetObjectItem(root, "result");
    
    	cJSON *timestamp = cJSON_GetObjectItem(result, "timestamp");
    	printf("timestamp value:\n\t%s\n", timestamp->valuestring);
    
    	cJSON *datetime_1 = cJSON_GetObjectItem(result, "datetime_1");
    	printf("datetime_1 value:\n\t%s\n", datetime_1->valuestring);
    
    	cJSON *datetime_2 = cJSON_GetObjectItem(result, "datetime_2");
    	printf("datetime_1 value:\n\t%s\n", datetime_2->valuestring);
    
    	cJSON *week_1 = cJSON_GetObjectItem(result, "week_1");
    	printf("timestamp value:\n\t%s\n", week_1->valuestring);
    	cJSON *week_2 = cJSON_GetObjectItem(result, "week_2");
    	printf("timestamp value:\n\t%s\n", week_2->valuestring);
    	cJSON *week_3 = cJSON_GetObjectItem(result, "week_3");
    	printf("timestamp value:\n\t%s\n", week_3->valuestring);
    	cJSON *week_4 = cJSON_GetObjectItem(result, "week_4");
    	printf("timestamp value:\n\t%s\n", week_4->valuestring);
    	
    
    	cJSON_Delete(root);
    
    
    • 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

    在这里插入图片描述

    初步来看一下解析里面的实现:

    cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)
    {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
    
    • 1
    • 2
    • 就是从子节点里面找到匹配的“名称”,如果子节点为空,则直接退出,返回NULL了,
    • 如果不为空,则cJSON_strcasecmp就是字符串匹配,没有匹配到,则进行next,这里就有疑问了,为什么是next,而不是child的,这是因为父节点里面会有很多项,你要找父节点中匹配的子节点,如果你要找子节点里面的子节点的项,就是child。
    • 从下面的result就能看出来,先找child,然后找child以及child的next,以及next的next。

    在这里插入图片描述

    2.2 json数组解析

    json格式里面,带[ ]就是数组的,需要用到获取数组size的接口。

    char json3[] = "{\"results\":[{\"location\":{\"id\":\"WTW3SJ5ZBJUY\",\"name\":\"上海\",\"country\":\"CN\",\"path\":\"上海, 上海, 中国\",\"timezone\":\"Asia / Shanghai\",\"timezone_offset\":\" + 08:00\"},\"now\":{\"text\":\"晴\",\"code\":\"1\",\"temperature\":\"26\"},\"last_update\":\"2023 - 09 - 06T23:40 : 13 + 08 : 00\"}]}";
    int    cJSON_GetArraySize(cJSON *array);
    cJSON *cJSON_GetArrayItem(cJSON *array,int item);
    
    • 1
    • 2
    • 3

    刚开始的时候,我按照上面正常数据的去解析,发现始终解析出来就是空的,很奇怪,

    • 第一步,cJSON_Parse解析root
    • 第二步,获取root下面的results
    • 第三步,获取到results之后,到[ ]了,然后我就开始获取数组size,但是结果返回1,明显不对,总共有3个,
      在这里插入图片描述
    cJSON* root = cJSON_Parse(json3);
    	if (root == 0)
    	{
    		printf("error\n");
    		return;
    	}
    	printf("%s\n", "有格式的方式打印Json:");
    	printf("%s\n\n", cJSON_Print(root));
    
    	cJSON* results = cJSON_GetObjectItem(root, "results");
    	if (0 == results)
    	{
    		return;
    	}
    
    	int array_size = cJSON_GetArraySize(results );
    	printf("item size=%d\r\n", array_size);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    然后我就开始调试,VS有个好处就是调试非常方便,断点处的数据也很清晰。
    在这里插入图片描述
    通过断点调试来看,我这样的写法也是有问题的,需要继续深入这个结构体去查看,发现需要继续下一层,通过回去NULL的子节点,然后终于出现location了,说明这一层才是getsize的节点。

    在这里插入图片描述
    然后我又紧接着去看子节点,发现是该节点里面的数据了,并不是同一级别的数据,然后我看到了next,果然是在这个里面找到了“now”,然后接着找next,找到了“last_update”,说明这样解析的顺序才是正确的。
    在这里插入图片描述
    在这里插入图片描述

    下面给出完整的实例代码。

    char json3[] = "{\"results\":[{\"location\":{\"id\":\"WTW3SJ5ZBJUY\",\"name\":\"上海\",\"country\":\"CN\",\"path\":\"上海, 上海, 中国\",\"timezone\":\"Asia / Shanghai\",\"timezone_offset\":\" + 08:00\"},\"now\":{\"text\":\"晴\",\"code\":\"1\",\"temperature\":\"26\"},\"last_update\":\"2023 - 09 - 06T23:40 : 13 + 08 : 00\"}]}";
    
    	cJSON* root = cJSON_Parse(json3);
    	if (root == 0)
    	{
    		printf("error\n");
    		return;
    	}
    	printf("%s\n", "有格式的方式打印Json:");
    	printf("%s\n\n", cJSON_Print(root));
    
    	cJSON* results = cJSON_GetObjectItem(root, "results");
    	if (0 == results)
    	{
    		return;
    	}
    	cJSON* results1 = cJSON_GetObjectItem(results, NULL);
    	//printf("results name:%s results value:%s\r\n", results->string, results->valuestring);
    	int array_size = cJSON_GetArraySize(results1);
    	printf("item size=%d\r\n", array_size);
    
    
    	cJSON* location = cJSON_GetArrayItem(results1, 0);
    	printf("location value:%p %s\r\n", location, location->valuestring);
    	if (0 == location)
    	{
    		return;
    	}
    	cJSON* id = cJSON_GetObjectItem(location, "id");
    	printf("id value:%p %s\r\n", id, id->valuestring);
    
    • 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

    在这里插入图片描述

    int    cJSON_GetArraySize(cJSON *array)							{cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
    cJSON *cJSON_GetArrayItem(cJSON *array,int item)				{cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
    
    • 1
    • 2

    来看一下数组的实现:

    • 这个主要就是看一下父节点下面有几个子节点,然后去按照节点顺序去访问。
      在这里插入图片描述

    2.3 注意事项

    • cjson在C代码空间里面是通过mallco获取ram空间的,所以需要记得堆的空间开大一点,不然无法解析,直接返回NULL。
      在这里插入图片描述
  • 相关阅读:
    java计算机毕业设计高校宿舍管理系统演示视频2021源码+mysql数据库+系统+lw文档+部署
    学习笔记-Windows 基础服务搭建
    【教程】 iOS混淆加固原理篇
    Linux的基本使用
    【NOWCODER】- Python:列表(三)
    计算机毕业设计springboot+vue+elementUI校园疫情防控系统
    2022面试相关- reactnatvie相关
    教程更新20220719
    SpringMVC基础篇(三)
    《算法系列》之 数组
  • 原文地址:https://blog.csdn.net/qq_34430371/article/details/133256173