diff --git a/cJSON.c b/cJSON.c index 62fd4bcc850ff44c832e453f5245bc61e3450870..cfb5057f22cdbc057333229f38840b3397ab3746 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1722,7 +1722,7 @@ cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) /* item doesn't exist */ return 0; } - if (c->prev) + if (c->prev) { /* not the first element */ c->prev->next = c->next; diff --git a/cJSON.h b/cJSON.h index 6b535a1a2143b15365531024ade8ddc5853a9120..c2b8d572304957615c33ab4aee52323959eb5a66 100644 --- a/cJSON.h +++ b/cJSON.h @@ -1,16 +1,16 @@ /* 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 @@ -36,7 +36,7 @@ extern "C" #define cJSON_String (1 << 4) #define cJSON_Array (1 << 5) #define cJSON_Object (1 << 6) - + #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 @@ -93,7 +93,7 @@ extern cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string); extern int cJSON_HasObjectItem(const cJSON *object, const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ extern const char *cJSON_GetErrorPtr(void); - + /* These calls create a cJSON item of the appropriate type. */ extern cJSON *cJSON_CreateNull(void); extern cJSON *cJSON_CreateTrue(void); diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 82aafeeea2e49b490797546274918537830daeb9..618b89f1c5c47cbd31ad2daa8450ea0f1efa367d 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -4,392 +4,810 @@ #include #include "cJSON_Utils.h" -static int cJSONUtils_strcasecmp(const char *s1,const char *s2) +static int cJSONUtils_strcasecmp(const char *s1, const char *s2) { - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); + if (!s1) + { + return (s1 == s2) ? 0 : 1; /* both NULL? */ + } + if (!s2) + { + return 1; + } + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) + { + if(*s1 == 0) + { + return 0; + } + } + + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); } /* JSON Pointer implementation: */ -static int cJSONUtils_Pstrcasecmp(const char *a,const char *e) +static int cJSONUtils_Pstrcasecmp(const char *a, const char *e) { - if (!a || !e) return (a==e)?0:1; - for (;*a && *e && *e!='/';a++,e++) { - if (*e=='~') {if (!(e[1]=='0' && *a=='~') && !(e[1]=='1' && *a=='/')) return 1; else e++;} - else if (tolower(*a)!=tolower(*e)) return 1; - } - if ((*e!=0 && *e!='/') != (*a!=0)) return 1; - return 0; + if (!a || !e) + { + return (a == e) ? 0 : 1; /* both NULL? */ + } + for (; *a && *e && (*e != '/'); a++, e++) /* compare until next '/' */ + { + if (*e == '~') + { + /* check for escaped '~' (~0) and '/' (~1) */ + if (!((e[1] == '0') && (*a == '~')) && !((e[1] == '1') && (*a == '/'))) + { + /* invalid escape sequence or wrong character in *a */ + return 1; + } + else + { + e++; + } + } + else if (tolower(*a) != tolower(*e)) + { + return 1; + } + } + if (((*e != 0) && (*e != '/')) != (*a != 0)) + { + /* one string has ended, the other not */ + return 1; + } + + return 0; } -static int cJSONUtils_PointerEncodedstrlen(const char *s) {int l=0;for (;*s;s++,l++) if (*s=='~' || *s=='/') l++;return l;} +static int cJSONUtils_PointerEncodedstrlen(const char *s) +{ + int l = 0; + for (; *s; s++, l++) + { + if ((*s == '~') || (*s == '/')) + { + l++; + } + } + + return l; +} -static void cJSONUtils_PointerEncodedstrcpy(char *d,const char *s) +static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s) { - for (;*s;s++) - { - if (*s=='/') {*d++='~';*d++='1';} - else if (*s=='~') {*d++='~';*d++='0';} - else *d++=*s; - } - *d=0; + for (; *s; s++) + { + if (*s == '/') + { + *d++ = '~'; + *d++ = '1'; + } + else if (*s == '~') + { + *d++ = '~'; + *d++ = '0'; + } + else + { + *d++ = *s; + } + } + + *d = '\0'; } -char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target) +char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target) { - int type=object->type,c=0;cJSON *obj=0; - - if (object==target) return strdup(""); - - for (obj=object->child;obj;obj=obj->next,c++) - { - char *found=cJSONUtils_FindPointerFromObjectTo(obj,target); - if (found) - { - if (type==cJSON_Array) - { - char *ret=(char*)malloc(strlen(found)+23); - sprintf(ret,"/%d%s",c,found); - free(found); - return ret; - } - else if (type==cJSON_Object) - { - char *ret=(char*)malloc(strlen(found)+cJSONUtils_PointerEncodedstrlen(obj->string)+2); - *ret='/';cJSONUtils_PointerEncodedstrcpy(ret+1,obj->string); - strcat(ret,found); - free(found); - return ret; - } - free(found); - return 0; - } - } - return 0; + int type = object->type; + int c = 0; + cJSON *obj = 0; + + if (object == target) + { + /* found */ + return strdup(""); + } + + /* recursively search all children of the object */ + for (obj = object->child; obj; obj = obj->next, c++) + { + char *found = cJSONUtils_FindPointerFromObjectTo(obj, target); + if (found) + { + if (type == cJSON_Array) + { + /* reserve enough memory for a 64 bit integer + '/' and '\0' */ + char *ret = (char*)malloc(strlen(found) + 23); + sprintf(ret, "/%d%s", c, found); /* / */ + free(found); + + return ret; + } + else if (type == cJSON_Object) + { + char *ret = (char*)malloc(strlen(found) + cJSONUtils_PointerEncodedstrlen(obj->string) + 2); + *ret = '/'; + cJSONUtils_PointerEncodedstrcpy(ret + 1, obj->string); + strcat(ret, found); + free(found); + + return ret; + } + + /* reached leaf of the tree, found nothing */ + free(found); + return 0; + } + } + + /* not found */ + return 0; } -cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer) +cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer) { - while (*pointer++=='/' && object) - { - if (object->type==cJSON_Array) - { - int which=0; while (*pointer>='0' && *pointer<='9') which=(10*which) + *pointer++ - '0'; - if (*pointer && *pointer!='/') return 0; - object=cJSON_GetArrayItem(object,which); - } - else if (object->type==cJSON_Object) - { - object=object->child; while (object && cJSONUtils_Pstrcasecmp(object->string,pointer)) object=object->next; /* GetObjectItem. */ - while (*pointer && *pointer!='/') pointer++; - } - else return 0; - } - return object; + /* follow path of the pointer */ + while ((*pointer++ == '/') && object) + { + if (object->type == cJSON_Array) + { + int which = 0; + /* parse array index */ + while ((*pointer >= '0') && (*pointer <= '9')) + { + which = (10 * which) + (*pointer++ - '0'); + } + if (*pointer && (*pointer != '/')) + { + /* not end of string or new path token */ + return 0; + } + object = cJSON_GetArrayItem(object, which); + } + else if (object->type == cJSON_Object) + { + object = object->child; + /* GetObjectItem. */ + while (object && cJSONUtils_Pstrcasecmp(object->string, pointer)) + { + object = object->next; + } + /* skip to the next path token or end of string */ + while (*pointer && (*pointer != '/')) + { + pointer++; + } + } + else + { + return 0; + } + } + + return object; } /* JSON Patch implementation. */ static void cJSONUtils_InplaceDecodePointerString(char *string) { - char *s2=string; - for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/'); - *s2=0; + char *s2 = string; + for (; *string; s2++, string++) + { + *s2 = (*string != '~') + ? (*string) + : ((*(++string) == '0') + ? '~' + : '/'); + } + + *s2 = '\0'; } -static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path) +static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path) { - char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0; - - parentptr=strdup(path); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0; - parent=cJSONUtils_GetPointer(object,parentptr); - cJSONUtils_InplaceDecodePointerString(childptr); - - if (!parent) ret=0; /* Couldn't find object to remove child from. */ - else if (parent->type==cJSON_Array) ret=cJSON_DetachItemFromArray(parent,atoi(childptr)); - else if (parent->type==cJSON_Object) ret=cJSON_DetachItemFromObject(parent,childptr); - free(parentptr); - return ret; + char *parentptr = 0; + char *childptr = 0; + cJSON *parent = 0; + cJSON *ret = 0; + + /* copy path and split it in parent and child */ + parentptr = strdup(path); + childptr = strrchr(parentptr, '/'); /* last '/' */ + if (childptr) + { + /* split strings */ + *childptr++ = '\0'; + } + parent = cJSONUtils_GetPointer(object, parentptr); + cJSONUtils_InplaceDecodePointerString(childptr); + + if (!parent) + { + /* Couldn't find object to remove child from. */ + ret = 0; + } + else if (parent->type == cJSON_Array) + { + ret = cJSON_DetachItemFromArray(parent, atoi(childptr)); + } + else if (parent->type == cJSON_Object) + { + ret = cJSON_DetachItemFromObject(parent, childptr); + } + free(parentptr); + + /* return the detachted item */ + return ret; } -static int cJSONUtils_Compare(cJSON *a,cJSON *b) +static int cJSONUtils_Compare(cJSON *a, cJSON *b) { - if (a->type!=b->type) return -1; /* mismatched type. */ - switch (a->type) - { - case cJSON_Number: return (a->valueint!=b->valueint || a->valuedouble!=b->valuedouble)?-2:0; /* numeric mismatch. */ - case cJSON_String: return (strcmp(a->valuestring,b->valuestring)!=0)?-3:0; /* string mismatch. */ - case cJSON_Array: for (a=a->child,b=b->child;a && b;a=a->next,b=b->next) {int err=cJSONUtils_Compare(a,b);if (err) return err;} - return (a || b)?-4:0; /* array size mismatch. */ - case cJSON_Object: - cJSONUtils_SortObject(a); - cJSONUtils_SortObject(b); - a=a->child,b=b->child; - while (a && b) - { - int err; - if (cJSONUtils_strcasecmp(a->string,b->string)) return -6; /* missing member */ - err=cJSONUtils_Compare(a,b);if (err) return err; - a=a->next,b=b->next; - } - return (a || b)?-5:0; /* object length mismatch */ - - default: break; - } - return 0; + if (a->type != b->type) + { + /* mismatched type. */ + return -1; + } + switch (a->type) + { + case cJSON_Number: + /* numeric mismatch. */ + return ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) ? -2 : 0; + case cJSON_String: + /* string mismatch. */ + return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0; + case cJSON_Array: + for (a = a->child, b = b->child; a && b; a = a->next, b = b->next) + { + int err = cJSONUtils_Compare(a, b); + if (err) + { + return err; + } + } + /* array size mismatch? (one of both children is not NULL) */ + return (a || b) ? -4 : 0; + case cJSON_Object: + cJSONUtils_SortObject(a); + cJSONUtils_SortObject(b); + a = a->child; + b = b->child; + while (a && b) + { + int err; + /* compare object keys */ + if (cJSONUtils_strcasecmp(a->string, b->string)) + { + /* missing member */ + return -6; + } + err = cJSONUtils_Compare(a, b); + if (err) + { + return err; + } + a = a->next; + b = b->next; + } + /* object length mismatch (one of both children is not null) */ + return (a || b) ? -5 : 0; + + default: + break; + } + /* null, true or false */ + return 0; } -static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch) +static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch) { - cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0; - - op=cJSON_GetObjectItem(patch,"op"); - path=cJSON_GetObjectItem(patch,"path"); - if (!op || !path) return 2; /* malformed patch. */ - - if (!strcmp(op->valuestring,"add")) opcode=0; - else if (!strcmp(op->valuestring,"remove")) opcode=1; - else if (!strcmp(op->valuestring,"replace"))opcode=2; - else if (!strcmp(op->valuestring,"move")) opcode=3; - else if (!strcmp(op->valuestring,"copy")) opcode=4; - else if (!strcmp(op->valuestring,"test")) return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value")); - else return 3; /* unknown opcode. */ - - if (opcode==1 || opcode==2) /* Remove/Replace */ - { - cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring)); /* Get rid of old. */ - if (opcode==1) return 0; /* For Remove, this is job done. */ - } - - if (opcode==3 || opcode==4) /* Copy/Move uses "from". */ - { - cJSON *from=cJSON_GetObjectItem(patch,"from"); if (!from) return 4; /* missing "from" for copy/move. */ - - if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring); - if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring); - if (!value) return 5; /* missing "from" for copy/move. */ - if (opcode==4) value=cJSON_Duplicate(value,1); - if (!value) return 6; /* out of memory for copy/move. */ - } - else /* Add/Replace uses "value". */ - { - value=cJSON_GetObjectItem(patch,"value"); - if (!value) return 7; /* missing "value" for add/replace. */ - value=cJSON_Duplicate(value,1); - if (!value) return 8; /* out of memory for add/replace. */ - } - - /* Now, just add "value" to "path". */ - - parentptr=strdup(path->valuestring); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0; - parent=cJSONUtils_GetPointer(object,parentptr); - cJSONUtils_InplaceDecodePointerString(childptr); - - /* add, remove, replace, move, copy, test. */ - if (!parent) {free(parentptr); cJSON_Delete(value); return 9;} /* Couldn't find object to add to. */ - else if (parent->type==cJSON_Array) - { - if (!strcmp(childptr,"-")) cJSON_AddItemToArray(parent,value); - else cJSON_InsertItemInArray(parent,atoi(childptr),value); - } - else if (parent->type==cJSON_Object) - { - cJSON_DeleteItemFromObject(parent,childptr); - cJSON_AddItemToObject(parent,childptr,value); - } - else - { - cJSON_Delete(value); - } - free(parentptr); - return 0; + cJSON *op = 0; + cJSON *path = 0; + cJSON *value = 0; + cJSON *parent = 0; + int opcode = 0; + char *parentptr = 0; + char *childptr = 0; + + op = cJSON_GetObjectItem(patch, "op"); + path = cJSON_GetObjectItem(patch, "path"); + if (!op || !path) + { + /* malformed patch. */ + return 2; + } + + /* decode operation */ + if (!strcmp(op->valuestring, "add")) + { + opcode = 0; + } + else if (!strcmp(op->valuestring, "remove")) + { + opcode = 1; + } + else if (!strcmp(op->valuestring, "replace")) + { + opcode = 2; + } + else if (!strcmp(op->valuestring, "move")) + { + opcode = 3; + } + else if (!strcmp(op->valuestring, "copy")) + { + opcode = 4; + } + else if (!strcmp(op->valuestring, "test")) + { + /* compare value: {...} with the given path */ + return cJSONUtils_Compare(cJSONUtils_GetPointer(object, path->valuestring), cJSON_GetObjectItem(patch, "value")); + } + else + { + /* unknown opcode. */ + return 3; + } + + /* Remove/Replace */ + if ((opcode == 1) || (opcode == 2)) + { + /* Get rid of old. */ + cJSON_Delete(cJSONUtils_PatchDetach(object, path->valuestring)); + if (opcode == 1) + { + /* For Remove, this is job done. */ + return 0; + } + } + + /* Copy/Move uses "from". */ + if ((opcode == 3) || (opcode == 4)) + { + cJSON *from = cJSON_GetObjectItem(patch, "from"); + if (!from) + { + /* missing "from" for copy/move. */ + return 4; + } + + if (opcode == 3) + { + /* move */ + value = cJSONUtils_PatchDetach(object, from->valuestring); + } + if (opcode == 4) + { + /* copy */ + value = cJSONUtils_GetPointer(object, from->valuestring); + } + if (!value) + { + /* missing "from" for copy/move. */ + return 5; + } + if (opcode == 4) + { + value = cJSON_Duplicate(value, 1); + } + if (!value) + { + /* out of memory for copy/move. */ + return 6; + } + } + else /* Add/Replace uses "value". */ + { + value = cJSON_GetObjectItem(patch, "value"); + if (!value) + { + /* missing "value" for add/replace. */ + return 7; + } + value = cJSON_Duplicate(value, 1); + if (!value) + { + /* out of memory for add/replace. */ + return 8; + } + } + + /* Now, just add "value" to "path". */ + + /* split pointer in parent and child */ + parentptr = strdup(path->valuestring); + childptr = strrchr(parentptr, '/'); + if (childptr) + { + *childptr++ = '\0'; + } + parent = cJSONUtils_GetPointer(object, parentptr); + cJSONUtils_InplaceDecodePointerString(childptr); + + /* add, remove, replace, move, copy, test. */ + if (!parent) + { + /* Couldn't find object to add to. */ + free(parentptr); + cJSON_Delete(value); + return 9; + } + else if (parent->type == cJSON_Array) + { + if (!strcmp(childptr, "-")) + { + cJSON_AddItemToArray(parent, value); + } + else + { + cJSON_InsertItemInArray(parent, atoi(childptr), value); + } + } + else if (parent->type == cJSON_Object) + { + cJSON_DeleteItemFromObject(parent, childptr); + cJSON_AddItemToObject(parent, childptr, value); + } + else + { + cJSON_Delete(value); + } + free(parentptr); + + return 0; } - -int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches) +int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches) { - int err; - if (patches->type!=cJSON_Array) return 1; /* malformed patches. */ - if (patches) patches=patches->child; - while (patches) - { - if ((err=cJSONUtils_ApplyPatch(object,patches))) return err; - patches=patches->next; - } - return 0; + int err; + if (patches->type != cJSON_Array) + { + /* malformed patches. */ + return 1; + } + if (patches) + { + patches = patches->child; + } + while (patches) + { + if ((err = cJSONUtils_ApplyPatch(object, patches))) + { + return err; + } + patches = patches->next; + } + + return 0; } -static void cJSONUtils_GeneratePatch(cJSON *patches,const char *op,const char *path,const char *suffix,cJSON *val) +static void cJSONUtils_GeneratePatch(cJSON *patches, const char *op, const char *path, const char *suffix, cJSON *val) { - cJSON *patch=cJSON_CreateObject(); - cJSON_AddItemToObject(patch,"op",cJSON_CreateString(op)); - if (suffix) - { - char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(suffix)+2); - cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),suffix); - cJSON_AddItemToObject(patch,"path",cJSON_CreateString(newpath)); - free(newpath); - } - else cJSON_AddItemToObject(patch,"path",cJSON_CreateString(path)); - if (val) cJSON_AddItemToObject(patch,"value",cJSON_Duplicate(val,1)); - cJSON_AddItemToArray(patches,patch); + cJSON *patch = cJSON_CreateObject(); + cJSON_AddItemToObject(patch, "op", cJSON_CreateString(op)); + if (suffix) + { + char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(suffix) + 2); + cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), suffix); + cJSON_AddItemToObject(patch, "path", cJSON_CreateString(newpath)); + free(newpath); + } + else + { + cJSON_AddItemToObject(patch, "path", cJSON_CreateString(path)); + } + if (val) + { + cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(val, 1)); + } + cJSON_AddItemToArray(patches, patch); } -void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val) {cJSONUtils_GeneratePatch(array,op,path,0,val);} - -static void cJSONUtils_CompareToPatch(cJSON *patches,const char *path,cJSON *from,cJSON *to) +void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val) { - if (from->type!=to->type) {cJSONUtils_GeneratePatch(patches,"replace",path,0,to); return; } - - switch (from->type) - { - case cJSON_Number: - if (from->valueint!=to->valueint || from->valuedouble!=to->valuedouble) - cJSONUtils_GeneratePatch(patches,"replace",path,0,to); - return; - - case cJSON_String: - if (strcmp(from->valuestring,to->valuestring)!=0) - cJSONUtils_GeneratePatch(patches,"replace",path,0,to); - return; - - case cJSON_Array: - { - int c;char *newpath=(char*)malloc(strlen(path)+23); /* Allow space for 64bit int. */ - for (c=0,from=from->child,to=to->child;from && to;from=from->next,to=to->next,c++){ - sprintf(newpath,"%s/%d",path,c); cJSONUtils_CompareToPatch(patches,newpath,from,to); - } - for (;from;from=from->next,c++) {sprintf(newpath,"%d",c); cJSONUtils_GeneratePatch(patches,"remove",path,newpath,0); } - for (;to;to=to->next,c++) cJSONUtils_GeneratePatch(patches,"add",path,"-",to); - free(newpath); - return; - } - - case cJSON_Object: - { - cJSON *a,*b; - cJSONUtils_SortObject(from); - cJSONUtils_SortObject(to); - - a=from->child,b=to->child; - while (a || b) - { - int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string); - if (!diff) - { - char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2); - cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string); - cJSONUtils_CompareToPatch(patches,newpath,a,b); - free(newpath); - a=a->next; - b=b->next; - } - else if (diff<0) {cJSONUtils_GeneratePatch(patches,"remove",path,a->string,0); a=a->next;} - else {cJSONUtils_GeneratePatch(patches,"add",path,b->string,b); b=b->next;} - } - return; - } - - default: break; - } + cJSONUtils_GeneratePatch(array, op, path, 0, val); } - -cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to) +static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *from, cJSON *to) { - cJSON *patches=cJSON_CreateArray(); - cJSONUtils_CompareToPatch(patches,"",from,to); - return patches; + if (from->type != to->type) + { + cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); + return; + } + + switch (from->type) + { + case cJSON_Number: + if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) + { + cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); + } + return; + + case cJSON_String: + if (strcmp(from->valuestring, to->valuestring) != 0) + { + cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); + } + return; + + case cJSON_Array: + { + int c; + char *newpath = (char*)malloc(strlen(path) + 23); /* Allow space for 64bit int. */ + /* generate patches for all array elements that exist in "from" and "to" */ + for (c = 0, from = from->child, to = to->child; from && to; from = from->next, to = to->next, c++) + { + sprintf(newpath, "%s/%d", path, c); /* path of the current array element */ + cJSONUtils_CompareToPatch(patches, newpath, from, to); + } + /* remove leftover elements from 'from' that are not in 'to' */ + for (; from; from = from->next, c++) + { + sprintf(newpath, "%d", c); + cJSONUtils_GeneratePatch(patches, "remove", path, newpath, 0); + } + /* add new elements in 'to' that were not in 'from' */ + for (; to; to = to->next, c++) + { + cJSONUtils_GeneratePatch(patches, "add", path, "-", to); + } + free(newpath); + return; + } + + case cJSON_Object: + { + cJSON *a; + cJSON *b; + cJSONUtils_SortObject(from); + cJSONUtils_SortObject(to); + + a = from->child; + b = to->child; + /* for all object values in the object with more of them */ + while (a || b) + { + int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp(a->string, b->string)); + if (!diff) + { + /* both object keys are the same */ + char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(a->string) + 2); + cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), a->string); + /* create a patch for the element */ + cJSONUtils_CompareToPatch(patches, newpath, a, b); + free(newpath); + a = a->next; + b = b->next; + } + else if (diff < 0) + { + /* object element doesn't exist in 'to' --> remove it */ + cJSONUtils_GeneratePatch(patches, "remove", path, a->string, 0); + a = a->next; + } + else + { + /* object element doesn't exist in 'from' --> add it */ + cJSONUtils_GeneratePatch(patches, "add", path, b->string, b); + b = b->next; + } + } + return; + } + + default: + break; + } } +cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to) +{ + cJSON *patches = cJSON_CreateArray(); + cJSONUtils_CompareToPatch(patches, "", from, to); + + return patches; +} +/* sort lists using mergesort */ static cJSON *cJSONUtils_SortList(cJSON *list) { - cJSON *first=list,*second=list,*ptr=list; - - if (!list || !list->next) return list; /* One entry is sorted already. */ - - while (ptr && ptr->next && cJSONUtils_strcasecmp(ptr->string,ptr->next->string)<0) ptr=ptr->next; /* Test for list sorted. */ - if (!ptr || !ptr->next) return list; /* Leave sorted lists unmodified. */ - ptr=list; - - while (ptr) {second=second->next;ptr=ptr->next;if (ptr) ptr=ptr->next;} /* Walk two pointers to find the middle. */ - if (second && second->prev) second->prev->next=0; /* Split the lists */ - - first=cJSONUtils_SortList(first); /* Recursively sort the sub-lists. */ - second=cJSONUtils_SortList(second); - list=ptr=0; - - while (first && second) /* Merge the sub-lists */ - { - if (cJSONUtils_strcasecmp(first->string,second->string)<0) - { - if (!list) list=ptr=first; - else {ptr->next=first;first->prev=ptr;ptr=first;} - first=first->next; - } - else - { - if (!list) list=ptr=second; - else {ptr->next=second;second->prev=ptr;ptr=second;} - second=second->next; - } - } - if (first) { if (!list) return first; ptr->next=first; first->prev=ptr; } /* Append any tails. */ - if (second) { if (!list) return second; ptr->next=second; second->prev=ptr; } - - return list; + cJSON *first = list; + cJSON *second = list; + cJSON *ptr = list; + + if (!list || !list->next) + { + /* One entry is sorted already. */ + return list; + } + + while (ptr && ptr->next && (cJSONUtils_strcasecmp(ptr->string, ptr->next->string) < 0)) + { + /* Test for list sorted. */ + ptr = ptr->next; + } + if (!ptr || !ptr->next) + { + /* Leave sorted lists unmodified. */ + return list; + } + + /* reset ptr to the beginning */ + ptr = list; + while (ptr) + { + /* Walk two pointers to find the middle. */ + second = second->next; + ptr = ptr->next; + /* advances ptr two steps at a time */ + if (ptr) + { + ptr = ptr->next; + } + } + if (second && second->prev) + { + /* Split the lists */ + second->prev->next = 0; + } + + /* Recursively sort the sub-lists. */ + first = cJSONUtils_SortList(first); + second = cJSONUtils_SortList(second); + list = ptr = 0; + + while (first && second) /* Merge the sub-lists */ + { + if (cJSONUtils_strcasecmp(first->string, second->string) < 0) + { + if (!list) + { + /* start merged list with the first element of the first list */ + list = ptr = first; + } + else + { + /* add first element of first list to merged list */ + ptr->next = first; + first->prev = ptr; + ptr = first; + } + first = first->next; + } + else + { + if (!list) + { + /* start merged list with the first element of the second list */ + list = ptr = second; + } + else + { + /* add first element of second list to merged list */ + ptr->next = second; + second->prev = ptr; + ptr = second; + } + second = second->next; + } + } + if (first) + { + /* Append rest of first list. */ + if (!list) + { + return first; + } + ptr->next = first; + first->prev = ptr; + } + if (second) + { + /* Append rest of second list */ + if (!list) + { + return second; + } + ptr->next = second; + second->prev = ptr; + } + + return list; } -void cJSONUtils_SortObject(cJSON *object) {object->child=cJSONUtils_SortList(object->child);} +void cJSONUtils_SortObject(cJSON *object) +{ + object->child = cJSONUtils_SortList(object->child); +} cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch) { - if (!patch || patch->type != cJSON_Object) {cJSON_Delete(target);return cJSON_Duplicate(patch,1);} - if (!target || target->type != cJSON_Object) {cJSON_Delete(target);target=cJSON_CreateObject();} - - patch=patch->child; - while (patch) - { - if (patch->type == cJSON_NULL) cJSON_DeleteItemFromObject(target,patch->string); - else - { - cJSON *replaceme=cJSON_DetachItemFromObject(target,patch->string); - cJSON_AddItemToObject(target,patch->string,cJSONUtils_MergePatch(replaceme,patch)); - } - patch=patch->next; - } - return target; + if (!patch || (patch->type != cJSON_Object)) + { + /* scalar value, array or NULL, just duplicate */ + cJSON_Delete(target); + return cJSON_Duplicate(patch, 1); + } + + if (!target || (target->type != cJSON_Object)) + { + cJSON_Delete(target); + target = cJSON_CreateObject(); + } + + patch = patch->child; + while (patch) + { + if (patch->type == cJSON_NULL) + { + /* NULL is the indicator to remove a value, see RFC7396 */ + cJSON_DeleteItemFromObject(target, patch->string); + } + else + { + cJSON *replaceme = cJSON_DetachItemFromObject(target, patch->string); + cJSON_AddItemToObject(target, patch->string, cJSONUtils_MergePatch(replaceme, patch)); + } + patch = patch->next; + } + return target; } -cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to) +cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to) { - cJSON *patch=0; - if (!to) return cJSON_CreateNull(); - if (to->type!=cJSON_Object || !from || from->type!=cJSON_Object) return cJSON_Duplicate(to,1); - cJSONUtils_SortObject(from); - cJSONUtils_SortObject(to); - from=from->child;to=to->child; - patch=cJSON_CreateObject(); - while (from || to) - { - int compare=from?(to?strcmp(from->string,to->string):-1):1; - if (compare<0) - { - cJSON_AddItemToObject(patch,from->string,cJSON_CreateNull()); - from=from->next; - } - else if (compare>0) - { - cJSON_AddItemToObject(patch,to->string,cJSON_Duplicate(to,1)); - to=to->next; - } - else - { - if (cJSONUtils_Compare(from,to)) cJSON_AddItemToObject(patch,to->string,cJSONUtils_GenerateMergePatch(from,to)); - from=from->next;to=to->next; - } - } - if (!patch->child) {cJSON_Delete(patch);return 0;} - return patch; -} \ No newline at end of file + cJSON *patch = 0; + if (!to) + { + /* patch to delete everything */ + return cJSON_CreateNull(); + } + if ((to->type != cJSON_Object) || !from || (from->type != cJSON_Object)) + { + return cJSON_Duplicate(to, 1); + } + + cJSONUtils_SortObject(from); + cJSONUtils_SortObject(to); + + from = from->child; + to = to->child; + patch = cJSON_CreateObject(); + while (from || to) + { + int compare = from ? (to ? strcmp(from->string, to->string) : -1) : 1; + if (compare < 0) + { + /* from has a value that to doesn't have -> remove */ + cJSON_AddItemToObject(patch, from->string, cJSON_CreateNull()); + from = from->next; + } + else if (compare > 0) + { + /* to has a value that from doesn't have -> add to patch */ + cJSON_AddItemToObject(patch, to->string, cJSON_Duplicate(to, 1)); + to = to->next; + } + else + { + /* object key exists in both objects */ + if (cJSONUtils_Compare(from, to)) + { + /* not identical --> generate a patch */ + cJSON_AddItemToObject(patch, to->string, cJSONUtils_GenerateMergePatch(from, to)); + } + /* next key in the object */ + from = from->next; + to = to->next; + } + } + if (!patch->child) + { + cJSON_Delete(patch); + return 0; + } + + return patch; +} diff --git a/cJSON_Utils.h b/cJSON_Utils.h index b129b0625249d691a43347da9df3724df730d398..cefda08bcd4a79e8dabf194336cea2e6e35c65c9 100644 --- a/cJSON_Utils.h +++ b/cJSON_Utils.h @@ -1,30 +1,44 @@ #include "cJSON.h" -/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ -cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer); +/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ +cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer); -/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ -cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to); -void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val); /* Utility for generating patch array entries. */ -int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches); /* Returns 0 for success. */ +/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ +cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to); +/* Utility for generating patch array entries. */ +void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val); +/* Returns 0 for success. */ +int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches); /* // Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use: //int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) //{ -// cJSON *modme=cJSON_Duplicate(*object,1); -// int error=cJSONUtils_ApplyPatches(modme,patches); -// if (!error) {cJSON_Delete(*object);*object=modme;} -// else cJSON_Delete(modme); -// return error; +// cJSON *modme = cJSON_Duplicate(*object, 1); +// int error = cJSONUtils_ApplyPatches(modme, patches); +// if (!error) +// { +// cJSON_Delete(*object); +// *object = modme; +// } +// else +// { +// cJSON_Delete(modme); +// } +// +// return error; //} // Code not added to library since this strategy is a LOT slower. */ /* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ -cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); /* target will be modified by patch. return value is new ptr for target. */ -cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to); /* generates a patch to move from -> to */ +/* target will be modified by patch. return value is new ptr for target. */ +cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); +/* generates a patch to move from -> to */ +cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to); -char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target); /* Given a root object and a target object, construct a pointer from one to the other. */ +/* Given a root object and a target object, construct a pointer from one to the other. */ +char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target); -void cJSONUtils_SortObject(cJSON *object); /* Sorts the members of the object into alphabetical order. */ +/* Sorts the members of the object into alphabetical order. */ +void cJSONUtils_SortObject(cJSON *object); diff --git a/test.c b/test.c index a9124a6cdb24ac0cf54fab136fee7e537ab4b409..97fa01fae8b93830118dc7df2e35ec485693f618 100644 --- a/test.c +++ b/test.c @@ -27,125 +27,269 @@ /* Parse text to JSON, then render back to text, and print! */ void doit(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); - } + 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); + } } /* Read a file, parse, render back, etc. */ void dofile(char *filename) { - FILE *f;long len;char *data; + FILE *f; + long len; + char *data; - f=fopen(filename,"rb");fseek(f,0,SEEK_END);len=ftell(f);fseek(f,0,SEEK_SET); - data=(char*)malloc(len+1);fread(data,1,len,f);data[len]='\0';fclose(f); - doit(data); - free(data); + /* open in read binary mode */ + f = fopen(filename,"rb"); + /* get the length */ + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + + data = (char*)malloc(len + 1); + + fread(data, 1, len, f); + data[len] = '\0'; + fclose(f); + + doit(data); + free(data); } /* Used by some code below as an example datatype. */ -struct record {const char *precision;double lat,lon;const char *address,*city,*state,*zip,*country; }; +struct record +{ + const char *precision; + double lat; + double lon; + const char *address; + const char *city; + const char *state; + const char *zip; + const char *country; +}; /* Create a bunch of objects as demonstration. */ void create_objects() { - cJSON *root,*fmt,*img,*thm,*fld;char *out;int i; /* declare a few. */ - /* Our "days of the week" array: */ - const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}; - /* Our matrix: */ - int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}}; - /* Our "gallery" item: */ - int ids[4]={116,943,234,38793}; - /* Our array of "records": */ - 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"}}; - volatile double zero = 0.0; - - /* 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. */ - - /* Our "days of the week" array: */ - root=cJSON_CreateStringArray(strings,7); - - out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out); - - /* Our matrix: */ - 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); - - - /* Our "gallery" item: */ - 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); - - /* Our array of "records": */ - - 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); - - root=cJSON_CreateObject(); - cJSON_AddNumberToObject(root,"number", 1.0/zero); - out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out); + /* declare a few. */ + cJSON *root; + cJSON *fmt; + cJSON *img; + cJSON *thm; + cJSON *fld; + char *out; + int i; + + /* Our "days of the week" array: */ + const char *strings[7] = + { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + }; + /* Our matrix: */ + int numbers[3][3] = + { + {0, -1, 0}, + {1, 0, 0}, + {0 ,0, 1} + }; + /* Our "gallery" item: */ + int ids[4] = { 116, 943, 234, 38793 }; + /* Our array of "records": */ + 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" + } + }; + volatile double zero = 0.0; + + /* 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); + + /* Print to text */ + out = cJSON_Print(root); + /* Delete the cJSON */ + cJSON_Delete(root); + /* print it */ + printf("%s\n",out); + /* release the string */ + free(out); + + /* Our "days of the week" array: */ + root = cJSON_CreateStringArray(strings, 7); + + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + /* Our matrix: */ + 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); + + + /* Our "gallery" item: */ + 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); + + /* Our array of "records": */ + + 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); + + root = cJSON_CreateObject(); + cJSON_AddNumberToObject(root, "number", 1.0 / zero); + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); } -int main (int argc, const char * argv[]) { - /* a bunch of json: */ - char text1[]="{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\": \"rect\", \n\"width\": 1920, \n\"height\": 1080, \n\"interlace\": false,\"frame rate\": 24\n}\n}"; - char text2[]="[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]"; - char text3[]="[\n [0, -1, 0],\n [1, 0, 0],\n [0, 0, 1]\n ]\n"; - 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 }"; - 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 ]"; - - char text6[] = "" +int main (int argc, const char *argv[]) +{ + /* a bunch of json: */ + char text1[] = + "{\n" + "\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n" + "\"format\": {\"type\": \"rect\", \n" + "\"width\": 1920, \n" + "\"height\": 1080, \n" + "\"interlace\": false,\"frame rate\": 24\n" + "}\n" + "}"; + char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]"; + char text3[] = + "[\n" + " [0, -1, 0],\n" + " [1, 0, 0],\n" + " [0, 0, 1]\n" + "\t]\n"; + char text4[] = + "{\n" + "\t\t\"Image\": {\n" + "\t\t\t\"Width\": 800,\n" + "\t\t\t\"Height\": 600,\n" + "\t\t\t\"Title\": \"View from 15th Floor\",\n" + "\t\t\t\"Thumbnail\": {\n" + "\t\t\t\t\"Url\": \"http:/*www.example.com/image/481989943\",\n" + "\t\t\t\t\"Height\": 125,\n" + "\t\t\t\t\"Width\": \"100\"\n" + "\t\t\t},\n" + "\t\t\t\"IDs\": [116, 943, 234, 38793]\n" + "\t\t}\n" + "\t}"; + char text5[] = + "[\n" + "\t {\n" + "\t \"precision\": \"zip\",\n" + "\t \"Latitude\": 37.7668,\n" + "\t \"Longitude\": -122.3959,\n" + "\t \"Address\": \"\",\n" + "\t \"City\": \"SAN FRANCISCO\",\n" + "\t \"State\": \"CA\",\n" + "\t \"Zip\": \"94107\",\n" + "\t \"Country\": \"US\"\n" + "\t },\n" + "\t {\n" + "\t \"precision\": \"zip\",\n" + "\t \"Latitude\": 37.371991,\n" + "\t \"Longitude\": -122.026020,\n" + "\t \"Address\": \"\",\n" + "\t \"City\": \"SUNNYVALE\",\n" + "\t \"State\": \"CA\",\n" + "\t \"Zip\": \"94085\",\n" + "\t \"Country\": \"US\"\n" + "\t }\n" + "\t ]"; + + char text6[] = + "" "\n" "\n" " \n" @@ -162,24 +306,24 @@ int main (int argc, const char * argv[]) { "\n" "\n"; - /* Process each json textblock by parsing, then rebuilding: */ - doit(text1); - doit(text2); - doit(text3); - doit(text4); - doit(text5); + /* Process each json textblock by parsing, then rebuilding: */ + doit(text1); + doit(text2); + doit(text3); + doit(text4); + doit(text5); doit(text6); - /* Parse standard testfiles: */ -/* dofile("../../tests/test1"); */ -/* dofile("../../tests/test2"); */ -/* dofile("../../tests/test3"); */ -/* dofile("../../tests/test4"); */ -/* dofile("../../tests/test5"); */ -/* dofile("../../tests/test6"); */ + /* Parse standard testfiles: */ + /* dofile("../../tests/test1"); */ + /* dofile("../../tests/test2"); */ + /* dofile("../../tests/test3"); */ + /* dofile("../../tests/test4"); */ + /* dofile("../../tests/test5"); */ + /* dofile("../../tests/test6"); */ - /* Now some samplecode for building objects concisely: */ - create_objects(); + /* Now some samplecode for building objects concisely: */ + create_objects(); - return 0; + return 0; }