turtledb / frozen

JSON parser and generator for C/C++

Home Page:http://cesanta.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JSON parser and generator for C/C++

Features

  • Portable to any environment
  • Simple API: 2 functions for parsing and 4 for generation
  • Very small footprint
  • No dependencies
  • Makes no memory allocations
  • Code is strict ISO C and strict ISO C++ at the same time
  • Supports superset of JSON: allows non-quoted identifiers as object keys
  • Complete 100% test coverage

How to use it

  1. Copy frozen.c and frozen.h to your project

  2. Add frozen.c to the list of source files

  3. Parsing with Frozen is done in two steps: first, split JSON string into tokens by parse_json(). Second, search for certain parameters in parsed string by find_json_token(). Below is an example, error handling is omitted for clarity:

    #include <stdio.h> #include "frozen.h"

    int main(void) { static const char *json = " { foo: 1, bar: 2 } "; struct json_token arr[10], *tok;

    // Tokenize json string, fill in tokens array
    parse_json(json, strlen(json), arr, sizeof(arr) / sizeof(arr[0]));
    
    // Search for parameter "bar" and print it's value
    tok = find_json_token(arr, "bar");
    printf("Value of bar is: [%.*s]\n", tok->len, tok->ptr);
    
    return 0;
    

    }

API documentation

int parse_json(const char *json_string, int json_string_length,
               struct json_token *tokens_array, int size_of_tokens_array);

Tokenizes json_string of length json_string_length. If tokens_array is not NULL, then all parse_json will store tokens in the tokens_array. Token with type JSON_TYPE_EOF marks the end of parsed tokens. JSON token is defined as:

struct json_token {
  const char *ptr;    // Points to the beginning of the token
  int len;            // Token length
  int num_desc;       // For arrays and object, total number of descendants
  int type;           // Type of the token, possible values below

#define JSON_TYPE_EOF     0   // Not a real token, but end-of-tokens marker
#define JSON_TYPE_STRING  1
#define JSON_TYPE_NUMBER  2
#define JSON_TYPE_OBJECT  3
#define JSON_TYPE_TRUE    4
#define JSON_TYPE_FALSE   5
#define JSON_TYPE_NULL    6
#define JSON_TYPE_ARRAY   7
};

If tokens_array is NULL, then parse_json just checks the validity of the JSON string, and points where parsing stops. If tokens_array is not NULL, it must be pre-allocated by the caller. Note that struct json_token just points to the data inside json_string, it does not own the data. Thus the token's lifetime is identical to the lifetime of json_string, until json_string is freed or mutated.
Return: On success, an offset inside json_string is returned where parsing has finished. On failure, a negative number is returned, one of:

#define JSON_STRING_INVALID           -1
#define JSON_STRING_INCOMPLETE        -2
#define JSON_TOKEN_ARRAY_TOO_SMALL    -3

Below is an illustration on how JSON string gets tokenized:

   JSON string:      {  "key_1" : "value_1",  "key_2": [ 12345, null  ]   }

   JSON_TYPE_OBJECT  |<-------------------------------------------------->|
   JSON_TYPE_STRING      |<->|
   JSON_TYPE_STRING                |<--->|
   JSON_TYPE_STRING                            |<->|
   JSON_TYPE_ARRAY                                     |<------------>|
   JSON_TYPE_NUMBER                                      |<->|
   JSON_TYPE_NULL                                               |<>|
   JSON_TYPE_EOF
const struct json_token *find_json_token(const struct json_token *toks,
                                         const char *path);

This is a convenience function to fetch specific values from the parsed string. toks must be a valid array, successfully populated by parse_json(). A path is a string, an accessor to the desired element of the JSON object, as if it was written in Javascript. For example, if parsed JSON string is
"{ foo : { bar: [1, 2, 3] } }", then path "foo.bar[0]" would return a token that points to number "1".
Return: pointer to the found token, or NULL on failure.

int json_emit_int(char *buf, int buf_len, long int value);
int json_emit_double(char *buf, int buf_len, double value);
int json_emit_quoted_str(char *buf, int buf_len, const char *str);
int json_emit_raw_str(char *buf, int buf_len, const char *str);

These functions are used to generate JSON string. All of them accept a destination buffer and a value to output, and return number of bytes printed. Returned value can be bigger then destination buffer size, this is an indication of overflow. If there is no overflow, a buffer is guaranteed to be nul-terminated. Numbers are printed by json_emit_double() and json_emit_int() functions, strings are printed by json_emit_quoted_str() function. Values for null, true, false, and characters {, }, [, ], ,, : are printed by json_emit_raw_str() function.

Example: accessing configuration parameters

#include "frozen.h"

static const char *config_str = " { ports: [ 80, 443 ] } ";
struct json_token tokens[10];
int tokens_size = sizeof(tokens) / sizeof(tokens[0]);

// Parse config string and make sure tokenization is correct
ASSERT(parse_json(config_str, strlen(config_str), tokens, tokens_size) > 0);

ASSERT(tokens[0].type == JSON_TYPE_OBJECT);   // Tokens are populated
ASSERT(tokens[1].type == JSON_TYPE_STRING);   // in order of their
ASSERT(tokens[2].type == JSON_TYPE_ARRAY);    // appearance in the
ASSERT(tokens[3].type == JSON_TYPE_NUMBER);   // JSON string
ASSERT(tokens[4].type == JSON_TYPE_NUMBER);
ASSERT(tokens[5].type == JSON_TYPE_EOF);      // Last token is always EOF

// Fetch port values
ASSERT(find_json_token(tokens, "ports") == &tokens[2]);
ASSERT(find_json_token(tokens, "ports[0]") == &tokens[3]);
ASSERT(find_json_token(tokens, "ports[1]") == &tokens[4]);
ASSERT(find_json_token(tokens, "ports[3]") == NULL);  // Outside boundaries
ASSERT(find_json_token(tokens, "foo.bar") == NULL);   // Nonexistent

Example: generating JSON string {"foo":[-123,true]}

char buf[1000], *p = buf;

p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, "{");
p += json_emit_quoted_str(p, &buf[sizeof(buf)] - p, "foo");
p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, ":[");
p += json_emit_int(p, &buf[sizeof(buf)] - p, -123);
p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, ",true]}");

ASSERT(strcmp(buf, "{\"foo\":[-123,true]}") == 0);
ASSERT(p < &buf[sizeof(buf)]);

License

Frozen is released under GNU GPL v.2. Businesses have an option to get non-restrictive, royalty-free commercial license and professional support from Cesanta Software.

Super Light DNS Resolver, Super Light Regexp Library, Mongoose web server are other projects by Cesanta Software, developed with the same philosophy of functionality and simplicity.

About

JSON parser and generator for C/C++

http://cesanta.com

License:Other