Использование cJSON для чтения в массиве JSON
Я пытаюсь использовать библиотеку cJSON, написанную Dave Gamble, для чтения в следующем массиве 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()
, но он принимает только число (предположительно индекс) и строку, чтобы указать, какое поле оно хочет.
Ответы
Ответ 1
Документ упоминает о 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");
}
}
Вызвать эту функцию как
request_json = cJSON_Parse(request_body);
parse_object(request_json);
Ответ 2
Если вы хотите работать немного быстрее, это выглядит так:
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;
}
}
Это позволяет избежать стоимости O (n ^ 2), которую правильно указывает RBerteig.
Позвонить с помощью:
parse_array(cJSON_GetObjectItem(cJSON_Parse(request_body),"items"));
Ответ 3
IMHO, это один из примеров случая, когда вы должны вскрыть инкапсуляцию библиотеки и напрямую работать с ней структурой данных объекта. 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 string, if type==cJSON_String */
int valueint; /* The item number, if type==cJSON_Number */
double valuedouble; /* The item number, if type==cJSON_Number */
char *string; /* The item 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
поля, которые содержат имя поля, связанное с этим дочерним элементом.
Итак, чтобы в общем случае выполнить итерацию по массиву JSON Array ja
в O (n), вызывая функцию для каждого элемента, вы пишете что-то вроде этого:
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_Array
, либо cJSON_Object
, а jchild->string
также будет не нулевым для объектов.
Выполнение одной и той же итерации путем вызова cJSON_GetArraySize()
и использование cJSON_GetArrayItem()
будет иметь порядок O (n ^ 2), потому что каждый раз нужно пересекать связанный список, чтобы найти n-й элемент.
Возможно, cJSON должен включать в себя некоторые общие функции ForEach
, но это может означать начало значительного объема охвата области видимости от его заявленной первоначальной цели - быть "самым тугим возможным парсером, с которым вы можете выполнить свою работу с помощью".
Ответ 4
Моя догадка (не прочитав спецификацию и немного ржавшую с 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
}