C语言解析JSON源码
2020-01-09
关键字:cJSON、linux JSON解析
JSON 是一种在互联网领域内很常用的轻量级数据交换协议。
它与 XML 的地位差不多,但就笔者而言,笔者更喜欢 JSON 的风格,因为它更符合我们的思维习惯,同样一份数据,JSON 格式的就是比 XML 要清晰明了一些。
最近笔者需要在 C语言 上解析 JSON 格式,在网上一顿找,找到一份很不错的开源代码。经过一阵研究与修改以后,终于变成了让笔者用的很顺手的 C语言 版 JSON 解析器。
现将这份经笔者小小修改过的代码记录一下,一来想给自己作个备忘,二来希望能帮到有同样需求的同学。
这份源码以及一份简易 demo 被打包至博客园网盘:
https://files.cnblogs.com/files/chorm590/C%E8%AF%AD%E8%A8%80%E8%A7%A3%E6%9E%90JSON%E7%A4%BA%E4%BE%8B.zip
这个压缩内包含有两份源码文件:
1、cJSON 解析器原始版与示例以及 readme 说明
2、经笔者修改过的示例代码
这里仅以笔者修改过的代码来作简要讲解。
经笔者修改过的代码文件共有 3 个代码文件,如下图所示:
其中 demo.c 是演示程序,共编写了 3 种常见的 JSON 数据格式及其解析方式来展示 cJSON 的用法。整个示例程序非常简单,同学们稍加阅读定能领悟其用法。
在这个解析器中,所有的 JSON 节点都被抽象成是一个 cJSON 对象,即 cJSON 结构体:
如上图所示,在这个解析器中,JSON 对象节点与 JSON 数组节点被绑在 next, prev, child 三个指针变量中。但凡涉及到 JSON 对象与 JSON 数组的解析,都可以快速地用这三个指针变量来找到对应的值。
本节点的类型则被记录在 type 变量中。type 的可选值如下图所示:
节点的值则被保存在 valuestring, valueint, valuedouble 中。其中 boolean 值是被当成 valueint 保存的。true 以 1 表示,false 以 0 表示。
而节点自身的名称,则是被保存在 struct cJSON 结构图所示的 string 变量中。
至于最后一个 pure_json 变量,则是笔者自己添加的。添加它的目的是为了保存去除了额外的空格字符与换行字符以后的纯 JSON 文本。是的,这个 cJSON 解析器默认情况下只能解析最标准格式的 JSON 文本,如下图所示:
而不能解析排版过后的 JSON 格式,如下图所示:
笔者前面提到的“稍稍修改了一下的版本”就是指添加了可以解析这种排版过后的 JSON 文本的功能。笔者为了在将“格式化”后的文本用完以后可以方便地 free 掉,就在 cJSON 结构体中添加了这个 pur_string 变量。同时,笔者这个格式化还可以保留字符串值中的空格字符与换行字符。
在使用这个解析器解析 JSON 时,只需要将原始 JSON 字符串传入 cJSON_Parse() 函数中即可自动将整个字符串解析一遍,并创建对应的节点链表。
不过必须要注意,由于解析 JSON 是使用 malloc 来分配内存空间的,因此在使用完以后一定要释放掉这些内存。释放内存的方式也简单,直接将根节点作为参数传入 cJSON_Delete() 函数中即可。
至于这个解析器解析的流程,有兴趣的同学可以自己去跟踪 cJSON.c 的源码实现。它并不复杂,笔者就不再这里赘述了。
如果你时间有限,也可以直接参考或修改笔者的示例代码来直接应用。
最后,贴上笔者的演示代码:
#include <stdio.h> #include "cJSON.h" static void parse_normal_json(); static void parse_object_json(); static void parse_array_json(); int main() { printf("hello world\n"); parse_normal_json(); parse_object_json(); parse_array_json(); return 0; } static void parse_normal_json() { printf("\n\n\n>>> parse_normal_json()\n\n"); const char *json = "\n\ {\n \"key0\":\"Js on is a fun\nny data for mat!\",\n\ \"key1\":\"value1\",\n\ \"key2\":26,\n\ \"key3\":false\n\ }\n "; printf("json:\n%s\n\n", json); cJSON *root = cJSON_Parse(json); if(root == 0) { printf("error\n"); return; } cJSON *key0_node = cJSON_GetObjectItem(root, "key0"); if(key0_node == 0) return; printf("key0 name:\n\t%s\nkey0 value:\n\t%s\n", key0_node->string, key0_node->valuestring); cJSON *key1_node = cJSON_GetObjectItem(root, "key1"); if(key1_node == 0) return; printf("key1 value:\n\t%s\n", key1_node->valuestring); cJSON *key2_node = cJSON_GetObjectItem(root, "key2"); if(key2_node == 0) return; printf("key2 value:\n\t%d\n", key2_node->valueint); cJSON *key3_node = cJSON_GetObjectItem(root, "key3"); if(key3_node == 0) return; printf("key3 value:\n\t%d\n", key1_node->valueint); cJSON_Delete(root); } static void parse_object_json() { printf("\n\n\n>>> parse_object_json()\n\n"); const char *json = "\n\ {\n \"obj\":{\n\ \"key\":71,\n\ \"name\":22\n\ }\n }\n "; printf("json:\n%s\n\n", json); cJSON *root = cJSON_Parse(json); if(root == 0) { printf("error\n"); return; } cJSON *obj_node = cJSON_GetObjectItem(root, "obj"); if(obj_node == 0) return; cJSON *key_node = cJSON_GetObjectItem(obj_node, "key"); if(key_node == 0) return; printf("key value:\n\t%d\n", key_node->valueint); cJSON *child = obj_node->child; if(child == 0) return; printf("key name:\n\t%s\nkey value:\n\t%d\n", child->string, child->valueint); cJSON *name_node = child->next; if(name_node == 0) return; printf("name name:\n\t%s\nname value:\n\t%d\n", name_node->string, name_node->valueint); cJSON_Delete(root); } static void parse_array_json() { printf("\n\n\n>>> parse_array_json()\n\n"); const char *json = "\n\ {\n \"arrs\":[15,3,99,108,22]\n\ }\n "; printf("json:\n%s\n\n", json); cJSON *root = cJSON_Parse(json); if(root == 0) { printf("error\n"); return; } cJSON *arrs_node = cJSON_GetObjectItem(root, "arrs"); if(arrs_node == 0) return; if(arrs_node->type == cJSON_Array) { cJSON *node = arrs_node->child; int i = 0; while(node != 0) { printf("item%d:\n\t%d\n", i++, node->valueint); node = node->next; } } else { printf("arrs_node is not a json_array.\n"); } cJSON_Delete(root); }
cJSON的演示代码