2 Copyright (c) 2009 Dave Gamble
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 /* JSON parser in C. */
35 static const char *ep;
37 const char *cJSON_GetErrorPtr() {return ep;}
39 static int cJSON_strcasecmp(const char *s1,const char *s2)
41 if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
42 for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
43 return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
46 static void *(*cJSON_malloc)(size_t sz) = malloc;
47 static void (*cJSON_free)(void *ptr) = free;
49 static char* cJSON_strdup(const char* str)
54 len = strlen(str) + 1;
55 if (!(copy = (char*)cJSON_malloc(len))) return 0;
60 void cJSON_InitHooks(cJSON_Hooks* hooks)
62 if (!hooks) { /* Reset hooks */
63 cJSON_malloc = malloc;
68 cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
69 cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
72 /* Internal constructor. */
73 static cJSON *cJSON_New_Item()
75 cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
76 if (node) memset(node,0,sizeof(cJSON));
80 /* Delete a cJSON structure. */
81 void cJSON_Delete(cJSON *c)
87 if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
88 if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
89 if (c->string) cJSON_free(c->string);
95 /* Parse the input text to generate a number, and populate the result into item. */
96 static const char *parse_number(cJSON *item,const char *num)
98 double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
100 /* Could use sscanf for this? */
101 if (*num=='-') sign=-1,num++; /* Has sign? */
102 if (*num=='0') num++; /* is zero */
103 if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
104 if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
105 if (*num=='e' || *num=='E') /* Exponent? */
106 { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
107 while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
110 n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
113 item->valueint=(int)n;
114 item->type=cJSON_Number;
118 /* Render the number nicely from the given item into a string. */
119 static char *print_number(cJSON *item)
122 double d=item->valuedouble;
123 if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
125 str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
126 if (str) sprintf(str,"%d",item->valueint);
130 str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
133 if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d);
134 else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
135 else sprintf(str,"%f",d);
141 /* Parse the input text into an unescaped cstring, and populate item. */
142 static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
143 static const char *parse_string(cJSON *item,const char *str)
145 const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
146 if (*str!='\"') {ep=str;return 0;} /* not a string! */
148 while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
150 out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
154 while (*ptr!='\"' && *ptr)
156 if (*ptr!='\\') *ptr2++=*ptr++;
162 case 'b': *ptr2++='\b'; break;
163 case 'f': *ptr2++='\f'; break;
164 case 'n': *ptr2++='\n'; break;
165 case 'r': *ptr2++='\r'; break;
166 case 't': *ptr2++='\t'; break;
167 case 'u': /* transcode utf16 to utf8. */
168 sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
170 if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid.
172 if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs.
174 if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate.
175 sscanf(ptr+3,"%4x",&uc2);ptr+=6;
176 if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate.
177 uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);
180 len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
183 case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
184 case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
185 case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
186 case 1: *--ptr2 =(uc | firstByteMark[len]);
190 default: *ptr2++=*ptr; break;
196 if (*ptr=='\"') ptr++;
197 item->valuestring=out;
198 item->type=cJSON_String;
202 /* Render the cstring provided to an escaped version that can be printed. */
203 static char *print_string_ptr(const char *str)
205 const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
207 if (!str) return cJSON_strdup("");
208 ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
210 out=(char*)cJSON_malloc(len+3);
217 if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
221 switch (token=*ptr++)
223 case '\\': *ptr2++='\\'; break;
224 case '\"': *ptr2++='\"'; break;
225 case '\b': *ptr2++='b'; break;
226 case '\f': *ptr2++='f'; break;
227 case '\n': *ptr2++='n'; break;
228 case '\r': *ptr2++='r'; break;
229 case '\t': *ptr2++='t'; break;
230 default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
234 *ptr2++='\"';*ptr2++=0;
237 /* Invote print_string_ptr (which is useful) on an item. */
238 static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
240 /* Predeclare these prototypes. */
241 static const char *parse_value(cJSON *item,const char *value);
242 static char *print_value(cJSON *item,int depth,int fmt);
243 static const char *parse_array(cJSON *item,const char *value);
244 static char *print_array(cJSON *item,int depth,int fmt);
245 static const char *parse_object(cJSON *item,const char *value);
246 static char *print_object(cJSON *item,int depth,int fmt);
248 /* Utility to jump whitespace and cr/lf */
249 static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
251 /* Parse an object - create a new root, and populate. */
252 cJSON *cJSON_Parse(const char *value)
254 cJSON *c=cJSON_New_Item();
256 if (!c) return 0; /* memory fail */
258 if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
262 /* Render a cJSON item/entity/structure to text. */
263 char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
264 char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
266 /* Parser core - when encountering text, process appropriately. */
267 static const char *parse_value(cJSON *item,const char *value)
269 if (!value) return 0; /* Fail on null. */
270 if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
271 if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
272 if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
273 if (*value=='\"') { return parse_string(item,value); }
274 if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
275 if (*value=='[') { return parse_array(item,value); }
276 if (*value=='{') { return parse_object(item,value); }
278 ep=value;return 0; /* failure. */
281 /* Render a value to text. */
282 static char *print_value(cJSON *item,int depth,int fmt)
286 switch ((item->type)&255)
288 case cJSON_NULL: out=cJSON_strdup("null"); break;
289 case cJSON_False: out=cJSON_strdup("false");break;
290 case cJSON_True: out=cJSON_strdup("true"); break;
291 case cJSON_Number: out=print_number(item);break;
292 case cJSON_String: out=print_string(item);break;
293 case cJSON_Array: out=print_array(item,depth,fmt);break;
294 case cJSON_Object: out=print_object(item,depth,fmt);break;
299 /* Build an array from input text. */
300 static const char *parse_array(cJSON *item,const char *value)
303 if (*value!='[') {ep=value;return 0;} /* not an array! */
305 item->type=cJSON_Array;
307 if (*value==']') return value+1; /* empty array. */
309 item->child=child=cJSON_New_Item();
310 if (!item->child) return 0; /* memory fail */
311 value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
312 if (!value) return 0;
317 if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
318 child->next=new_item;new_item->prev=child;child=new_item;
319 value=skip(parse_value(child,skip(value+1)));
320 if (!value) return 0; /* memory fail */
323 if (*value==']') return value+1; /* end of array */
324 ep=value;return 0; /* malformed. */
327 /* Render an array to text */
328 static char *print_array(cJSON *item,int depth,int fmt)
331 char *out=0,*ptr,*ret;int len=5;
332 cJSON *child=item->child;
333 int numentries=0,i=0,fail=0;
335 /* How many entries in the array? */
336 while (child) numentries++,child=child->next;
337 /* Allocate an array to hold the values for each */
338 entries=(char**)cJSON_malloc(numentries*sizeof(char*));
339 if (!entries) return 0;
340 memset(entries,0,numentries*sizeof(char*));
341 /* Retrieve all the results: */
343 while (child && !fail)
345 ret=print_value(child,depth+1,fmt);
347 if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
351 /* If we didn't fail, try to malloc the output string */
352 if (!fail) out=(char*)cJSON_malloc(len);
353 /* If that fails, we fail. */
356 /* Handle failure. */
359 for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
364 /* Compose the output array. */
367 for (i=0;i<numentries;i++)
369 strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
370 if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
371 cJSON_free(entries[i]);
378 /* Build an object from the text. */
379 static const char *parse_object(cJSON *item,const char *value)
382 if (*value!='{') {ep=value;return 0;} /* not an object! */
384 item->type=cJSON_Object;
386 if (*value=='}') return value+1; /* empty array. */
388 item->child=child=cJSON_New_Item();
389 if (!item->child) return 0;
390 value=skip(parse_string(child,skip(value)));
391 if (!value) return 0;
392 child->string=child->valuestring;child->valuestring=0;
393 if (*value!=':') {ep=value;return 0;} /* fail! */
394 value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
395 if (!value) return 0;
400 if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
401 child->next=new_item;new_item->prev=child;child=new_item;
402 value=skip(parse_string(child,skip(value+1)));
403 if (!value) return 0;
404 child->string=child->valuestring;child->valuestring=0;
405 if (*value!=':') {ep=value;return 0;} /* fail! */
406 value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
407 if (!value) return 0;
410 if (*value=='}') return value+1; /* end of array */
411 ep=value;return 0; /* malformed. */
414 /* Render an object to text. */
415 static char *print_object(cJSON *item,int depth,int fmt)
417 char **entries=0,**names=0;
418 char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
419 cJSON *child=item->child;
420 int numentries=0,fail=0;
421 /* Count the number of entries. */
422 while (child) numentries++,child=child->next;
423 /* Allocate space for the names and the objects */
424 entries=(char**)cJSON_malloc(numentries*sizeof(char*));
425 if (!entries) return 0;
426 names=(char**)cJSON_malloc(numentries*sizeof(char*));
427 if (!names) {cJSON_free(entries);return 0;}
428 memset(entries,0,sizeof(char*)*numentries);
429 memset(names,0,sizeof(char*)*numentries);
431 /* Collect all the results into our arrays: */
432 child=item->child;depth++;if (fmt) len+=depth;
435 names[i]=str=print_string_ptr(child->string);
436 entries[i++]=ret=print_value(child,depth,fmt);
437 if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
441 /* Try to allocate the output string */
442 if (!fail) out=(char*)cJSON_malloc(len);
448 for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
449 cJSON_free(names);cJSON_free(entries);
453 /* Compose the output: */
454 *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
455 for (i=0;i<numentries;i++)
457 if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
458 strcpy(ptr,names[i]);ptr+=strlen(names[i]);
459 *ptr++=':';if (fmt) *ptr++='\t';
460 strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
461 if (i!=numentries-1) *ptr++=',';
462 if (fmt) *ptr++='\n';*ptr=0;
463 cJSON_free(names[i]);cJSON_free(entries[i]);
466 cJSON_free(names);cJSON_free(entries);
467 if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
472 /* Get Array size/item / object item. */
473 int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
474 cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
475 cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
477 /* Utility for array list handling. */
478 static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
479 /* Utility for handling references. */
480 static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
482 /* Add item to array/object. */
483 void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
484 void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
485 void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
486 void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
488 cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
489 if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
490 void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
491 cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
492 void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
494 /* Replace array/object items with new ones. */
495 void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
496 newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
497 if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
498 void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
500 /* Create basic types: */
501 cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
502 cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
503 cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
504 cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
505 cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
506 cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
507 cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
508 cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
511 cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
512 cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
513 cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
514 cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}