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); }