Dave Gambleによって作成されたcJSONライブラリを使用して、次のJSON配列を読み取ろうとしています。
_"items":
[
{
"name": "command",
"index": "X",
"optional": "0"
},
{
"name": "status",
"index": "X",
"optional": "0"
}
]
_
彼の documentation を読むと、個々のオブジェクトを読み取る方法が見つかりましたが、配列については何もありませんでした。また、与えられた例からその方法を推測することができませんでした。
これが私が試していることです:
_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");
}
}
この関数を次のように呼び出します
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"));
私見、これは、ライブラリのカプセル化をバーストして、そのオブジェクトデータ構造を直接操作する必要がある場合の1つの例です。 _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;
_
(もちろん、著者が作成した名前の選択のいくつかを誤解する可能性があります。しかし、適切な名前はhardです。)
注意すべき重要な点は、JSONオブジェクトとJSON配列の両方にnull以外のchild
フィールドがあり、これはその子の二重リンクリストを指していることです。 JSONオブジェクトの子には、その子に関連付けられたフィールド名を含むnull以外のstring
フィールドもあります。
したがって、JSON配列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
_も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
}