使用cJSON读入JSON数组

我试图使用由Dave Gamble编写的cJSON库来读入以下JSON数组:

"items": [ { "name": "command", "index": "X", "optional": "0" }, { "name": "status", "index": "X", "optional": "0" } ] 

通过阅读他的文档 ,我找到了阅读单个对象的方法,但没有关于数组的内容,我无法从给出的示例中推测如何做到这一点。

这是我正在尝试的:

 cJSON* request_json = NULL; cJSON* items = cJSON_CreateArray(); cJSON* name = NULL; cJSON* index = NULL; cJSON* optional = NULL; request_json = cJSON_Parse(request_body); items = cJSON_GetObjectItem(request_json, "items"); name = cJSON_GetObjectItem(items, "name"); index = cJSON_GetObjectItem(items, "index"); optional = cJSON_GetObjectItem(items, "optional"); 

我知道这是错的,不仅因为它不起作用,而且我无法弄清楚如何使它正确。

显然,我需要循环读取数组中每个索引的所有条目的过程。 我不知道我将如何做到这一点,因为我不知道我应该在这个代码中使用索引,或者它是否是正确的开始。 有一个cJSON_GetArrayItem() ,但它只需要一个数字(可能是一个索引)而没有字符串来指示它想要的字段。

文档提到了parse_object()。

我想这就是你需要做的。

 void parse_object(cJSON *root) { cJSON* name = NULL; cJSON* index = NULL; cJSON* optional = NULL; int i; cJSON *item = cJSON_GetObjectItem(items,"items"); for (i = 0 ; i < cJSON_GetArraySize(item) ; i++) { cJSON * subitem = cJSON_GetArrayItem(item, i); name = cJSON_GetObjectItem(subitem, "name"); index = cJSON_GetObjectItem(subitem, "index"); optional = cJSON_GetObjectItem(subitem, "optional"); } } 

将此function称为

 request_json = cJSON_Parse(request_body); parse_object(request_json); 

如果你想运行得稍快,这就是代码的样子:

 void parse_array(cJSON *array) { cJSON *item = array ? array->child : 0; while (item) { cJSON *name = cJSON_GetObjectItem(item, "name"); cJSON *index = cJSON_GetObjectItem(item, "index"); cJSON *optional = cJSON_GetObjectItem(item, "optional"); item=item->next; } } 

这避免了RBerteig正确指出的O(n ^ 2)成本。

致电:

 parse_array(cJSON_GetObjectItem(cJSON_Parse(request_body),"items")); 

恕我直言,这是一个例子,你应该破解库的封装并直接使用它的对象数据结构。 cJSON.h将核心对象定义为以下struct

 /* The cJSON structure: */ typedef struct cJSON { struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ int type; /* The type of the item, as above. */ char *valuestring; /* The item's string, if type==cJSON_String */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's number, if type==cJSON_Number */ char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ } cJSON; 

(当然,人们可以对作者所做的一些命名选择进行狡辩。但好的命名很难 。)

需要注意的关键是JSON对象和JSON数组都有一个非空子字段,它指向child的双向链表。 JSON对象的子节点也具有非空string字段,其中包含与该子节点关联的字段名称。

因此,为了在O(n)时间内对JSON Array ja进行一般迭代,为每个元素调用一个函数,你可以这样写:

 cJSON_ForEachItem(cJSON *ja, int (*f)(cJSON *ja, int i, cJSON *jchild)) { cJSON *jchild; int i; for (jchild=ja->child, i=0; jchild; jchild=jchild->next, ++i) { // do something here with the ith child... if (f(ja, i, jchild)) break; } } 

由于对象和数组仅在每个子项的名称存在时内部不同,因此该函数也将迭代对象的字段。 回调可以告诉我,因为ja->type将是cJSON_ArraycJSON_Object ,并且jchild->string对于Objects也将是非null。

通过调用cJSON_GetArraySize()并使用cJSON_GetArrayItem()执行相同的迭代将是O(n ^ 2)的顺序,因为它必须每次遍历链接列表以找到第n个项目。

可以说,cJSON应该包含一些通用的ForEach函数,但这可能代表了大量范围的开始 – 远离它声称的最初的目标是成为“你可以完成工作的最愚蠢的解析器”。

我的猜测(没有阅读规范,并且对C有点生疏):

 request_json = cJSON_Parse(request_body); items = cJSON_GetObjectItem(request_json, "items"); for (int i = 0; i < max; i++) { // Presumably "max" can be derived from "items" somehow cJSON* item = cJSON_GetArrayItem(items, i); name = cJSON_GetObjectItem(item, "name"); index = cJSON_GetObjectItem(item, "index"); optional = cJSON_GetObjectItem(item, "optional"); // Stash above info somewhere }