Parson

A simple and compact C library for JSON processing
2014-09-14
~
4 min read

The soaring popularity of web APIs in recent years has made the use of JSON as data storage increasingly more common. Many of the contemporary scripting languages, including Ruby and Python, support it directly in their standard libraries; it is compact, yet pretty versatile and also human readable, so why not use it? Personally, I prefer JSON to other formats, such as XML. But what if you need to read it from C where string manipulation alone is a pain? As it turns out, there is quite a selection of libraries available. Today, I would like to introduce to you my favourite one.

Simple and Lightweight

Parson has a simple and very descriptive interface. All you need to read is this 100 lines-long header file and you pretty much know how to use it. The API comprises a set of functions to traverse through your data either by stepping through the hierarchy of objects and arrays one level at a time, or using the familiar dot.notation to access a key deep in the structure without having to unwrap each layer of it.

Parson is very compact as it is only made up by two files. That makes it easy to integrate in you project (hello git submodules) without having to worry about external dependencies. The library itself doesn’t depend on anything apart from libc.

A Complete Example

Now, let’s have a look on how it works in practice. The following example is the one you’ll find in the library’s README file, but with a main function thrown in, so you can compile and run it easily (see the steps below). The program will query GitHub’s API endpoint for a list of commits in a repo, and parse the result (for simplicity, the query is just a system() call of curl). It then reads the file and iterates trough every commit. For each of them, the dot.notation is used to retrieve data stored deeper within the object structure.

#include <stdio.h>
#include <stdlib.h>

#include "parson.h"

void print_commits_info(const char *username, const char *repo) {
    JSON_Value *root_value;
    JSON_Array *commits;
    JSON_Object *commit;
    size_t i;

    char curl_command[512];
    char cleanup_command[256];
    char output_filename[] = "commits.json";

    /* it ain't pretty, but it's not a libcurl tutorial */
    sprintf(curl_command,
        "curl -s \"https://api.github.com/repos/%s/%s/commits\" > %s",
        username, repo, output_filename);
    sprintf(cleanup_command, "rm -f %s", output_filename);
    system(curl_command);

    /* parsing json and validating output */
    root_value = json_parse_file(output_filename);
    if (json_value_get_type(root_value) != JSONArray) {
        system(cleanup_command);
        return;
    }

    /* getting array from root value and printing commit info */
    commits = json_value_get_array(root_value);
    printf("%-10.10s %-10.10s %s\n", "Date", "SHA", "Author");
    for (i = 0; i < json_array_get_count(commits); i++) {
        commit = json_array_get_object(commits, i);
        printf("%.10s %.10s %s\n",
               json_object_dotget_string(commit, "commit.author.date"),
               json_object_get_string(commit, "sha"),
               json_object_dotget_string(commit, "commit.author.name"));
    }

    /* cleanup code */
    json_value_free(root_value);
    system(cleanup_command);
}

int main(int argc, char **argv)
{
    if (argc != 3) {
        fprintf(stderr, "error: Wrong arguments.\n");
        fprintf(stderr, "usage: %s <github-user> <github-repo>\n", argv[0]);
        return 1;
    }

    print_commits_info(argv[1], argv[2]);
    return 0;
}

The json_parse_file() function will process the file and return its root value. It is expected to be an array and we need to check to proceed further json_value_get_type(). A for loop is then used to iterate through the array of commit objects. During each iteration, we use json_array_get_object() to retrieve the i-th object of the array. Because the commit is a fairly complex object (see an example), we will use the json_object_dotget_string() functions to access values in child objects without having to unwrap them first.

Follow these steps to run this example:

  1. Clone parson git clone https://github.com/kgabis/parson
  2. Type cd parson/ to change to the repo’s directory
  3. Download the example: curl -L -s http://bit.ly/ZlkhnO >example.c
  4. Compile it: gcc example.c parson.c -o example
  5. And finally execute it: ./example pazdera tco

Summary

If you need to process JSON files in C, this library is an excellent tool for the job. It was written by Krzysztof Gabis in 2012, and it’s available to everyone under the MIT license. It only supports reading at the moment, which is a problem if you need to create or edit files too. There is, however, an unstable devel branch in making with the support of writing, if you’re one of the more adventurous types :-).

Update

The changes from parson-devel were merged to master on 7th Oct, so the stable version of parson now comes with the support of serialisation, yay! It also adds a new validation feature that you can use to compare the structure of two JSON objects.