MakingThings
  • Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

json.c

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.