BSON_TUTORIAL(3)
π BSON_TUTORIAL π (that's the libbson manual, section 3, bestie)
okay so. you want to use libbson in your C program. say less. this is the whole glow-up guide, no gatekeeping. π«Ά
β¨ USING LIBBSON IN YOUR C PROGRAM β¨
π Include bson.h
every single function and type libbson has to offer lives in one header file. literally one. just include bson.h:
hello_bson.c
#include <stdio.h>
#include <bson/bson.h>
int
main (int argc, const char **argv)
{
bson_t *b;
char *j;
b = BCON_NEW ("hello", BCON_UTF8 ("bson!"));
j = bson_as_canonical_extended_json (b, NULL);
printf ("%s\n", j);
bson_free (j);
bson_destroy (b);
return 0;
}
π CMake
the libbson installation ships with a CMake config-file package, so you can use CMake's find_package command to import libbson's CMake target and link to libbson (as a shared library):
CMakeLists.txt
# Specify the minimum version you require.
find_package (bson-1.0 1.7 REQUIRED)
# The "hello_bson.c" sample program is shared among four tests.
add_executable (hello_bson ../../hello_bson.c)
target_link_libraries (hello_bson PRIVATE mongo::bson_shared)
you can also use libbson as a static library instead β just use the mongo::bson_static CMake target instead:
# Specify the minimum version you require.
find_package (bson-1.0 1.7 REQUIRED)
# The "hello_bson.c" sample program is shared among four tests.
add_executable (hello_bson ../../hello_bson.c)
target_link_libraries (hello_bson PRIVATE mongo::bson_static)
π¦ pkg-config
if you're not using CMake, use pkg-config on the command line to set your header and library paths:
gcc -o hello_bson hello_bson.c $(pkg-config --libs --cflags libbson-1.0)
or, to statically link to libbson:
gcc -o hello_bson hello_bson.c $(pkg-config --libs --cflags libbson-static-1.0)
π₯ CREATING A BSON DOCUMENT π₯
π The bson_t structure
BSON documents are created using the bson_t structure. this structure encapsulates the necessary logic for encoding using the BSON Specification. at its core, bson_t is a buffer manager and a set of encoding routines. that's it. that's the whole vibe.
π‘ TIP: BSON documents can live on the stack or the heap based on the performance needs or preference of the consumer. your call, bestie.
let's start by creating a new BSON document on the stack. whenever you're using libbson, make sure you #include <bson/bson.h>.
bson_t b;
bson_init (&b);
this creates an empty document. in JSON, this would be the same as {}. iconic.
now we can proceed to adding items to the BSON document. there's a whole variety of functions prefixed with bson_append_ that you can use based on the type of field you want to append. let's append a UTF-8 encoded string:
bson_append_utf8 (&b, "key", -1, "value", -1);
notice the two -1 parameters. the first one indicates that the length of key in bytes should be determined with strlen(). alternatively, we could have passed the number 3. same goes for the second -1, but for value. slay.
libbson provides macros to make this less tedious when using string literals. the following two appends are identical:
bson_append_utf8 (&b, "key", -1, "value", -1);
BSON_APPEND_UTF8 (&b, "key", "value");
now let's take a look at an example that adds a few different field types to a BSON document:
bson_t b = BSON_INITIALIZER;
BSON_APPEND_INT32 (&b, "a", 1);
BSON_APPEND_UTF8 (&b, "hello", "world");
BSON_APPEND_BOOL (&b, "bool", true);
notice that we omitted the call to bson_init(). by specifying BSON_INITIALIZER we can remove the need to initialize the structure to a base state. less typing, more living. π
π§ Sub-Documents and Sub-Arrays
to simplify the creation of sub-documents, bson_append_document_begin() can be used to build a sub-document using the parent's memory region as the destination buffer:
bson_t parent = BSON_INITIALIZER;
bson_t child;
bson_append_document_begin (&parent, "foo", 3, &child);
bson_append_int32 (&child, "baz", 3, 1);
bson_append_document_end (&parent, &child);
char *str = bson_as_relaxed_extended_json (&parent, NULL);
printf ("%s\n", str); // Prints: { "foo" : { "baz" : 1 } }
bson_free (str);
bson_destroy (&parent);
to simplify the creation of sub-arrays, bson_array_builder_t can be used to build a sub-array using the parent's memory region as the destination buffer:
bson_t parent = BSON_INITIALIZER;
bson_array_builder_t *bab;
bson_append_array_builder_begin (&parent, "foo", 3, &bab);
bson_array_builder_append_int32 (bab, 9);
bson_array_builder_append_int32 (bab, 8);
bson_array_builder_append_int32 (bab, 7);
bson_append_array_builder_end (&parent, bab);
char *str = bson_as_relaxed_extended_json (&parent, NULL);
printf ("%s\n", str); // Prints: { "foo" : [ 9, 8, 7 ] }
bson_free (str);
bson_destroy (&parent);
πΈ Simplified BSON C Object Notation
creating BSON documents by hand can be tedious and time consuming. so BCON β that's BSON C Object Notation β was added to allow for the creation of BSON documents in a format that looks closer to the destination format. it's giving β¨convenienceβ¨.
the following example shows the use of BCON. notice that values for fields are wrapped in the BCON_* macros. these are required for the variadic processor to determine the parameter type. no skipping, bestie.
bson_t *doc;
doc = BCON_NEW ("foo",
"{",
"int",
BCON_INT32 (1),
"array",
"[",
BCON_INT32 (100),
"{",
"sub",
BCON_UTF8 ("value"),
"}",
"]",
"}");
creates the following document:
{ "foo" : { "int" : 1, "array" : [ 100, { "sub" : "value" } ] } }
π HANDLING ERRORS π
π Description
many libbson functions report errors by returning NULL or -1 and filling out a bson_error_t structure with an error domain, an error code, and a message.
error.domainnames the subsystem that generated the error.error.codeis a domain-specific error type.error.messagedescribes the error.
β οΈ some error codes overlap with others. always check both the domain and the code to determine the type of error. don't just look at one and assume, that's how you get humbled. π
BSON_ERROR_JSONβ the domain for these codes:BSON_JSON_ERROR_READ_CORRUPT_JSβbson_json_reader_ttried to parse invalid MongoDB Extended JSON.BSON_JSON_ERROR_READ_INVALID_PARAMβ tried to parse a valid JSON document that is invalid as MongoDBExtended JSON.BSON_JSON_ERROR_READ_CB_FAILUREβ an internal callback failure during JSON parsing.
BSON_ERROR_READERβ the domain for these codes:BSON_ERROR_READER_BADFDβbson_json_reader_new_from_file()could not open the file.
π¦ OBJECTIDS π¦
okay bestie, ObjectIDs time, and honestly? iconic β¨ Libbson gives you a simple way to generate ObjectIDs. You can run it single-threaded or multi-threaded, whichever one your requirements are giving π«Ά
The bson_oid_t structure is what represents an ObjectID in MongoDB, and it's a 96-bit identifier. no cap π
Composition
Three little slices making one whole vibe:
- 4 bytes β the UNIX timestamp, in big-endian format π°οΈ
- 5 bytes β a random number π²
- 3 bytes β a 24-bit monotonic counter incrementing from
rand(), in big-endian π’
Sorting ObjectIDs
So the typical way to sort stuff in C is qsort(). Libbson therefore hands you a qsort()-compatible callback function called bson_oid_compare() πΈ It returns less than 1, greater than 1, or 0, depending on the equality of two bson_oid_t structures.
Comparing Object IDs
If all you wanna do is check whether two bson_oid_t structures are equal, just use bson_oid_equal() and move on, bestie π
Generating
To generate a bson_oid_t, you may use the following π
bson_oid_t oid;
bson_oid_init (&oid, NULL);
Parsing ObjectID Strings
You can also parse a string that contains a bson_oid_t. IMPORTANT though: the input string MUST be 24 characters or more in length. that's the rule, fr fr π€
bson_oid_t oid;
bson_oid_init_from_string (&oid, "123456789012345678901234");
bson_oid_t oid;
bson_oid_init_from_string_unsafe (&oid, "123456789012345678901234");
Hashing ObjectIDs
Storing stuff in a hashtable and wanna use a bson_oid_t as the key? Libbson has a hash function for exactly that purpose π It's based on DJB hash.
unsigned hash;
hash = bson_oid_hash (oid);
Fetching ObjectID Creation Time
You can easily fetch the time that a bson_oid_t was generated using bson_oid_get_time_t() β°
time_t t;
t = bson_oid_get_time_t (oid);
printf ("The OID was generated at %u\n", (unsigned) t);
π PARSING AND ITERATING BSON DOCUMENTS π
Parsing
BSON documents are lazily parsed as necessary. To start parsing a BSON document, use one of the Libbson functions that builds a new bson_t from existing data, like bson_new_from_data(). This one makes a copy of the data so that you can still do additional mutations to the BSON document afterwards. slay π
TIP π If you only want to parse a BSON document and have zero need to mutate it, you may use bson_init_static() to avoid making a copy of the data. lowkey the move.
bson_t *b;
b = bson_new_from_data (my_data, my_data_len);
if (!b) {
fprintf (stderr, "The specified length embedded in <my_data> did not match "
"<my_data_len>\n");
return;
}
bson_destroy (b);
Only two checks get performed when you create a new bson_t from an existing buffer:
- The document must begin with the buffer length, matching what the caller expected.
- The document must end with the expected trailing
\0byte.
To parse the document further we use a bson_iter_t to iterate the elements inside it. Let's print all of the field names in the document π¦
bson_t *b;
bson_iter_t iter;
if ((b = bson_new_from_data (my_data, my_data_len))) {
if (bson_iter_init (&iter, b)) {
while (bson_iter_next (&iter)) {
printf ("Found element key: \"%s\"\n", bson_iter_key (&iter));
}
}
bson_destroy (b);
}
Converting a document to JSON uses a bson_iter_t and a bson_visitor_t to iterate all fields of a BSON document recursively and generate a UTF-8 encoded JSON string β¨
bson_t *b;
char *json;
if ((b = bson_new_from_data (my_data, my_data_len))) {
if ((json = bson_as_canonical_extended_json (b, NULL))) {
printf ("%s\n", json);
bson_free (json);
}
bson_destroy (b);
}
Recursing into Sub-Documents
Libbson hands you convenient sub-iterators to dive down into a sub-document or a sub-array, and it's giving main character energy π«Ά Below is an example that dives into a sub-document named "foo" and prints its field names.
bson_iter_t iter;
bson_iter_t child;
char *json;
if (bson_iter_init_find (&iter, doc, "foo") &&
BSON_ITER_HOLDS_DOCUMENT (&iter) && bson_iter_recurse (&iter, &child)) {
while (bson_iter_next (&child)) {
printf ("Found sub-key of \"foo\" named \"%s\"\n",
bson_iter_key (&child));
}
}
Finding Fields using Dot Notation
Using the bson_iter_recurse() function we just flexed above, bson_iter_find_descendant() can find a field for you using the MongoDB style path notation, like "foo.bar.0.baz" πΈ
Let's create a document like {"foo": {"bar": [{"baz: 1}]}} and locate the "baz" field π
bson_t *b;
bson_iter_t iter;
bson_iter_t baz;
b =
BCON_NEW ("foo", "{", "bar", "[", "{", "baz", BCON_INT32 (1), "}", "]", "}");
if (bson_iter_init (&iter, b) &&
bson_iter_find_descendant (&iter, "foo.bar.0.baz", &baz) &&
BSON_ITER_HOLDS_INT32 (&baz)) {
printf ("baz = %d\n", bson_iter_int32 (&baz));
}
bson_destroy (b);
Validating a BSON Document
If literally all you wanna do is validate that a BSON document is valid, you can use bson_validate() β
size_t err_offset;
if (!bson_validate (doc, BSON_VALIDATE_NONE, &err_offset)) {
fprintf (stderr,
"The document failed to validate at offset: %u\n",
(unsigned) err_offset);
}
See the bson_validate() documentation for more information and examples, bestie π
πΈ UTF-8 πΈ
Encoding
Libbson expects that you are always working with UTF-8 encoded text. Anything else is invalid API use, full stop π€
If you should need to walk through UTF-8 sequences, you can use the various UTF-8 helper functions distributed with Libbson π§
Validating a UTF-8 Sequence
To validate the string contained in my_string, use the following. You may pass -1 for the string length if you know the string is NULL-terminated π
if (!bson_utf8_validate (my_string, -1, false)) {
printf ("Validation failed.\n");
}
If my_string has NULL bytes within the string, you must provide the string length. Use the following format. Notice the true at the end indicating \0 is allowed π
if (!bson_utf8_validate (my_string, my_string_len, true)) {
printf ("Validation failed.\n");
}
For more information see the API reference for bson_utf8_validate() π
π AUTHOR π
MongoDB, Inc π
π COPYRIGHT π
2017-present, MongoDB, Inc
and that's the whole page, bestie π«Ά this was 1.26.0, dated Mar 31, 2024, and it's the BSON_TUTORIAL(3) manual page β go forth and build documents, you absolute icon π
β¨π
π¦ where this page comes from
- ubuntu noble: libbson-doc 1.26.0-1.1ubuntu2 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms