C-based memory friendly JSON parser based on Serge Zaitsev's JSMN (https://bitbucket.org/zserge/jsmn/wiki/Home)

Dependents:   _library_jsmn _library_jsmn _library_jsmn

JSMN

jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be easily integrated into resource-limited or embedded projects.

You can find more information about JSON format at json.org

Library sources are available at https://bitbucket.org/zserge/jsmn

The web page with some information about jsmn can be found at http://zserge.com/jsmn.html

Files at this revision

API Documentation at this revision

Comitter:
yoonghm
Date:
Tue May 06 09:33:30 2014 +0000
Child:
1:70061827a9c8
Commit message:
Initial version.

Changed in this revision

jsmn.cpp Show annotated file Show diff for this revision Revisions of this file
jsmn.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jsmn.cpp	Tue May 06 09:33:30 2014 +0000
@@ -0,0 +1,370 @@
+/* C-based low-memory footprint JSON parser for mbed
+ * Based on Serge Zaitsev's JSMN https://bitbucket.org/zserge/jsmn/wiki/Home
+ * JSMN is distributed under MIT license.
+ *
+ * Copyright (c) 2014 YoongHM
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include "jsmn.h"
+
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *
+jsmn_alloc_token
+(jsmn_parser *parser
+,jsmntok_t   *tokens
+,size_t       num_tokens
+)
+{
+  jsmntok_t  *tok;
+  if (parser->toknext >= num_tokens)
+  {
+    return NULL;
+  }
+  tok = &tokens[parser->toknext++];
+  tok->start = tok->end = -1;
+  tok->size = 0;
+  tok->parent = -1;
+  return tok;
+}
+
+
+/**
+ * Fills token type and boundaries.
+ */
+static void
+jsmn_fill_token
+(jsmntok_t   *token
+,jsmntype_t   type
+,int          start
+,int          end
+)
+{
+  token->type = type;
+  token->start = start;
+  token->end = end;
+  token->size = 0;
+}
+
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int
+jsmn_parse_primitive
+(jsmn_parser *parser
+,const char  *js
+,size_t       len
+,jsmntok_t   *tokens
+,size_t       num_tokens
+)
+{
+  jsmntok_t  *token;
+  int         start;
+
+  start = parser->pos;
+
+  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) 
+  {
+    switch (js[parser->pos])
+    {
+      case '\t':
+      case '\r': 
+      case '\n': 
+      case ' ' :
+      case ',' : 
+      case ']' : 
+      case '}' :
+        goto found;
+    }
+    if (js[parser->pos] < 32 || js[parser->pos] >= 127)
+    {
+      parser->pos = start;
+      return JSMN_ERR_INVAL;
+    }
+  }
+  parser->pos = start;
+  return JSMN_ERR_PART;
+
+found:
+  if (tokens == NULL)
+  {
+    parser->pos--;
+    return 0;
+  }
+  
+  token = jsmn_alloc_token(parser, tokens, num_tokens);
+  if (token == NULL)
+  {
+    parser->pos = start;
+    return JSMN_ERR_NOMEM;
+  }
+  jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+
+  token->parent = parser->toksuper;
+  parser->pos--;
+
+  return 0;
+}
+
+
+/**
+ * Fill next token with JSON string.
+ */
+static int
+jsmn_parse_string
+(jsmn_parser *parser
+,const char  *js
+,size_t       len
+,jsmntok_t   *tokens
+,size_t       num_tokens
+)
+{
+  jsmntok_t  *token;
+  int start = parser->pos;
+
+  parser->pos++;
+
+  /* Skip starting quote */
+  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) 
+  {
+    char c = js[parser->pos];
+
+    /* Quote: end of string */
+    if (c == '\"')
+    {
+      if (tokens == NULL)
+        return 0;
+
+      token = jsmn_alloc_token(parser, tokens, num_tokens);
+      if (token == NULL)
+      {
+        parser->pos = start;
+        return JSMN_ERR_NOMEM;
+      }
+
+      jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+      token->parent = parser->toksuper;
+      return 0;
+    }
+
+    /* Backslash: Quoted symbol expected */
+    if (c == '\\')
+    {
+      parser->pos++;
+      switch (js[parser->pos])
+      {
+        /* Allowed escaped symbols */
+        case '\"': 
+        case '/' : 
+        case '\\': 
+        case 'b' :
+        case 'f' : 
+        case 'r' : 
+        case 'n' : 
+        case 't' :
+          break;
+
+        /* Allows escaped symbol \uXXXX */
+        case 'u':
+          parser->pos++;
+          int i = 0;
+          for (; i < 4 && js[parser->pos] != '\0'; i++) 
+          {
+            /* If it isn't a hex character we have an ERR */
+            if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+                  (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+                  (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+              parser->pos = start;
+              return JSMN_ERR_INVAL;
+            }
+            parser->pos++;
+          }
+          parser->pos--;
+          break;
+
+        /* Unexpected symbol */
+        default:
+          parser->pos = start;
+          return JSMN_ERR_INVAL;
+      }
+    }
+  }
+  parser->pos = start;
+  return JSMN_ERR_PART;
+}
+
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int 
+jsmn_parse
+(jsmn_parser *parser
+,const char  *js
+,size_t       len
+,jsmntok_t   *tokens
+,unsigned int num_tokens
+)
+{
+  int         r;
+  int         i;
+  jsmntok_t  *token;
+  int         count = 0;
+
+  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) 
+  {
+    char      c;
+    jsmntype_t type = JSMN_INVALID;
+
+    c = js[parser->pos];
+    switch (c)
+    {
+      case '{' : 
+      case '[' :
+        count++;
+        if (tokens == NULL)
+          break;
+
+        token = jsmn_alloc_token(parser, tokens, num_tokens);
+
+        if (token == NULL)
+          return JSMN_ERR_NOMEM;
+
+        if (parser->toksuper != -1)
+        {
+          tokens[parser->toksuper].size++;
+          token->parent = parser->toksuper;
+        }
+        token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+        token->start = parser->pos;
+        parser->toksuper = parser->toknext - 1;
+        break;
+
+      case '}': 
+      case ']':
+        if (tokens == NULL)
+          break;
+
+        type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+
+        if (parser->toknext < 1)
+          return JSMN_ERR_INVAL;
+
+        token = &tokens[parser->toknext - 1];
+        for (;;)
+        {
+          if (token->start != -1 && token->end == -1) 
+          {
+            if (token->type != type) 
+              return JSMN_ERR_INVAL;
+
+            token->end = parser->pos + 1;
+            parser->toksuper = token->parent;
+            break;
+          }
+
+          if (token->parent == -1) 
+            break;
+
+          token = &tokens[token->parent];
+        }
+        break;
+
+      case '\"':
+        r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+        if (r < 0)
+          return r;
+
+        count++;
+        if (parser->toksuper != -1 && tokens != NULL)
+          tokens[parser->toksuper].size++;
+        break;
+
+      case '\t': 
+      case '\r': 
+      case '\n': 
+      case ':' : 
+      case ',' : 
+      case ' ' : 
+        break;
+      
+      case '-' : 
+      case '0' : 
+      case '1' : 
+      case '2' : 
+      case '3' : 
+      case '4' :
+      case '5' : 
+      case '6' : 
+      case '7' : 
+      case '8' : 
+      case '9' : 
+      case 't' : 
+      case 'f' : 
+      case 'n' :
+        r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+        if (r < 0)
+          return r;
+
+        count++;
+
+        if (parser->toksuper != -1 && tokens != NULL)
+          tokens[parser->toksuper].size++;
+        break;
+
+      /* Unexpected char in strict mode */
+      default:
+        return JSMN_ERR_INVAL;
+    }
+  }
+
+  for (i = parser->toknext - 1; i >= 0; i--) 
+  {
+    /* Unmatched opened object or array */
+    if (tokens[i].start != -1 && tokens[i].end == -1) 
+    {
+      return JSMN_ERR_PART;
+    }
+  }
+
+  return count;
+}
+
+/**
+ * Creates a new parser based over a given  buffer with an array of tokens 
+ * available.
+ */
+void 
+jsmn_init
+(jsmn_parser *parser)
+{
+  parser->pos = 0;
+  parser->toknext = 0;
+  parser->toksuper = -1;
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jsmn.h	Tue May 06 09:33:30 2014 +0000
@@ -0,0 +1,209 @@
+/* C-based low-memory footprint JSON parser for mbed
+ * Based on Serge Zaitsev's JSMN https://bitbucket.org/zserge/jsmn/wiki/Home
+ * JSMN is distributed under MIT license.
+ *
+ * Copyright (c) 2014 YoongHM
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MBED_JSMN_H
+#define MBED_JSMN_H
+
+/**
+ * A c-based low-memory low-memory footprint JSON parser for mbed.
+ *
+ * The code is based on Serge Zaitsev's JSMN on
+ * https://bitbucket.org/zserge/jsmn/wiki/Home
+ *
+ * @code
+ *#include "mbed.h"
+ *
+ *#include "jsmn.h"
+ *
+ *#define       MAXTOKEN       64
+ *
+ *const char *jsmn_type_str[] = {
+ *  "PRIMITIVE",
+ *  "OBJECT",
+ * "ARRAY",
+ *  "STRING"
+ *};
+ *
+ *int main()
+ *{
+ *  const char *js;            // Pointer to json string
+ *  int         r;             // Number of token parsed
+ *  jsmn_parser p;             // jsmn parser
+ *  jsmntok_t   t[MAXTOKEN];   // Parsed token
+ *
+ *  // JSON may contain multibyte characters or encoded unicode in
+ *  // \uXXXX format.
+ *  // mbed compiler may complain "invalid multibyte characters ...".
+ *  js =
+ *"{"
+ *"  \"menu\":"
+ *"  {"
+ *"    \"id\": 1234,"
+ *"    \"group\": \"File\","
+ *"    \"popup\":"
+ *"    {"
+ *"      \"menuitem\":"
+ *"      ["
+ *"        {\"value\": true,    \"onclick\"  : \"বিশাল\"},"
+ *"        {\"value\": false,   0x1328       : \"groß\"},"
+ *"        {\"value\": null,    \"15\u00f8C\": \"3\u0111\"},"
+ *"        {\"value\": \"測試\", -12.34       :  99}"
+ *"      ]"
+ *"    }"
+ *"  }"
+ *"}";
+ *
+ *  jsmn_init(&p);
+ *  r = jsmn_parse(&p, js, strlen(js), t, MAXTOKEN);
+ *
+ *  printf("Parsed %d tokens\n", r);
+ *  
+ *  printf("            TYPE       START   END  SIZE PAR\n");
+ *  printf("           ----------  -----  ----  ---- ---\n");
+ *
+ *  char        ch;
+ *  jsmntok_t   at;            // A token for general use
+ *  for (int i = 0; i < r; i++)
+ *  {
+ *    at = t[i];
+ *    printf("Token %2d = %-10.10s (%4d - %4d, %3d, %2d) --> ",
+ *           i, jsmn_type_str[at.type],
+ *           at.start, at.end,
+ *           at.size, at.parent);
+ *
+ *    switch (at.type)
+ *    {
+ *      case JSMN_STRING:
+ *        printf("%-10.*s\n", at.end - at.start + 2, js + at.start - 1);
+ *        break;
+ *
+ *      case JSMN_PRIMITIVE:
+ *        ch = *(js + at.start);
+ *
+ *        if (isdigit(ch) || ch == '-') 
+ *          printf("%-10.*s\n", at.end - at.start, js + at.start);
+ *        else if (tolower(ch) == 'n')
+ *          printf("null\n");
+ *        else if (tolower(ch) == 't')
+ *          printf("true\n");
+ *        else if (tolower(ch) == 'f')
+ *          printf("false\n");
+ *        break;
+ *
+ *      default:
+ *        printf("\n");
+ *        break;
+ *    }
+ *  }
+ * 
+ *  while (1)
+ *    ;
+ *} 
+ * @endcode
+ */
+
+/** jsmntype_t - JSON type recongized by JSMN.
+ *  There are
+ *  - Primitive: number, boolean (true/false) or null
+ *  - Object
+ *  - Array
+ *  - String
+ */
+typedef enum {
+  JSMN_PRIMITIVE = 0,
+  JSMN_OBJECT    = 1,
+  JSMN_ARRAY     = 2,
+  JSMN_STRING    = 3,
+  JSMN_INVALID   = 4
+} jsmntype_t;
+
+
+/** Error message return by JSMN API
+ * 
+ */
+#define  JSMN_ERR_OKAY    0  /* No problem                            */
+#define  JSMN_ERR_NOMEM  -1  /* Not enough tokens were provided       */
+#define  JSMN_ERR_INVAL  -2  /* Invalid character inside JSON string  */
+#define  JSMN_ERR_PART   -3  /* Incomplete JSON packet                */ 
+
+
+/** jsmntok_t - JSON token structure.
+ * 
+ * It is a c structure that contained parsed JSON token.
+ *
+ * @param   type    type (primitive, object, array, string)
+ * @param   start   start position in JSON data string, excl " if is a string
+ * @param   end     end position in JSON data string, incl " if is a string
+ * @param   size    number of token directly under it
+ * @param   parent  link to its direct parent token number
+ */
+typedef struct {
+  jsmntype_t type;
+  int        start;          /* Token's start pos;excl " if is a str  */
+  int        end;            /* Token's last  pos;incl " if is a str  */
+  int        size;           /* Number of token directly under it     */
+  int        parent;         /* Nth-token which is its direct parent  */
+} jsmntok_t;
+
+
+/** JSON parser - JSON parser structure.
+ *
+ * Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+  unsigned int pos;          /* offset in the JSON string */
+  unsigned int toknext;      /* next token to allocate */
+  int          toksuper;     /* superior token node, (parent or array)*/
+} jsmn_parser;
+
+
+/** Initialize jsmn_parser structure for a new JSON parsing
+ *
+ * @param *parser  jsmn_parser structure
+ */
+void 
+jsmn_init
+(jsmn_parser *parser
+);
+
+/** Parse a given JSON data string into array of jsmntok_t tokens.
+ * 
+ * @param *parser  jsmn_parser structure
+ * @param *js      JSON data string
+ * @param  len     string length of *js
+ * @param *tokens  tokens to hold the parsed data 
+ */
+int
+jsmn_parse
+(jsmn_parser *parser
+,const char  *js
+,size_t       len
+,jsmntok_t   *tokens
+,unsigned int num_tokens
+);
+
+
+#endif // JSMN_H
\ No newline at end of file