libmongo-client
0.1.6.1
|
Now that we have a basic grasp of the library, we'll write a solution to a real life problem: converting JSON to BSON.
Our program will expect correctly formatted JSON, in condensed one-line format, and will output a BSON document for each line of JSON received.
#define __STRICT_ANSI__ 1 #include <bson.h> #include <json.h> #include <stdio.h> #include <unistd.h> #include <glib.h>
First, we forward declare the json_to_bson() function, because we'll recursively use it later on:
static bson *json_to_bson (struct json_object *json);
Next, we create the heart of the program, a function that takes a BSON object, a value and a key, and appends the key-value pair to the bson object, with the correct type.
static void json_key_to_bson_key (bson *b, void *val, const gchar *key) {
We do this by checking the JSON object's type, and acting up on it:
switch (json_object_get_type (val))
{
The boolean, double, integer and string types are easy: we just use the appropriate bson_append_*() function:
case json_type_boolean: bson_append_boolean (b, key, json_object_get_boolean (val)); break; case json_type_double: bson_append_double (b, key, json_object_get_double (val)); break; case json_type_int: bson_append_int32 (b, key, json_object_get_int (val)); break; case json_type_string: bson_append_string (b, key, json_object_get_string (val), -1); break;
Converting a JSON object to BSON is a bit more complicated, yet, straightforward nevertheless:
case json_type_object: { bson *sub; sub = json_to_bson (val); bson_append_document (b, key, sub); bson_free (sub); break; }
This is one of the reasons we needed to forward-declare json_to_bson(): we're using it to turn the JSON value into BSON, and append it as a subdocument.
Next up: arrays! This is even trickier than sub-documents, as we need to iterate over the elements, and append each individually. But, trickier as it may be, it's still straightforward;
case json_type_array: { gint pos; bson *sub; sub = bson_new (); for (pos = 0; pos < json_object_array_length (val); pos++) { gchar *nk = g_strdup_printf ("%d", pos); json_key_to_bson_key (sub, json_object_array_get_idx (val, pos), nk); g_free (nk); } bson_finish (sub); bson_append_array (b, key, sub); bson_free (sub); break; }
Anything else, we ignore:
default: break;
}
}
And to bind this together with JSON-C's API, we need two more functions. The first one will simply iterate over a JSON object, and call the function we wrote above:
static void json_to_bson_foreach (bson *b, struct json_object *json) { json_object_object_foreach (json, key, val) { json_key_to_bson_key (b, val, key); } }
The next one is another wrapper around this former: it creates a BSON document, calls the foreach method, then finishes the BSON object and we're done:
static bson * json_to_bson (struct json_object *json) { bson *b; b = bson_new (); json_to_bson_foreach (b, json); bson_finish (b); return b; }
We're almost done! All that is left is writing our program's entry point: something that will read the input, turn it into BSON, and write it out:
int main (int argc, char **argv) { GIOChannel *input; GString *json_str; GError *error = NULL; struct json_tokener *tokener;
We do some setting up, creating a new IO channel, and a JSON tokenizer:
input = g_io_channel_unix_new (0); json_str = g_string_new (NULL); tokener = json_tokener_new ();
Then, until we have something to read...
while (g_io_channel_read_line_string (input, json_str, NULL, &error) == G_IO_STATUS_NORMAL) { struct json_object *json; bson *bson;
We reset the tokenizer before parsing another line, then parse the JSON we received:
json_tokener_reset (tokener); json = json_tokener_parse_ex (tokener, json_str->str, json_str->len); if (!json) { fprintf (stderr, "Error parsing json: %s\n", json_str->str); break; }
If we received something other than a JSON object, we can't turn that into BSON, so we write an error to STDERR, and skip this line:
if (json_object_get_type (json) != json_type_object) { fprintf (stderr, "Error: json's top-level object is not object: %s\n", json_str->str); json_object_put (json); break; }
Otherwise, we turn it into BSON, and write it to STDOUT:
bson = json_to_bson (json); json_object_put (json); write (1, bson_data (bson), bson_size (bson)); bson_free (bson);
}
And that was our program, a very simple application that turns each line of JSON into BSON.
return 0;
}