|
@@ -21,6 +21,7 @@
|
|
*/
|
|
*/
|
|
|
|
|
|
Welcome to cJSON.
|
|
Welcome to cJSON.
|
|
|
|
+-----------------
|
|
|
|
|
|
cJSON aims to be the dumbest possible parser that you can get your job done with.
|
|
cJSON aims to be the dumbest possible parser that you can get your job done with.
|
|
It's a single file of C, and a single header file.
|
|
It's a single file of C, and a single header file.
|
|
@@ -29,68 +30,68 @@ JSON is described best here: http://www.json.org/
|
|
It's like XML, but fat-free. You use it to move data around, store things, or just
|
|
It's like XML, but fat-free. You use it to move data around, store things, or just
|
|
generally represent your program's state.
|
|
generally represent your program's state.
|
|
|
|
|
|
-
|
|
|
|
First up, how do I build?
|
|
First up, how do I build?
|
|
Add cJSON.c to your project, and put cJSON.h somewhere in the header search path.
|
|
Add cJSON.c to your project, and put cJSON.h somewhere in the header search path.
|
|
For example, to build the test app:
|
|
For example, to build the test app:
|
|
|
|
|
|
-gcc cJSON.c test.c -o test -lm
|
|
|
|
-./test
|
|
|
|
-
|
|
|
|
|
|
+ gcc cJSON.c test.c -o test -lm
|
|
|
|
+ ./test
|
|
|
|
|
|
As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
|
|
As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
|
|
As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
|
|
As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
|
|
in one of two modes: Auto and Manual. Let's have a quick run-through.
|
|
in one of two modes: Auto and Manual. Let's have a quick run-through.
|
|
|
|
|
|
-
|
|
|
|
I lifted some JSON from this page: http://www.json.org/fatfree.html
|
|
I lifted some JSON from this page: http://www.json.org/fatfree.html
|
|
That page inspired me to write cJSON, which is a parser that tries to share the same
|
|
That page inspired me to write cJSON, which is a parser that tries to share the same
|
|
philosophy as JSON itself. Simple, dumb, out of the way.
|
|
philosophy as JSON itself. Simple, dumb, out of the way.
|
|
|
|
|
|
Some JSON:
|
|
Some JSON:
|
|
-{
|
|
|
|
- "name": "Jack (\"Bee\") Nimble",
|
|
|
|
- "format": {
|
|
|
|
- "type": "rect",
|
|
|
|
- "width": 1920,
|
|
|
|
- "height": 1080,
|
|
|
|
- "interlace": false,
|
|
|
|
- "frame rate": 24
|
|
|
|
|
|
+----------
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ "name": "Jack (\"Bee\") Nimble",
|
|
|
|
+ "format": {
|
|
|
|
+ "type": "rect",
|
|
|
|
+ "width": 1920,
|
|
|
|
+ "height": 1080,
|
|
|
|
+ "interlace": false,
|
|
|
|
+ "frame rate": 24
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
|
|
Assume that you got this from a file, a webserver, or magic JSON elves, whatever,
|
|
Assume that you got this from a file, a webserver, or magic JSON elves, whatever,
|
|
you have a char * to it. Everything is a cJSON struct.
|
|
you have a char * to it. Everything is a cJSON struct.
|
|
Get it parsed:
|
|
Get it parsed:
|
|
- cJSON *root = cJSON_Parse(my_json_string);
|
|
|
|
|
|
+
|
|
|
|
+ cJSON *root = cJSON_Parse(my_json_string);
|
|
|
|
|
|
This is an object. We're in C. We don't have objects. But we do have structs.
|
|
This is an object. We're in C. We don't have objects. But we do have structs.
|
|
What's the framerate?
|
|
What's the framerate?
|
|
|
|
|
|
- cJSON *format = cJSON_GetObjectItem(root,"format");
|
|
|
|
- int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
|
|
|
|
|
|
+ cJSON *format = cJSON_GetObjectItem(root,"format");
|
|
|
|
+ int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
|
|
|
|
|
|
|
|
|
|
Want to change the framerate?
|
|
Want to change the framerate?
|
|
- cJSON_GetObjectItem(format,"frame rate")->valueint=25;
|
|
|
|
-
|
|
|
|
|
|
+ cJSON_GetObjectItem(format,"frame rate")->valueint=25;
|
|
|
|
+
|
|
Back to disk?
|
|
Back to disk?
|
|
- char *rendered=cJSON_Print(root);
|
|
|
|
|
|
+ char *rendered=cJSON_Print(root);
|
|
|
|
|
|
Finished? Delete the root (this takes care of everything else).
|
|
Finished? Delete the root (this takes care of everything else).
|
|
- cJSON_Delete(root);
|
|
|
|
|
|
+ cJSON_Delete(root);
|
|
|
|
|
|
That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers
|
|
That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers
|
|
before you dereference them. If you want to see how you'd build this struct in code?
|
|
before you dereference them. If you want to see how you'd build this struct in code?
|
|
- cJSON *root,*fmt;
|
|
|
|
- root=cJSON_CreateObject();
|
|
|
|
- cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
|
|
|
|
- cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
|
|
|
|
- cJSON_AddStringToObject(fmt,"type", "rect");
|
|
|
|
- cJSON_AddNumberToObject(fmt,"width", 1920);
|
|
|
|
- cJSON_AddNumberToObject(fmt,"height", 1080);
|
|
|
|
- cJSON_AddFalseToObject (fmt,"interlace");
|
|
|
|
- cJSON_AddNumberToObject(fmt,"frame rate", 24);
|
|
|
|
|
|
+ cJSON *root,*fmt;
|
|
|
|
+ root=cJSON_CreateObject();
|
|
|
|
+ cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
|
|
|
|
+ cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
|
|
|
|
+ cJSON_AddStringToObject(fmt,"type", "rect");
|
|
|
|
+ cJSON_AddNumberToObject(fmt,"width", 1920);
|
|
|
|
+ cJSON_AddNumberToObject(fmt,"height", 1080);
|
|
|
|
+ cJSON_AddFalseToObject (fmt,"interlace");
|
|
|
|
+ cJSON_AddNumberToObject(fmt,"frame rate", 24);
|
|
|
|
|
|
Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup.
|
|
Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup.
|
|
Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and
|
|
Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and
|
|
@@ -111,18 +112,20 @@ Sibling hs type False, name "interlace", and a sibling:
|
|
Sibling has type Number, name "frame rate", value 24
|
|
Sibling has type Number, name "frame rate", value 24
|
|
|
|
|
|
Here's the structure:
|
|
Here's the structure:
|
|
-typedef struct cJSON {
|
|
|
|
- struct cJSON *next,*prev;
|
|
|
|
- struct cJSON *child;
|
|
|
|
|
|
+---------------------
|
|
|
|
+
|
|
|
|
+ typedef struct cJSON {
|
|
|
|
+ struct cJSON *next,*prev;
|
|
|
|
+ struct cJSON *child;
|
|
|
|
|
|
- int type;
|
|
|
|
|
|
+ int type;
|
|
|
|
|
|
- char *valuestring;
|
|
|
|
- int valueint;
|
|
|
|
- double valuedouble;
|
|
|
|
|
|
+ char *valuestring;
|
|
|
|
+ int valueint;
|
|
|
|
+ double valuedouble;
|
|
|
|
|
|
- char *string;
|
|
|
|
-} cJSON;
|
|
|
|
|
|
+ char *string;
|
|
|
|
+ } cJSON;
|
|
|
|
|
|
By default all values are 0 unless set by virtue of being meaningful.
|
|
By default all values are 0 unless set by virtue of being meaningful.
|
|
|
|
|
|
@@ -146,59 +149,59 @@ the root object, and traverse the structure (which is, formally, an N-tree),
|
|
and tokenise as you please. If you wanted to build a callback style parser, this is how
|
|
and tokenise as you please. If you wanted to build a callback style parser, this is how
|
|
you'd do it (just an example, since these things are very specific):
|
|
you'd do it (just an example, since these things are very specific):
|
|
|
|
|
|
-void parse_and_callback(cJSON *item,const char *prefix)
|
|
|
|
-{
|
|
|
|
- while (item)
|
|
|
|
- {
|
|
|
|
- char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2);
|
|
|
|
- sprintf(newprefix,"%s/%s",prefix,item->name);
|
|
|
|
- int dorecurse=callback(newprefix, item->type, item);
|
|
|
|
- if (item->child && dorecurse) parse_and_callback(item->child,newprefix);
|
|
|
|
- item=item->next;
|
|
|
|
- free(newprefix);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ void parse_and_callback(cJSON *item,const char *prefix)
|
|
|
|
+ {
|
|
|
|
+ while (item)
|
|
|
|
+ {
|
|
|
|
+ char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2);
|
|
|
|
+ sprintf(newprefix,"%s/%s",prefix,item->name);
|
|
|
|
+ int dorecurse=callback(newprefix, item->type, item);
|
|
|
|
+ if (item->child && dorecurse) parse_and_callback(item->child,newprefix);
|
|
|
|
+ item=item->next;
|
|
|
|
+ free(newprefix);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
The prefix process will build you a separated list, to simplify your callback handling.
|
|
The prefix process will build you a separated list, to simplify your callback handling.
|
|
The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or
|
|
The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or
|
|
let you invoke it per-item. For the item above, your callback might look like this:
|
|
let you invoke it per-item. For the item above, your callback might look like this:
|
|
|
|
|
|
-int callback(const char *name,int type,cJSON *item)
|
|
|
|
-{
|
|
|
|
- if (!strcmp(name,"name")) { /* populate name */ }
|
|
|
|
- else if (!strcmp(name,"format/type") { /* handle "rect" */ }
|
|
|
|
- else if (!strcmp(name,"format/width") { /* 800 */ }
|
|
|
|
- else if (!strcmp(name,"format/height") { /* 600 */ }
|
|
|
|
- else if (!strcmp(name,"format/interlace") { /* false */ }
|
|
|
|
- else if (!strcmp(name,"format/frame rate") { /* 24 */ }
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
|
|
+ int callback(const char *name,int type,cJSON *item)
|
|
|
|
+ {
|
|
|
|
+ if (!strcmp(name,"name")) { /* populate name */ }
|
|
|
|
+ else if (!strcmp(name,"format/type") { /* handle "rect" */ }
|
|
|
|
+ else if (!strcmp(name,"format/width") { /* 800 */ }
|
|
|
|
+ else if (!strcmp(name,"format/height") { /* 600 */ }
|
|
|
|
+ else if (!strcmp(name,"format/interlace") { /* false */ }
|
|
|
|
+ else if (!strcmp(name,"format/frame rate") { /* 24 */ }
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
|
|
Alternatively, you might like to parse iteratively.
|
|
Alternatively, you might like to parse iteratively.
|
|
You'd use:
|
|
You'd use:
|
|
|
|
|
|
-void parse_object(cJSON *item)
|
|
|
|
-{
|
|
|
|
- int i; for (i=0;i<cJSON_GetArraySize(item);i++)
|
|
|
|
- {
|
|
|
|
- cJSON *subitem=cJSON_GetArrayItem(item,i);
|
|
|
|
- // handle subitem.
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ void parse_object(cJSON *item)
|
|
|
|
+ {
|
|
|
|
+ int i; for (i=0;i<cJSON_GetArraySize(item);i++)
|
|
|
|
+ {
|
|
|
|
+ cJSON *subitem=cJSON_GetArrayItem(item,i);
|
|
|
|
+ // handle subitem.
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
Or, for PROPER manual mode:
|
|
Or, for PROPER manual mode:
|
|
|
|
|
|
-void parse_object(cJSON *item)
|
|
|
|
-{
|
|
|
|
- cJSON *subitem=item->child;
|
|
|
|
- while (subitem)
|
|
|
|
- {
|
|
|
|
- // handle subitem
|
|
|
|
- if (subitem->child) parse_object(subitem->child);
|
|
|
|
-
|
|
|
|
- subitem=subitem->next;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ void parse_object(cJSON *item)
|
|
|
|
+ {
|
|
|
|
+ cJSON *subitem=item->child;
|
|
|
|
+ while (subitem)
|
|
|
|
+ {
|
|
|
|
+ // handle subitem
|
|
|
|
+ if (subitem->child) parse_object(subitem->child);
|
|
|
|
+
|
|
|
|
+ subitem=subitem->next;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
Of course, this should look familiar, since this is just a stripped-down version
|
|
Of course, this should look familiar, since this is just a stripped-down version
|
|
of the callback-parser.
|
|
of the callback-parser.
|
|
@@ -212,20 +215,20 @@ You can, of course, hand your sub-objects to other functions to populate.
|
|
Also, if you find a use for it, you can manually build the objects.
|
|
Also, if you find a use for it, you can manually build the objects.
|
|
For instance, suppose you wanted to build an array of objects?
|
|
For instance, suppose you wanted to build an array of objects?
|
|
|
|
|
|
-cJSON *objects[24];
|
|
|
|
-
|
|
|
|
-cJSON *Create_array_of_anything(cJSON **items,int num)
|
|
|
|
-{
|
|
|
|
- int i;cJSON *prev, *root=cJSON_CreateArray();
|
|
|
|
- for (i=0;i<24;i++)
|
|
|
|
- {
|
|
|
|
- if (!i) root->child=objects[i];
|
|
|
|
- else prev->next=objects[i], objects[i]->prev=prev;
|
|
|
|
- prev=objects[i];
|
|
|
|
- }
|
|
|
|
- return root;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
|
|
+ cJSON *objects[24];
|
|
|
|
+
|
|
|
|
+ cJSON *Create_array_of_anything(cJSON **items,int num)
|
|
|
|
+ {
|
|
|
|
+ int i;cJSON *prev, *root=cJSON_CreateArray();
|
|
|
|
+ for (i=0;i<24;i++)
|
|
|
|
+ {
|
|
|
|
+ if (!i) root->child=objects[i];
|
|
|
|
+ else prev->next=objects[i], objects[i]->prev=prev;
|
|
|
|
+ prev=objects[i];
|
|
|
|
+ }
|
|
|
|
+ return root;
|
|
|
|
+ }
|
|
|
|
+
|
|
and simply: Create_array_of_anything(objects,24);
|
|
and simply: Create_array_of_anything(objects,24);
|
|
|
|
|
|
cJSON doesn't make any assumptions about what order you create things in.
|
|
cJSON doesn't make any assumptions about what order you create things in.
|
|
@@ -235,7 +238,6 @@ of those objects.
|
|
As soon as you call cJSON_Print, it renders the structure to text.
|
|
As soon as you call cJSON_Print, it renders the structure to text.
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
The test.c code shows how to handle a bunch of typical cases. If you uncomment
|
|
The test.c code shows how to handle a bunch of typical cases. If you uncomment
|
|
the code, it'll load, parse and print a bunch of test files, also from json.org,
|
|
the code, it'll load, parse and print a bunch of test files, also from json.org,
|
|
which are more complex than I'd care to try and stash into a const char array[].
|
|
which are more complex than I'd care to try and stash into a const char array[].
|
|
@@ -243,5 +245,4 @@ which are more complex than I'd care to try and stash into a const char array[].
|
|
|
|
|
|
Enjoy cJSON!
|
|
Enjoy cJSON!
|
|
|
|
|
|
-
|
|
|
|
- Dave Gamble, Aug 2009
|
|
- Dave Gamble, Aug 2009
|