Parson
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:
- Clone parson
git clone https://github.com/kgabis/parson
- Type
cd parson/
to change to the repo’s directory - Download the example:
curl -L -s http://bit.ly/ZlkhnO >example.c
- Compile it:
gcc example.c parson.c -o example
- 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 :-).
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.