12

I need to parse a small JSON file on an embedded system (only 10K RAM/flash). The JSON is:

{
"data1":[1,2,3,4,5,6,7,8,9],
"data2":[
     [3,4,5,6,1],
     [8,4,5,6,1],
     [10,4,5,3,61],
     [3,4,5,6,1],
     [3,4,5,6,1],
     [3,4,5,6,1] 
]}

jsmn looks great to fit the requirement, but it's not like most JSON parsers, since it only gives you tokens. I tried, but could not figure it out.

Could someone share an example of how to parse it with jsmn?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Vincent Zhou
  • 503
  • 1
  • 6
  • 17
  • 1
    I'd be astounded if it took as much as 6Kb to parse that! Are you sure that is not the size of the entire *object code library* rather than the binary footprint of a final linked application? – Clifford Jan 18 '13 at 19:50
  • 1
    Thanks for your apply, H2CO3. I'm new to jsmn and if could, can you share your code to parse that? Thanks. – Vincent Zhou Jan 18 '13 at 20:34
  • 6
    jsmn _is_ an unusual parser; it's right there in its philosophy -- "Most JSON parsers offer you a bunch of functions to load JSON data, parse it and extract any value by its name. [jsmn does not]." Also, its emphasis on minimalism extends to its documentation. – Ben Flynn Feb 05 '13 at 01:24

4 Answers4

25

jsmn will give you a set of tokens referring to the tokens in your JSON reading left to right.

In your case:

token[0]: (outer) object, 2 children
token[1]: string token ("data1")
token[2]: array, 9 children
token[3]: primitive token (1)
etc...

The basic code to do the parsing is:

int resultCode;
jsmn_parser p;
jsmntok_t tokens[128]; // a number >= total number of tokens

jsmn_init(&p);
resultCode = jsmn_parse(&p, yourJson, tokens, 256);

The other trick is getting the value of a token. The token contains the start and end points of the data on your original string.

jsmntok_t key = tokens[1];
unsigned int length = key.end - key.start;
char keyString[length + 1];    
memcpy(keyString, &yourJson[key.start], length);
keyString[length] = '\0';
printf("Key: %s\n", keyString);

With that you should be able to figure you how to iterate through your data.

Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
  • 1
    Forgive me if I'm wrong, by shouldn't `token[1]` be `tokens[1]` (if that's what it was originally called). Also, I couldn't compile the second part with `*key`, had to make it just `key`? Thx again – SSH This Feb 11 '13 at 04:55
  • Thanks @SSHThis -- that's what I get for coding directly into the answer box. I'll update with your fixes. – Ben Flynn Feb 11 '13 at 17:55
  • Thanks Ben and SSHThis. it's very helpful – Vincent Zhou Feb 21 '13 at 17:29
  • 1
    How can I know how many tokens were parsed? Or how can I iterate through the results and know I've reached the end? – hippietrail Mar 12 '13 at 08:52
  • 1
    @hippietrail it should be the "size" of the first token, since all other tokens will be a child of it. – Ben Flynn Mar 12 '13 at 17:14
  • 1
    @hippietrail If jsmn_parse suceeds, it will return the number of tokens that were parsed. – Klas. S Jun 11 '16 at 11:13
4

I was curious as about this as well, so I made a small program to show the structure of the tokens array:

This program:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "jsmn.h"


jsmntok_t t[512];
char jsonStr[512*1024];

int main()
{
  size_t pos = 0;
  int    c;
  while ((c = getchar()) != EOF)
  {
    jsonStr[pos] = c;
    ++pos;
    if (pos == sizeof(jsonStr)) break;
  }
  jsonStr[pos] = 0;

  jsmn_parser p;
  jsmn_init(&p);

  int tCount = jsmn_parse(&p, jsonStr, strlen(jsonStr), t, sizeof(t)/sizeof(*t));

  for (int i = 0; i != tCount; ++i)
  {
    jsmntok_t *token = t + i;
    char * type = 0;
    switch (token->type)
    {
      case JSMN_PRIMITIVE:
        type = "PRIMITIVE";
        break;
      case JSMN_OBJECT:
        type = "OBJECT";
        break;
      case JSMN_ARRAY:
        type = "ARRAY";
        break;
      case JSMN_STRING:
        type = "STRING";
        break;
      default:
        type = "UNDEF";
    }
#ifdef JSMN_PARENT_LINKS
    printf("node: %4d, %9s, parent: %4d, children: %5d, data:\n%.*s, \n", i, type, token->parent, token->size, token->end-token->start, jsonStr+token->start);
#else
    printf("node: %4d, %9s, children: %5d, data:\n%.*s, \n", i, type, token->size, token->end-token->start, jsonStr+token->start);
#endif
  }
}

prints this json (taken from: https://en.wikipedia.org/wiki/JSON):

{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    },
    {
      "type": "mobile",
      "number": "123 456-7890"
    }
  ],
  "children": [],
  "spouse": null
}

as:

node:    0,    OBJECT, parent:   -1, children:     8, data:
{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    },
    {
      "type": "mobile",
      "number": "123 456-7890"
    }
  ],
  "children": [],
  "spouse": null
},
node:    1,    STRING, parent:    0, children:     1, data:
firstName,
node:    2,    STRING, parent:    1, children:     0, data:
John,
node:    3,    STRING, parent:    0, children:     1, data:
lastName,
node:    4,    STRING, parent:    3, children:     0, data:
Smith,
node:    5,    STRING, parent:    0, children:     1, data:
isAlive,
node:    6, PRIMITIVE, parent:    5, children:     0, data:
true,
node:    7,    STRING, parent:    0, children:     1, data:
age,
node:    8, PRIMITIVE, parent:    7, children:     0, data:
25,
node:    9,    STRING, parent:    0, children:     1, data:
address,
node:   10,    OBJECT, parent:    9, children:     4, data:
{
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
node:   11,    STRING, parent:   10, children:     1, data:
streetAddress,
node:   12,    STRING, parent:   11, children:     0, data:
21 2nd Street,
node:   13,    STRING, parent:   10, children:     1, data:
city,
node:   14,    STRING, parent:   13, children:     0, data:
New York,
node:   15,    STRING, parent:   10, children:     1, data:
state,
node:   16,    STRING, parent:   15, children:     0, data:
NY,
node:   17,    STRING, parent:   10, children:     1, data:
postalCode,
node:   18,    STRING, parent:   17, children:     0, data:
10021-3100,
node:   19,    STRING, parent:    0, children:     1, data:
phoneNumbers,
node:   20,     ARRAY, parent:   19, children:     3, data:
[
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    },
    {
      "type": "mobile",
      "number": "123 456-7890"
    }
  ],
node:   21,    OBJECT, parent:   20, children:     2, data:
{
      "type": "home",
      "number": "212 555-1234"
    },
node:   22,    STRING, parent:   21, children:     1, data:
type,
node:   23,    STRING, parent:   22, children:     0, data:
home,
node:   24,    STRING, parent:   21, children:     1, data:
number,
node:   25,    STRING, parent:   24, children:     0, data:
212 555-1234,
node:   26,    OBJECT, parent:   20, children:     2, data:
{
      "type": "office",
      "number": "646 555-4567"
    },
node:   27,    STRING, parent:   26, children:     1, data:
type,
node:   28,    STRING, parent:   27, children:     0, data:
office,
node:   29,    STRING, parent:   26, children:     1, data:
number,
node:   30,    STRING, parent:   29, children:     0, data:
646 555-4567,
node:   31,    OBJECT, parent:   20, children:     2, data:
{
      "type": "mobile",
      "number": "123 456-7890"
    },
node:   32,    STRING, parent:   31, children:     1, data:
type,
node:   33,    STRING, parent:   32, children:     0, data:
mobile,
node:   34,    STRING, parent:   31, children:     1, data:
number,
node:   35,    STRING, parent:   34, children:     0, data:
123 456-7890,
node:   36,    STRING, parent:    0, children:     1, data:
children,
node:   37,     ARRAY, parent:   36, children:     0, data:
[],
node:   38,    STRING, parent:    0, children:     1, data:
spouse,
node:   39, PRIMITIVE, parent:   38, children:     0, data:
null,
Lanting
  • 3,060
  • 12
  • 28
  • @NTMS that's the full code (excluding jsmn itself). Syntax should comply with c99 or c11. The json is read from standard input, and the result outputted to standard output. Something like `gcc --std=gnu11 main.c jsmn.c && cat my.json | a.out` should do the trick. – Lanting Nov 01 '17 at 12:25
1

You can use tiny-json. It gives you more than tokens. I have used with microcontrollers of 16-bits and 32-bits and it works fine.

char str[] = "{"
                 "\"data1\":[1,2,3,4,5,6,7,8,9],"
                 "\"data2\":["
                     "[3,4,5,6,1],"
                     "[8,4,5,6,1],"
                     "[10,4,5,3,61],"
                     "[3,4,5,6,1],"
                     "[3,4,5,6,1],"
                     "[3,4,5,6,1]"
                 "]"
             "}";
puts( str );

json_t pool[64];
json_t const* root = json_create( str, pool, 64 );

json_t const* data1 = json_getProperty( root, "data1" );
if ( data1 && JSON_ARRAY == json_getType( data1 ) ) {

    json_t const* field = json_getChild( data1 );
    while( field ) {

        if ( JSON_INTEGER == json_getType( field ) ) {
            long long int data = json_getInteger( field );
            printf("Integer from data1: %lld\n", data );
        }

        field = json_getSibling( field );
    }
}

json_t const* data2 = json_getProperty( root, "data2" );
if ( data2 && JSON_ARRAY == json_getType( data2 ) ) {

    json_t const* array = json_getChild( data2 );
    while( array ) {

        if ( JSON_ARRAY == json_getType( array ) ) {
            puts("Array in data2");

            json_t const* field = json_getChild( array );
            while( field ) {

                if ( JSON_INTEGER == json_getType( field ) ) {
                    long long int data = json_getInteger( field );
                    printf("Integer from array of data2: %lld\n", data );
                }

                field = json_getSibling( field ); 
            }
        }
        array = json_getSibling( array );
    }
} 
Rafa
  • 11
  • 1
1

I've just created jsmnRipper. This code lets you extract the tokens parsed with jsmn in a very simple way.

As an example here is the result of parsed the JSON message returned by ACRCloud

  • metadata.music[1].score:100:
  • metadata.music[1].album.name:The Best Of Depeche Mode Volume 1:
  • metadata.music[1].artists[0].name:Depeche Mode:
  • metadata.music[1].title:Enjoy The Silence (Remastered Version) (Original):

You can find the code for doing that at https://github.com/ipserc/jsmnRipper