From 42000d013c9e817baf40d6445ac015991bb4a50b Mon Sep 17 00:00:00 2001 From: patacongo Date: Sat, 27 Oct 2012 18:21:26 +0000 Subject: Add port of cJSON from Darcy Gong git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5267 42af7a65-404d-4744-a932-0658087f49c3 --- apps/examples/json/Kconfig | 14 ++ apps/examples/json/Makefile | 103 +++++++++++++ apps/examples/json/README | 258 ++++++++++++++++++++++++++++++++ apps/examples/json/json_main.c | 325 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 700 insertions(+) create mode 100644 apps/examples/json/Kconfig create mode 100644 apps/examples/json/Makefile create mode 100644 apps/examples/json/README create mode 100644 apps/examples/json/json_main.c (limited to 'apps/examples/json') diff --git a/apps/examples/json/Kconfig b/apps/examples/json/Kconfig new file mode 100644 index 000000000..6d948f0d7 --- /dev/null +++ b/apps/examples/json/Kconfig @@ -0,0 +1,14 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config EXAMPLES_JSON + bool "JSON example" + default n + select NETUTILS_JSON + ---help--- + An example for the netutils/json library. + +if EXAMPLES_JSON +endif diff --git a/apps/examples/json/Makefile b/apps/examples/json/Makefile new file mode 100644 index 000000000..c2fd26bff --- /dev/null +++ b/apps/examples/json/Makefile @@ -0,0 +1,103 @@ +############################################################################ +# apps/examples/json/Makefile +# +# Copyright (C) 2012 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# cJSON built-in application info + +APPNAME = json +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 2048 + +ASRCS = +CSRCS = json_main.c + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(WINTOOL),y) + BIN = "${shell cygpath -w $(APPDIR)/libapps$(LIBEXT)}" +else + BIN = "$(APPDIR)/libapps$(LIBEXT)" +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: clean depend distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $(BIN), $${obj}); \ + done ; ) + @touch .built + +.context: +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) + $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main) + @touch $@ +endif + +context: .context + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f *.o *~ .*.swp .built + $(call CLEAN) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep diff --git a/apps/examples/json/README b/apps/examples/json/README new file mode 100644 index 000000000..de9a0b2ed --- /dev/null +++ b/apps/examples/json/README @@ -0,0 +1,258 @@ +apps/examples/json/README.txt +============================= + +This directory contains logic taken from the cJSON project: + +http://sourceforge.net/projects/cjson/ + +This corresponds to SVN revision r42 (with lots of changes for NuttX coding +standards). As of r42, the SVN repository was last updated on 2011-10-10 +so I presume that the code is stable and there is no risk of maintaining +duplicate logic in the NuttX repository. + +Contents +======== + + o License + o Welcome to cJSON + +License +======= + + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Welcome to cJSON +================ + +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. + +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 +generally represent your program's state. + +First up, how do I build? +Add cJSON.c to your project, and put cJSON.h somewhere in the header search path. +For example, to build the test app: + +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 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. + +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 +philosophy as JSON itself. Simple, dumb, out of the way. + +Some JSON: +{ + "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, +you have a char * to it. Everything is a cJSON struct. +Get it parsed: + 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. +What's the framerate? + + cJSON *format = cJSON_GetObjectItem(root,"format"); + int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint; + +Want to change the framerate? + cJSON_GetObjectItem(format,"frame rate")->valueint=25; + +Back to disk? + char *rendered=cJSON_Print(root); + +Finished? Delete the root (this takes care of everything else). + cJSON_Delete(root); + +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? + 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. +Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and +a few from elsewhere. + +What about manual mode? First up you need some detail. +Let's cover how the cJSON objects represent the JSON data. +cJSON doesn't distinguish arrays from objects in handling; just type. +Each cJSON has, potentially, a child, siblings, value, a name. + +The root object has: Object Type and a Child +The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling: +Sibling has type Object, name "format", and a child. +That child has type String, name "type", value "rect", and a sibling: +Sibling has type Number, name "width", value 1920, and a sibling: +Sibling has type Number, name "height", value 1080, and a sibling: +Sibling hs type False, name "interlace", and a sibling: +Sibling has type Number, name "frame rate", value 24 + +Here's the structure: +typedef struct cJSON { + struct cJSON *next,*prev; + struct cJSON *child; + + int type; + + char *valuestring; + int valueint; + double valuedouble; + + char *string; +} cJSON; + +By default all values are 0 unless set by virtue of being meaningful. + +next/prev is a doubly linked list of siblings. next takes you to your sibling, +prev takes you back from your sibling to you. +Only objects and arrays have a "child", and it's the head of the doubly linked list. +A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0. +The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in +cJSON.h + +A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read +valuedouble. + +Any entry which is in the linked list which is the child of an object will have a "string" +which is the "name" of the entry. When I said "name" in the above example, that's "string". +"string" is the JSON name for the 'variable name' if you will. + +Now you can trivially walk the lists, recursively, and parse as you please. +You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take +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 +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); + } +} + +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 +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; +} + +Alternatively, you might like to parse iteratively. +You'd use: + +void parse_object(cJSON *item) +{ + int i; for (i=0;ichild; + 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 the callback-parser. + +This should cover most uses you'll find for parsing. The rest should be possible +to infer.. and if in doubt, read the source! There's not a lot of it! ;) + +In terms of constructing JSON data, the example code above is the right way to do it. +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. +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; +} + +and simply: Create_array_of_anything(objects,24); + +cJSON doesn't make any assumptions about what order you create things in. +You can attach the objects, as above, and later add children to each +of those objects. + +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 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[]. + +Enjoy cJSON! + +- Dave Gamble, Aug 2009 diff --git a/apps/examples/json/json_main.c b/apps/examples/json/json_main.c new file mode 100644 index 000000000..443be387b --- /dev/null +++ b/apps/examples/json/json_main.c @@ -0,0 +1,325 @@ +/**************************************************************************** + * apps/examples/json/json_main.c + * + * This file is a part of NuttX: + * + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Ported by: Darcy Gong + * + * And derives from the cJSON Project which has an MIT license: + * + * Copyright (c) 2009 Dave Gamble + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Used by some code below as an example datatype. */ + +struct record +{ + const char *precision; + double lat; + double lon; + const char *address; + const char *city; + const char *state; + const char *zip; + const char *country; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: doit + * + * Description: + * Parse text to JSON, then render back to text, and print! + * + ****************************************************************************/ + +static void doit(const char *text) +{ + char *out; + cJSON *json; + json = cJSON_Parse(text); + if (!json) + { + printf("Error before: [%s]\n", cJSON_GetErrorPtr()); + } + else + { + out = cJSON_Print(json); + cJSON_Delete(json); + printf("%s\n", out); + free(out); + } +} + +/**************************************************************************** + * Name: dofile + * + * Description: + * Read a file, parse, render back, etc. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static void dofile(char *filename) +{ + FILE *f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + char *data = malloc(len + 1); + fread(data, 1, len, f); + fclose(f); + doit(data); + free(data); +} +#endif + +/**************************************************************************** + * Name: create_objects + * + * Description: + * Create a bunch of objects as demonstration. + * + ****************************************************************************/ + +static void create_objects(void) +{ + cJSON *root; + cJSON *fmt; + cJSON *img; + cJSON *thm; + cJSON *fld; + char *out; + int i; + + /* Our "days of the week" array */ + + static const char *strings[7] = + { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }; + + /* Our matrix: */ + + static const int numbers[3][3] = { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} }; + + /* Our "gallery" item: */ + + static const int ids[4] = { 116, 943, 234, 38793 }; + + /* Our array of "records": */ + + static const struct record fields[2] = + { + {"zip", 37.7668, -1.223959e+2, "", "SAN FRANCISCO", "CA", "94107", "US"}, + {"zip", 37.371991, -1.22026e+2, "", "SUNNYVALE", "CA", "94085", "US"} + }; + + /* Here we construct some JSON standards, from the JSON site. */ + /* Our "Video" datatype: */ + + 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); + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + /* Print to text, Delete the cJSON, print it, release the string. */ + + root = cJSON_CreateStringArray(strings, 7); + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + root = cJSON_CreateArray(); + for (i = 0; i < 3; i++) + { + cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3)); + } + +/*cJSON_ReplaceItemInArray(root,1,cJSON_CreateString("Replacement")); */ + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + root = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject()); + cJSON_AddNumberToObject(img, "Width", 800); + cJSON_AddNumberToObject(img, "Height", 600); + cJSON_AddStringToObject(img, "Title", "View from 15th Floor"); + cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject()); + cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943"); + cJSON_AddNumberToObject(thm, "Height", 125); + cJSON_AddStringToObject(thm, "Width", "100"); + cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4)); + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + root = cJSON_CreateArray(); + for (i = 0; i < 2; i++) + { + cJSON_AddItemToArray(root, fld = cJSON_CreateObject()); + cJSON_AddStringToObject(fld, "precision", fields[i].precision); + cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat); + cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon); + cJSON_AddStringToObject(fld, "Address", fields[i].address); + cJSON_AddStringToObject(fld, "City", fields[i].city); + cJSON_AddStringToObject(fld, "State", fields[i].state); + cJSON_AddStringToObject(fld, "Zip", fields[i].zip); + cJSON_AddStringToObject(fld, "Country", fields[i].country); + } + +/*cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root,1),"City",cJSON_CreateIntArray(ids,4)); */ + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: + * + * Description: + * + * + ****************************************************************************/ + +int json_main(int argc, const char *argv[]) +{ + /* a bunch of json: */ + + static const char text1[] = + "{\n" + " \"name\": \"Jack (\\\"Bee\\\") Nimble\",\n" + " \"format\": {\n" + " \"type\": \"rect\",\n" + " \"width\": 1920,\n" + " \"height\": 1080,\n" + " \"interlace\": false,\n" + " \"frame rate\": 24\n" + " }\n" + "}"; + + static const char text2[] = + "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]"; + + static const char text3[] = + "[\n" + " [0, -1, 0],\n" + " [1, 0, 0],\n" + " [0, 0, 1]\n" + "]\n"; + + static const char text4[] = + "{\n" + " \"Image\": {\n" + " \"Width\": 800,\n" + " \"Height\": 600,\n" + " \"Title\": \"View from 15th Floor\",\n" + " \"Thumbnail\": {\n" + " \"Url\": \"http:/*www.example.com/image/481989943\",\n" + " \"Height\": 125,\n" + " \"Width\": \"100\"\n" + " },\n" + " \"IDs\": [116, 943, 234, 38793]\n" + " }\n" + "}"; + + static const char text5[] = + "[\n" + " {\n" + " \"precision\": \"zip\",\n" + " \"Latitude\": 37.7668,\n" + " \"Longitude\": -122.3959,\n" + " \"Address\": \"\",\n" + " \"City\": \"SAN FRANCISCO\",\n" + " \"State\": \"CA\",\n" + " \"Zip\": \"94107\",\n" + " \"Country\": \"US\"\n" + " },\n" + " {\n" + " \"precision\": \"zip\",\n" + " \"Latitude\": 37.371991,\n" + " \"Longitude\": -122.026020,\n" + " \"Address\": \"\",\n" + " \"City\": \"SUNNYVALE\",\n" + " \"State\": \"CA\",\n" + " \"Zip\": \"94085\",\n" + " \"Country\": \"US\"\n" + " }\n" + "]"; + + /* Process each json textblock by parsing, then rebuilding: */ doit(text1); + + doit(text2); + doit(text3); + doit(text4); + doit(text5); + + /* Parse standard testfiles: */ + +#if 0 /* Not yet */ + dofile("../../tests/test1"); + dofile("../../tests/test2"); + dofile("../../tests/test3"); + dofile("../../tests/test4"); + dofile("../../tests/test5"); +#endif + + /* Now some samplecode for building objects concisely: */ + + create_objects(); + return 0; +} -- cgit v1.2.3