00001 /********************************************************************************* 00002 00003 Copyright 2006-2008 MakingThings 00004 00005 Licensed under the Apache License, 00006 Version 2.0 (the "License"); you may not use this file except in compliance 00007 with the License. You may obtain a copy of the License at 00008 00009 http://www.apache.org/licenses/LICENSE-2.0 00010 00011 Unless required by applicable law or agreed to in writing, software distributed 00012 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 00013 CONDITIONS OF ANY KIND, either express or implied. See the License for 00014 the specific language governing permissions and limitations under the License. 00015 00016 *********************************************************************************/ 00017 00018 #include "json.h" 00019 #include "string.h" 00020 #include "stdio.h" 00021 #include <stdlib.h> 00022 #include <ctype.h> 00023 00024 /** \defgroup json JSON 00025 The Make Controller JSON library provides a very small and very fast library for parsing and 00026 generating json. 00027 00028 From http://www.json.org: "JSON (JavaScript Object Notation) 00029 is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for 00030 machines to parse and generate." 00031 00032 JSON is quite widely used when communicating with web servers, or other network enabled devices. 00033 It's nice and small, and easy to work with. It's quite well supported in many programming 00034 environments, so it's not a bad option for a communication format when you need to talk to other 00035 devices from the Make Controller. 00036 00037 \b Disclaimer - in an attempt to keep it as small and as simple as possible, this library is not 00038 completely full featured at the moment. It doesn't process escaped strings for you, and doesn't deal 00039 with some of the more exotic numeric representations outlined in the JSON specification. 00040 It does, however, work quite well for most other JSON tasks. 00041 00042 \section Generating 00043 Generating JSON is pretty simple - just make successive calls to the API to add the desired 00044 elements to your string. 00045 00046 You need to provide a few things: 00047 - a JsonEncode_State variable 00048 - the buffer in which to store the JSON string being built 00049 - a count of how many bytes are left in that buffer 00050 The API will update that count, so it's not too much trouble. 00051 00052 \code 00053 #define MAX_JSON_LEN 256 00054 char jsonbuf[MAX_JSON_LEN]; 00055 int remaining = MAX_JSON_LEN; 00056 JsonEncode_State s; 00057 00058 char *p = jsonbuf; // keep a pointer to the current location 00059 JsonEncode_Init(&s); // initialize our state variable 00060 p = JsonEncode_ObjectOpen(&s, p, &remaining); 00061 p = JsonEncode_String(&s, p, "hello", &remaining); 00062 p = JsonEncode_Int(&s, p, 234, &remaining); 00063 p = JsonEncode_ObjectClose(&s, p, &remaining); 00064 // now the string in jsonbuf looks like {"hello":234} - beautifully formatted JSON 00065 int json_len = MAX_JSON_LEN - remaining; // this is the total length of the string in jsonbuf 00066 \endcode 00067 00068 \b Note - the library will add the appropriate separators (: or , usually) to the string, 00069 depending on the context of the objects and arrays you've opened, or other data you've inserted. 00070 00071 \section Parsing 00072 Parsing is done using an event-based mechanism. This means you can register for any parse events you care 00073 to hear about, and then be called back with their value as they're encountered in the JSON string. 00074 Each parse process needs its own JsonDecode_State variable to keep track of where it is. 00075 00076 In each callback, return true to continue parsing, or return false and parsing will stop. 00077 00078 If you need to pass around some context that you would like available in each of the callbacks, 00079 you can pass it to JsonDecode_Init() and it will be passed to each of the callbacks you've registered. 00080 Otherwise, just pass 0 if you don't need it. 00081 00082 \code 00083 // first, define the functions that we want to be called back on 00084 bool on_obj_opened(void* ctx) 00085 { 00086 // will be called when an object has been opened... 00087 return true; // keep parsing 00088 } 00089 00090 bool on_int(void *ctx, int val) 00091 { 00092 iny my_json_int = val; 00093 // called when an int is encountered... 00094 return true; // keep parsing 00095 } 00096 00097 bool on_string(void *ctx, char *string, int len) 00098 { 00099 // called when a string is encountered... 00100 return true; // keep parsing 00101 } 00102 00103 // Now, register these callbacks with the JSON parser. 00104 JsonDecode_SetStartObjCallback(on_obj_opened); 00105 JsonDecode_SetIntCallback(on_int); 00106 JsonDecode_SetStringCallback(on_string); 00107 00108 // Finally, run the parser. 00109 JsonDecode_State s; 00110 JsonDecode_Init(&s, 0); // pass 0 if you don't need to use any special context 00111 char jsonstr[] = "[{\"label\":\"value\",\"label2\":{\"nested\":234}}]"; 00112 JsonDecode(&s, jsonstr, strlen(jsonstr)); 00113 // now each of our callbacks will be triggered at the appropriate time 00114 \endcode 00115 00116 Thanks to YAJL (http://code.google.com/p/yajl-c) for some design inspiration. 00117 \par 00118 00119 \ingroup Libraries 00120 @{ 00121 */ 00122 00123 static void JsonEncode_AppendedAtom(JsonEncode_State* state); 00124 00125 /** 00126 Initialize or reset the state of a JsonEncode_State variable. 00127 Be sure to do this each time before you start parsing. 00128 */ 00129 void JsonEncode_Init(JsonEncode_State* state) 00130 { 00131 state->depth = 0; 00132 state->steps[0] = JSON_START; 00133 } 00134 00135 /** 00136 Open up a new JSON object. 00137 This adds an opening '{' to the json string. 00138 @param state A pointer to the JsonEncode_State variable being used for this encode process. 00139 @param buf A pointer to the buffer holding the JSON string. 00140 @param remaining A pointer to the count of how many bytes are left in your JSON buffer. 00141 @return A pointer to the location in the JSON buffer after this element has been added, or NULL if there was no room. 00142 */ 00143 char* JsonEncode_ObjectOpen(JsonEncode_State* state, char *buf, int *remaining) 00144 { 00145 if( !buf ) 00146 return 0; 00147 int len = 1; 00148 switch(state->steps[state->depth]) 00149 { 00150 case JSON_ARRAY_START: 00151 case JSON_OBJ_START: 00152 case JSON_START: 00153 { 00154 if(*remaining < len) 00155 return NULL; 00156 memcpy(buf, "{", len); 00157 break; 00158 } 00159 case JSON_OBJ_KEY: 00160 case JSON_IN_ARRAY: 00161 { 00162 len += 1; // for , 00163 if(*remaining < len) 00164 return NULL; 00165 memcpy(buf, ",{", len); 00166 break; 00167 } 00168 case JSON_OBJ_VALUE: 00169 { 00170 len += 1; // for : 00171 if(*remaining < len) 00172 return NULL; 00173 memcpy(buf, ":{", len); 00174 break; 00175 } 00176 } 00177 00178 if(++state->depth > JSON_MAX_DEPTH) 00179 return NULL; 00180 state->steps[state->depth] = JSON_OBJ_START; 00181 (*remaining) -= len; 00182 return buf + len; 00183 } 00184 00185 /** 00186 Set the key for a JSON object. 00187 This is a convenience function that simply calls JsonEncode_String(). 00188 It is provided to help enforce the idea that the first member of a JSON 00189 object pair must be a string. 00190 @see JsonEncode_String() 00191 */ 00192 char* JsonEncode_ObjectKey(JsonEncode_State* state, char *buf, const char *key, int *remaining) 00193 { 00194 return JsonEncode_String(state, buf, key, remaining); 00195 } 00196 00197 /** 00198 Close a JSON object. 00199 Adds a closing '}' to the string. 00200 @param state A pointer to the JsonEncode_State variable being used for this encode process. 00201 @param buf A pointer to the buffer which contains your JSON string. 00202 @param remaining A pointer to an integer keeping track of how many bytes are left in your JSON buffer. 00203 @return A pointer to the location in the JSON buffer after this element has been added, or NULL if there was no room. 00204 */ 00205 char* JsonEncode_ObjectClose(JsonEncode_State* state, char *buf, int *remaining) 00206 { 00207 if(*remaining < 1 || !buf ) 00208 return NULL; 00209 memcpy(buf, "}", 1); 00210 state->depth--; 00211 JsonEncode_AppendedAtom(state); 00212 (*remaining)--; 00213 return buf + 1; 00214 } 00215 00216 /** 00217 Open up a new JSON array. 00218 This adds an opening '[' to the json string. 00219 @param state A pointer to the JsonEncode_State variable being used for this encode process. 00220 @param buf A pointer to the buffer holding the JSON string. 00221 @param remaining A pointer to the count of how many bytes are left in your JSON buffer. 00222 @return A pointer to the location in the JSON buffer after this element has been added, or NULL if there was no room. 00223 */ 00224 char* JsonEncode_ArrayOpen(JsonEncode_State* state, char *buf, int *remaining) 00225 { 00226 if( !buf ) 00227 return 0; 00228 int len = 1; 00229 switch(state->steps[state->depth]) 00230 { 00231 case JSON_ARRAY_START: 00232 case JSON_OBJ_START: 00233 case JSON_START: 00234 { 00235 if(*remaining < len) 00236 return NULL; 00237 memcpy(buf, "[", len); 00238 break; 00239 } 00240 case JSON_OBJ_KEY: 00241 case JSON_IN_ARRAY: 00242 { 00243 len += 1; // for , 00244 if(*remaining < len) 00245 return NULL; 00246 memcpy(buf, ",[", len); 00247 break; 00248 } 00249 case JSON_OBJ_VALUE: 00250 { 00251 len += 1; // for : 00252 if(*remaining < len) 00253 return NULL; 00254 memcpy(buf, ":[", len); 00255 break; 00256 } 00257 } 00258 if(++state->depth > JSON_MAX_DEPTH) 00259 return NULL; 00260 state->steps[state->depth] = JSON_ARRAY_START; 00261 (*remaining) -= len; 00262 return buf + len; 00263 } 00264 00265 /** 00266 Close an array. 00267 Adds a closing ']' to the string. 00268 @param state A pointer to the JsonEncode_State variable being used for this encode process. 00269 @param buf A pointer to the buffer which contains your JSON string. 00270 @param remaining A pointer to the count of how many bytes are left in your JSON buffer. 00271 @return A pointer to the JSON buffer after this element has been added, or NULL if there was no room. 00272 */ 00273 char* JsonEncode_ArrayClose(JsonEncode_State* state, char *buf, int *remaining) 00274 { 00275 if(*remaining < 1 || !buf ) 00276 return NULL; 00277 memcpy(buf, "]", 1); 00278 state->depth--; 00279 JsonEncode_AppendedAtom(state); 00280 (*remaining)--; 00281 return buf + 1; 00282 } 00283 00284 /** 00285 Add a string to the current JSON string. 00286 Depending on whether you've opened objects, arrays, or other inserted 00287 other data, the approprate separating symbols will be added to the string. 00288 00289 @param state A pointer to the JsonEncode_State variable being used for this encode process. 00290 @param buf A pointer to the buffer containing the JSON string. 00291 @param string The string to be added. 00292 @param remaining A pointer to the count of bytes remaining in the JSON buffer. 00293 @return A pointer to the JSON buffer after this element has been added, or NULL if there was no room. 00294 */ 00295 char* JsonEncode_String(JsonEncode_State* state, char *buf, const char *string, int *remaining) 00296 { 00297 if( !buf ) 00298 return 0; 00299 int string_len = strlen(string) + 2 /* quotes */; 00300 00301 switch(state->steps[state->depth]) 00302 { 00303 case JSON_ARRAY_START: 00304 case JSON_OBJ_START: 00305 { 00306 if(*remaining < string_len) 00307 return NULL; 00308 char temp[string_len+1]; 00309 snprintf(temp, string_len+1, "\"%s\"", string); 00310 memcpy(buf, temp, string_len); 00311 break; 00312 } 00313 case JSON_OBJ_KEY: 00314 case JSON_IN_ARRAY: 00315 { 00316 string_len += 1; // for , 00317 if(*remaining < string_len) 00318 return NULL; 00319 char temp[string_len+1]; 00320 snprintf(temp, string_len+1, ",\"%s\"", string); 00321 memcpy(buf, temp, string_len); 00322 break; 00323 } 00324 case JSON_OBJ_VALUE: 00325 { 00326 string_len += 1; // for : 00327 if(*remaining < string_len) 00328 return NULL; 00329 char temp[string_len+1]; 00330 snprintf(temp, string_len+1, ":\"%s\"", string); 00331 memcpy(buf, temp, string_len); 00332 break; 00333 } 00334 default: 00335 return NULL; 00336 } 00337 JsonEncode_AppendedAtom(state); 00338 (*remaining) -= string_len; 00339 return buf + string_len; 00340 } 00341 00342 /** 00343 Add an int to a JSON string. 00344 00345 @param state A pointer to the JsonEncode_State variable being used for this encode process. 00346 @param buf A pointer to the buffer containing the JSON string. 00347 @param value The integer to be added. 00348 @param remaining A pointer to the count of bytes remaining in the JSON buffer. 00349 @return A pointer to the JSON buffer after this element has been added, or NULL if there was no room. 00350 */ 00351 char* JsonEncode_Int(JsonEncode_State* state, char *buf, int value, int *remaining) 00352 { 00353 if( !buf ) 00354 return 0; 00355 int int_as_str_len = 11; // largest 32-bit int is 10 digits long, and also leave room for a +/- 00356 int int_len = 0; 00357 switch(state->steps[state->depth]) 00358 { 00359 case JSON_ARRAY_START: 00360 { 00361 char temp[int_as_str_len+1]; 00362 snprintf(temp, int_as_str_len+1, "%d", value); 00363 int_len = strlen(temp); 00364 if(*remaining < int_len) 00365 return NULL; 00366 memcpy(buf, temp, int_len); 00367 break; 00368 } 00369 case JSON_IN_ARRAY: 00370 { 00371 int_as_str_len += 1; // for , 00372 char temp[int_as_str_len+1]; 00373 snprintf(temp, int_as_str_len+1, ",%d", value); 00374 int_len = strlen(temp); 00375 if(*remaining < int_len) 00376 return NULL; 00377 memcpy(buf, temp, int_len); 00378 break; 00379 } 00380 case JSON_OBJ_VALUE: 00381 { 00382 int_as_str_len += 1; // for : 00383 char temp[int_as_str_len+1]; 00384 snprintf(temp, int_as_str_len+1, ":%d", value); 00385 int_len = strlen(temp); 00386 if(*remaining < int_len) 00387 return NULL; 00388 memcpy(buf, temp, int_len); 00389 break; 00390 } 00391 default: 00392 return NULL; // bogus state 00393 } 00394 JsonEncode_AppendedAtom(state); 00395 (*remaining) -= int_len; 00396 return buf + int_len; 00397 } 00398 00399 /** 00400 Add a boolean value to a JSON string. 00401 00402 @param state A pointer to the JsonEncode_State variable being used for this encode process. 00403 @param buf A pointer to the buffer containing the JSON string. 00404 @param value The boolean value to be added. 00405 @param remaining A pointer to the count of bytes remaining in the JSON buffer. 00406 @return A pointer to the JSON buffer after this element has been added, or NULL if there was no room. 00407 */ 00408 char* JsonEncode_Bool(JsonEncode_State* state, char *buf, bool value, int *remaining) 00409 { 00410 if( !buf ) 00411 return 0; 00412 const char* boolval = (value) ? "true" : "false"; 00413 int bool_len = strlen(boolval); 00414 switch(state->steps[state->depth]) 00415 { 00416 case JSON_ARRAY_START: 00417 { 00418 if(*remaining < bool_len) 00419 return NULL; 00420 char temp[bool_len+1]; 00421 snprintf(temp, bool_len+1, "%s", boolval); 00422 memcpy(buf, temp, bool_len); 00423 break; 00424 } 00425 case JSON_IN_ARRAY: 00426 { 00427 bool_len += 1; // for , 00428 if(*remaining < bool_len) 00429 return NULL; 00430 char temp[bool_len+1]; 00431 snprintf(temp, bool_len+1, ",%s", boolval); 00432 memcpy(buf, temp, bool_len); 00433 break; 00434 } 00435 case JSON_OBJ_VALUE: 00436 { 00437 bool_len += 1; // for : 00438 if(*remaining < bool_len) 00439 return NULL; 00440 char temp[bool_len+1]; 00441 snprintf(temp, bool_len+1, ":%s", boolval); 00442 memcpy(buf, temp, bool_len); 00443 break; 00444 } 00445 default: 00446 return NULL; // bogus state 00447 } 00448 JsonEncode_AppendedAtom(state); 00449 (*remaining) -= bool_len; 00450 return buf + bool_len; 00451 } 00452 00453 /* 00454 Called after adding a new value (atom) to a string in order 00455 to update the state appropriately. 00456 */ 00457 // static 00458 void JsonEncode_AppendedAtom(JsonEncode_State* state) 00459 { 00460 switch(state->steps[state->depth]) 00461 { 00462 case JSON_OBJ_START: 00463 case JSON_OBJ_KEY: 00464 state->steps[state->depth] = JSON_OBJ_VALUE; 00465 break; 00466 case JSON_ARRAY_START: 00467 state->steps[state->depth] = JSON_IN_ARRAY; 00468 break; 00469 case JSON_OBJ_VALUE: 00470 state->steps[state->depth] = JSON_OBJ_KEY; 00471 break; 00472 default: 00473 break; 00474 } 00475 } 00476 00477 /**************************************************************************** 00478 JsonDecode 00479 ****************************************************************************/ 00480 00481 typedef enum { 00482 token_true, 00483 token_false, 00484 token_colon, 00485 token_comma, 00486 token_eof, 00487 token_error, 00488 token_left_brace, 00489 token_left_bracket, 00490 token_null, 00491 token_right_brace, 00492 token_right_bracket, 00493 token_number, 00494 token_maybe_negative, 00495 token_string, 00496 token_unknown 00497 } JsonDecode_Token; 00498 00499 struct 00500 { 00501 bool(*null_callback)(void*); 00502 bool(*bool_callback)(void*, bool); 00503 bool(*int_callback)(void*, int); 00504 bool(*float_callback)(void*, float); 00505 bool(*string_callback)(void*, char*, int); 00506 bool(*start_obj_callback)(void*); 00507 bool(*obj_key_callback)(void*, char*, int); 00508 bool(*end_obj_callback)(void*); 00509 bool(*start_array_callback)(void*); 00510 bool(*end_array_callback)(void*); 00511 } JsonDecode_Callbacks; 00512 00513 static JsonDecode_Token JsonDecode_GetToken(char* text, int len); 00514 00515 /** 00516 Set the function to be called back when an integer is parsed. 00517 The function must have the format: 00518 \code 00519 bool on_int(void* context, int value); 00520 \endcode 00521 which would then be registered like so: 00522 \code 00523 JsonDecode_SetIntCallback(on_int); 00524 \endcode 00525 The \b context parameter will be set to whatever you originally passed to 00526 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00527 this makes it convenient to know in your callback which one you're currently parsing. 00528 00529 @param int_callback The function to be called back. 00530 */ 00531 void JsonDecode_SetIntCallback(bool(*int_callback)(void *ctx, int val)) 00532 { 00533 JsonDecode_Callbacks.int_callback = int_callback; 00534 } 00535 00536 /** 00537 Set the function to be called back when a float is parsed. 00538 The function must have the format: 00539 \code 00540 bool on_float(void* context, float value); 00541 \endcode 00542 which would then be registered like so: 00543 \code 00544 JsonDecode_SetFloatCallback(on_float); 00545 \endcode 00546 The \b context parameter will be set to whatever you originally passed to 00547 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00548 this makes it convenient to know in your callback which one you're currently parsing. 00549 00550 @param float_callback The function to be called back. 00551 */ 00552 void JsonDecode_SetFloatCallback(bool(*float_callback)(void *ctx, float val)) 00553 { 00554 JsonDecode_Callbacks.float_callback = float_callback; 00555 } 00556 00557 /** 00558 Set the function to be called back when a boolean is parsed. 00559 The function must have the format: 00560 \code 00561 bool on_bool(void* context, bool value); 00562 \endcode 00563 which would then be registered like so: 00564 \code 00565 JsonDecode_SetBoolCallback(on_bool); 00566 \endcode 00567 The \b context parameter will be set to whatever you originally passed to 00568 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00569 this makes it convenient to know in your callback which one you're currently parsing. 00570 00571 @param bool_callback The function to be called back. 00572 */ 00573 void JsonDecode_SetBoolCallback(bool(*bool_callback)(void *ctx, bool val)) 00574 { 00575 JsonDecode_Callbacks.bool_callback = bool_callback; 00576 } 00577 00578 /** 00579 Set the function to be called back when a string is parsed. 00580 00581 \b Note - escaped elements in strings are respected, but not processed/removed 00582 from the string at the moment, since the internal implementation simply points 00583 to the string in the original data. If you have escaped data, you'll need to 00584 handle it in your code. 00585 00586 The function must have the format: 00587 \code 00588 bool on_string(void* context, char* string); 00589 \endcode 00590 which would then be registered like so: 00591 \code 00592 JsonDecode_SetStringCallback(on_string); 00593 \endcode 00594 The \b context parameter will be set to whatever you originally passed to 00595 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00596 this makes it convenient to know in your callback which one you're currently parsing. 00597 00598 @param string_callback The function to be called back. 00599 */ 00600 void JsonDecode_SetStringCallback(bool(*string_callback)(void *ctx, char *string, int len)) 00601 { 00602 JsonDecode_Callbacks.string_callback = string_callback; 00603 } 00604 00605 /** 00606 Set the function to be called back when an object is started. 00607 The left bracket - { - is the opening element of an object. 00608 The function must have the format: 00609 \code 00610 bool on_obj_started(void* context); 00611 \endcode 00612 which would then be registered like so: 00613 \code 00614 JsonDecode_SetStartObjCallback(on_obj_started); 00615 \endcode 00616 The \b context parameter will be set to whatever you originally passed to 00617 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00618 this makes it convenient to know in your callback which one you're currently parsing. 00619 00620 @param start_obj_callback The function to be called back. 00621 */ 00622 void JsonDecode_SetStartObjCallback(bool(*start_obj_callback)(void *ctx)) 00623 { 00624 JsonDecode_Callbacks.start_obj_callback = start_obj_callback; 00625 } 00626 00627 /** 00628 Set the function to be called back when the key of a key-value pair has been encountered. 00629 A key must always be a string in JSON, so you'll get the string back. This is particularly 00630 helpful for setting how the next element (the value in the key-value pair) should be handled. 00631 00632 The function must have the format: 00633 \code 00634 bool on_obj_key(void* context); 00635 \endcode 00636 which would then be registered like so: 00637 \code 00638 JsonDecode_SetObjKeyCallback(on_obj_key); 00639 \endcode 00640 The \b context parameter will be set to whatever you originally passed to 00641 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00642 this makes it convenient to know in your callback which one you're currently parsing. 00643 00644 @param obj_key_callback The function to be called back. 00645 */ 00646 void JsonDecode_SetObjKeyCallback(bool(*obj_key_callback)(void *ctx, char *key, int len)) 00647 { 00648 JsonDecode_Callbacks.obj_key_callback = obj_key_callback; 00649 } 00650 00651 /** 00652 Set the function to be called back when an object is ended. 00653 The right bracket - } - is the closing element of an object. 00654 The function must have the format: 00655 \code 00656 bool on_obj_ended(void* context); 00657 \endcode 00658 which would then be registered like so: 00659 \code 00660 JsonDecode_SetEndObjCallback(on_obj_ended); 00661 \endcode 00662 The \b context parameter will be set to whatever you originally passed to 00663 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00664 this makes it convenient to know in your callback which one you're currently parsing. 00665 00666 @param end_obj_callback The function to be called back. 00667 */ 00668 void JsonDecode_SetEndObjCallback(bool(*end_obj_callback)(void *ctx)) 00669 { 00670 JsonDecode_Callbacks.end_obj_callback = end_obj_callback; 00671 } 00672 00673 /** 00674 Set the function to be called back when an array is started. 00675 The left brace - [ - is the starting element of an array. 00676 The function must have the format: 00677 \code 00678 bool on_array_started(void* context); 00679 \endcode 00680 which would then be registered like so: 00681 \code 00682 JsonDecode_SetStartArrayCallback(on_array_started); 00683 \endcode 00684 The \b context parameter will be set to whatever you originally passed to 00685 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00686 this makes it convenient to know in your callback which one you're currently parsing. 00687 00688 @param start_array_callback The function to be called back. 00689 */ 00690 void JsonDecode_SetStartArrayCallback(bool(*start_array_callback)(void *ctx)) 00691 { 00692 JsonDecode_Callbacks.start_array_callback = start_array_callback; 00693 } 00694 00695 /** 00696 Set the function to be called back when an array is ended. 00697 The right brace - ] - is the closing element of an array. 00698 The function must have the format: 00699 \code 00700 bool on_array_ended(void* context); 00701 \endcode 00702 which would then be registered like so: 00703 \code 00704 JsonDecode_SetEndArrayCallback(on_array_ended); 00705 \endcode 00706 The \b context parameter will be set to whatever you originally passed to 00707 JsonDecode_Init(). Since each JsonDecode_State can have a different context, 00708 this makes it convenient to know in your callback which one you're currently parsing. 00709 00710 @param end_array_callback The function to be called back. 00711 */ 00712 void JsonDecode_SetEndArrayCallback(bool(*end_array_callback)(void *ctx)) 00713 { 00714 JsonDecode_Callbacks.end_array_callback = end_array_callback; 00715 } 00716 00717 /** 00718 Initialize or reset a JsonDecode_State variable. 00719 Do this prior to making a call to JsonDecode(). 00720 @param state A pointer to the JsonDecode_State variable being used for this decode process. 00721 @param context An optional paramter that your code can use to 00722 pass around a known object within the callbacks. Otherwise, just set it to 0 00723 */ 00724 void JsonDecode_Init(JsonDecode_State* state, void* context) 00725 { 00726 state->depth = 0; 00727 state->gotcomma = false; 00728 state->context = context; 00729 state->p = 0; 00730 state->len = 0; 00731 } 00732 00733 /** 00734 Parse a JSON string. 00735 The JSON parser is event based, meaning that you will receive any callbacks 00736 you registered for as the elements are encountered in the JSON string. 00737 00738 @param state A pointer to the JsonDecode_State variable being used for this decode process. 00739 @param text The JSON string to parse. 00740 @param len The length of the JSON string. 00741 @return True on a successful parse, false on failure. 00742 00743 \par Example 00744 \code 00745 // quotes are escaped since I'm writing it out manually 00746 JsonDecode_State s; 00747 char jsonstr[] = "[{\"label\":\"value\",\"label2\":{\"nested\":234}}]"; 00748 JsonDecode_Init(&s, 0); 00749 JsonDecode(jsonstr, strlen(jsonstr), 0); // don't pass in any context 00750 // now we expect to be called back on any callbacks we registered. 00751 \endcode 00752 */ 00753 bool JsonDecode(JsonDecode_State* state, char* text, int len) 00754 { 00755 JsonDecode_Token token; 00756 00757 // if these haven't been initialized, do it 00758 if(!state->p) 00759 state->p = text; 00760 if(!state->len) 00761 state->len = len; 00762 00763 while(state->len) 00764 { 00765 while(*state->p == ' ') // eat white space 00766 state->p++; 00767 token = JsonDecode_GetToken( state->p, state->len); 00768 switch(token) 00769 { 00770 case token_true: 00771 if(JsonDecode_Callbacks.bool_callback) 00772 { 00773 if(!JsonDecode_Callbacks.bool_callback(state->context, true)) 00774 return false; 00775 } 00776 state->p += 4; 00777 state->len -= 4; 00778 break; 00779 case token_false: 00780 if(JsonDecode_Callbacks.bool_callback) 00781 { 00782 if(!JsonDecode_Callbacks.bool_callback(state->context, false)) 00783 return false; 00784 } 00785 state->p += 5; 00786 state->len -= 5; 00787 break; 00788 case token_null: 00789 if(JsonDecode_Callbacks.null_callback) 00790 { 00791 if(!JsonDecode_Callbacks.null_callback(state->context)) 00792 return false; 00793 } 00794 state->p += 4; 00795 state->len -= 4; 00796 break; 00797 case token_comma: 00798 state->gotcomma = true; 00799 // intentional fall-through 00800 case token_colon: // just get the next one 00801 state->p++; 00802 state->len--; 00803 break; 00804 case token_left_bracket: 00805 state->steps[++state->depth] = JSON_DECODE_OBJECT_START; 00806 if(JsonDecode_Callbacks.start_obj_callback) 00807 { 00808 if(!JsonDecode_Callbacks.start_obj_callback(state->context)) 00809 return false; 00810 } 00811 state->p++; 00812 state->len--; 00813 break; 00814 case token_right_bracket: 00815 state->depth--; 00816 if(JsonDecode_Callbacks.end_obj_callback) 00817 { 00818 if(!JsonDecode_Callbacks.end_obj_callback(state->context)) 00819 return false; 00820 } 00821 state->p++; 00822 state->len--; 00823 break; 00824 case token_left_brace: 00825 state->steps[++state->depth] = JSON_DECODE_IN_ARRAY; 00826 if(JsonDecode_Callbacks.start_array_callback) 00827 { 00828 if(!JsonDecode_Callbacks.start_array_callback(state->context)) 00829 return false; 00830 } 00831 state->p++; 00832 state->len--; 00833 break; 00834 case token_right_brace: 00835 state->depth--; 00836 if(JsonDecode_Callbacks.end_array_callback) 00837 { 00838 if(!JsonDecode_Callbacks.end_array_callback(state->context)) 00839 return false; 00840 } 00841 state->p++; 00842 state->len--; 00843 break; 00844 case token_number: 00845 { 00846 const char* p = state->p; 00847 bool keepgoing = true; 00848 bool gotdecimal = false; 00849 do 00850 { 00851 if(*p == '.') 00852 { 00853 if(gotdecimal) // we only expect to get one decimal place in a number 00854 return false; 00855 gotdecimal = true; 00856 p++; 00857 } 00858 else if(!isdigit(*p)) 00859 keepgoing = false; 00860 else 00861 p++; 00862 } while(keepgoing); 00863 int size = p - state->p; 00864 if(gotdecimal) 00865 { 00866 if(JsonDecode_Callbacks.float_callback) 00867 { 00868 if(!JsonDecode_Callbacks.float_callback(state->context, atof(state->p))) 00869 return false; 00870 } 00871 } 00872 else 00873 { 00874 if(JsonDecode_Callbacks.int_callback) 00875 { 00876 if(!JsonDecode_Callbacks.int_callback(state->context, atoi(state->p))) 00877 return false; 00878 } 00879 } 00880 state->p += size; 00881 state->len -= size; 00882 break; 00883 } 00884 case token_string: 00885 { 00886 char* p = ++state->p; // move past the opening " 00887 state->len--; 00888 bool keepgoing = true; 00889 while(keepgoing) 00890 { 00891 if(*p == '\\') // we got an escape - skip the escape and the next character 00892 p += 2; 00893 else if(*p == '"') // we got the end of a string 00894 keepgoing = false; 00895 else 00896 p++; // keep looking for the end of the string 00897 } 00898 int size = p - state->p; 00899 *p = 0; // replace the trailing " with a null to make a string 00900 00901 // figure out if this is a key or a normal string 00902 bool objkey = false; 00903 if(state->steps[state->depth] == JSON_DECODE_OBJECT_START) 00904 { 00905 state->steps[state->depth] = JSON_DECODE_IN_OBJECT; 00906 objkey = true; 00907 } 00908 if(state->gotcomma && state->steps[state->depth] == JSON_DECODE_IN_OBJECT) 00909 { 00910 state->gotcomma = false; 00911 objkey = true; 00912 } 00913 00914 if(objkey) // last one was a comma - next string has to be a key 00915 { 00916 if(JsonDecode_Callbacks.obj_key_callback) 00917 { 00918 if(!JsonDecode_Callbacks.obj_key_callback(state->context, state->p, size)) 00919 return false; 00920 } 00921 } 00922 else // just a normal string 00923 { 00924 if(JsonDecode_Callbacks.string_callback) 00925 { 00926 if(!JsonDecode_Callbacks.string_callback(state->context, state->p, size)) 00927 return false; 00928 } 00929 } 00930 state->p += (size+1); // account for the trailing " 00931 state->len -= (size+1); 00932 break; 00933 } 00934 case token_eof: // we don't expect to get this, since our len should run out before we get eof 00935 return false; 00936 default: 00937 return false; 00938 } 00939 } 00940 return true; 00941 } 00942 00943 /** @} 00944 */ 00945 00946 // static 00947 JsonDecode_Token JsonDecode_GetToken(char* text, int len) 00948 { 00949 char c = text[0]; 00950 switch(c) 00951 { 00952 case ':': 00953 return token_colon; 00954 case ',': 00955 return token_comma; 00956 case '{': 00957 return token_left_bracket; 00958 case '}': 00959 return token_right_bracket; 00960 case '[': 00961 return token_left_brace; 00962 case ']': 00963 return token_right_brace; 00964 case '"': 00965 return token_string; 00966 case '0': case '1': case '2': case '3': case '4': 00967 case '5': case '6': case '7': case '8': case '9': 00968 return token_number; 00969 case '-': 00970 return token_maybe_negative; 00971 case 't': 00972 { 00973 if(len < 4) // not enough space; 00974 return token_unknown; 00975 if(!strncmp(text, "true", 4)) 00976 return token_true; 00977 else 00978 return token_unknown; 00979 } 00980 case 'f': 00981 { 00982 if(len < 5) // not enough space; 00983 return token_unknown; 00984 if(!strncmp(text, "false", 5)) 00985 return token_false; 00986 else 00987 return token_unknown; 00988 } 00989 case 'n': 00990 { 00991 if(len < 4) // not enough space; 00992 return token_unknown; 00993 if(!strncmp(text, "null", 4)) 00994 return token_null; 00995 else 00996 return token_unknown; 00997 } 00998 case '\0': 00999 return token_eof; 01000 default: 01001 return token_unknown; 01002 } 01003 } 01004 01005 01006 01007 01008
The Make Controller Kit is an open source project maintained by MakingThings.
MakingThings code is released under the Apache 2.0 license.
Bug tracker, development wiki and status can be found at http://dev.makingthings.com.
This document was last updated on 18 May 2009.