GithubHelp home page GithubHelp logo

ghh_json's Introduction

ghh_json.h

a single-header ISO-C99 (and C++ compatible) json loader.

why?

obviously this isn't the first json library written for C, so why would I write this?

  1. easy build

there are great json C libraries with clean APIs and many more features than my implementation out there, but they require build system integration which immediately adds work I don't want to deal with.

  1. easy usage

there are other single-header json libraries for C out there, but I wasn't super into the way they are structured. I wrote this the way I would expect a json library to behave. this is more of a taste thing than anything, if you don't like how ghh_json works check out another implementation.

usage

note: ghh_json does not currently support unicode escape sequences, like "\u0123". otherwise it is json specification conforming.

also see examples

to include in your project:

#define GHH_JSON_IMPL
#include "ghh_json.h"

the basic program structure looks like this:

json_t json;
json_load_file(&json, "mydata.json");

// do stuff ...

json_unload(&json);

there are only 3 types you need to think about:

  • json_type_e, json type enum
    • types are: JSON_OBJECT, JSON_ARRAY, JSON_STRING, JSON_NUMBER, JSON_TRUE, JSON_FALSE, JSON_NULL
  • json_object_t, a tagged union
    • access type through .type
    • access and modify data using json_get, json_put, and json_pop functions
  • json_t, a reusable memory context for json objects
    • access root element through .root.
    • when unloaded, all memory associated with it is freed - so you don't need to explicitly manage individual json_object_t *s. unloading includes popped objects, so if you create a new object on one json_t and then put it on another, the object will be invalidated once its parent json_t is unloaded.

api

settings

// define your own allocation functions
#define JSON_MALLOC(size)
#define JSON_FREE(size)

// change the size of the char buffer for fread()
#define JSON_FREAD_BUF_SIZE

// the json_t allocator works by allocating pages to accommodate objects and
// data. increasing this means less allocations during parsing
#define JSON_PAGE_SIZE

json_t lifetime

// load json from a string
void json_load(json_t *, char *text);
// create an empty json_t context
void json_load_empty(json_t *);
// load json from a file
void json_load_file(json_t *, const char *filepath);
// free all memory associated with json context
void json_unload(json_t *);

data access

// returns a string allocated with JSON_MALLOC
// if mini, won't add newlines or indentation
char *json_serialize(json_object_t *, bool mini, int indent, size_t *out_len);

// retrieve a key from an object
// if NDEBUG is not defined, will type check the root object
json_object_t *json_get_object(json_object_t *, char *key);
// returns actual, mutable array pointer
json_object_t **json_get_array(json_object_t *, char *key, size_t *out_size);
char *json_get_string(json_object_t *, char *key);
double json_get_number(json_object_t *, char *key);
bool json_get_bool(json_object_t *, char *key);

// cast an object to a type
// if NDEBUG is not defined, will type check the object
json_object_t **json_to_array(json_object_t *, size_t *out_size);
char *json_to_string(json_object_t *);
double json_to_number(json_object_t *);
bool json_to_bool(json_object_t *);

data modification

// remove a json_object from another json_object (unordered)
json_object_t *json_pop(json_t *, json_object_t *, char *key);
// pop but ordered, this is O(n) rather than O(1) removal time
json_object_t *json_pop_ordered(json_t *, json_object_t *, char *key);

// add a json_object to another json_object
void json_put(json_t *, json_object_t *, char *key, json_object_t *child);

// create a new json type on a json_t, and add it to an object
json_object_t *json_put_object(json_t *, json_object_t *, char *key);
void json_put_array(
	json_t *, json_object_t *, json_object_t **objects, size_t size
);
void json_put_string(json_t *, json_object_t *, char *key, char *string);
void json_put_number(json_t *, json_object_t *, char *key, double number);
void json_put_bool(json_t *, json_object_t *, bool value);
void json_put_null(json_t *, json_object_t *, char *key);

// create a json data type on a json_t memory context
json_object_t *json_new_object(json_t *);
json_object_t *json_new_array(json_t *, json_object_t **objects, size_t size);
json_object_t *json_new_string(json_t *, char *string);
json_object_t *json_new_number(json_t *, double number);
json_object_t *json_new_bool(json_t *, bool value);
json_object_t *json_new_null(json_t *);

examples

// load json from a file and print it to stdout with formatting
json_t json;
json_load_file(&json, "data.json");

char *str = json_serialize(json.root, false, 2, NULL);
printf(str);
free(str);

json_unload(&json);
// load json data into a struct
char *json_data =
"[{\"name\": \"henry\", \"age\": 21},"
" {\"name\": \"barb\", \"age\": 56}]";

struct person { char *name; int age; } people[2];

json_t json;
json_load(&json, json_data);

// grab array of objects
size_t length;
json_object_t **objects = json_to_array(json.root, &length);

// retrieve object members
for (size_t i = 0; i < length; ++i) {
    people[i].name = strdup(json_get_string(objects[i], "name"));
    people[i].age = (int)json_get_number(objects[i], "age");
}

json_unload(&json);
// create an empty json document, add data, and save
const size_t num_countries = 3;
struct country {
    char *name;
    int population;
    bool is_dope;
} countries[num_countries] = {
    {"america", 3, true},
    {"canada", 10000, true},
    {"india", 56, true}
};

json_t json;
json_load_empty(&json);

// add data
json.root = json_new_object(&json);

for (size_t i = 0; i < num_countries; ++i) {
    // create a new country entry
    json_object_t *country = json_put_object(
        &json,
        json.root,
        countries[i].name
    );

    // fill in country data
    json_put_number(&json, country, "population", countries[i].population);
    json_put_bool(&json, country, "is_dope", countries[i].is_dope);
}

// serialize and save to file
char *str = json_serialize(json.root, false, 2, NULL);

FILE *file = fopen("data.json", "w");
fprintf(file, str);
fclose(file);

free(str);

json_unload(&json);

ghh_json's People

Contributors

garrisonhh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

ghh_json's Issues

Merging two JSON objects

Is there an easy way to merge two JSON objects? E.g. if they have the same keys then overwrite, otherwise add new?

Thanks,
Bartek

Non-scientific floating point numbers not supported

Hi,

Thanks for this great library! I noticed that non-scientific floating point numbers are not supported, i.e. they are parsed into completely wrong numbers which was hard to debug :)

E.g. this works: 1e-3 but this doesn't: 0.001, it actually outputs 0.1.

I propose to simplify json_expect_number to this code:

#include <stdlib.h>. // for strtod
...
static double json_expect_number(json_ctx_t *ctx) {
    // Convert string to double
    double num = strtod(&ctx->text[ctx->index], NULL);

    // Move pointer until not a comma and not end of line
    while (ctx->text[ctx->index] != ',' && ctx->text[ctx->index] != '\n' && ctx->text[ctx->index] != ']') {
        ctx->index++;
    }

    return num;
}

Works well this way with both scientific and regular notation.

Cheers,
Bartek

Error if empty array

Hi,

Thank you for your great and useful json parser! It's super easy to use.

I've got an error when trying to parse a json object with an empty array. Example:

    json_t json2;
    char *test = "{\"foo\":\"bar\",\"boo\":[]}";
    json_load(&json2, test);

Error:

JSON ERROR: unknown token, expected value.
     1 | {"foo":"bar","boo":[]}
       |     

Kind regards,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.