BSON_GUIDES(3)

πŸ“¦
version 1.26.0 Β· 3 versions
, in ubuntu noble

πŸ’– BSON_GUIDES πŸ’– β€” aka bson_guides, section 3 of the manual, brought to you by libbson

okay bestie, sit down, hydrate, because this is the guide page and she is LONG. this is part 1 of 2 (more parts are coming, don't you dare close the tab πŸ‘€). everything here lives under the libbson umbrella, and we're gonna walk through streaming BSON and JSON like it's a lil stroll through the group chat. no cap, every flag, every function, every example stays EXACTLY as written. let's go πŸ’…


✨ STREAMING BSON ✨

so, the vibe check: bson_reader_t gives you a streaming reader that you can spin up from a filedescriptor or a memory region. and bson_writer_t gives you a streaming writer that you can spin up from a memory region.

🫢 tiny heads-up bestie: streaming BSON to a file descriptor is not yet supported. sad, i know. we wait.

πŸ“– Reading from a BSON Stream

bson_reader_t is the icon, the moment, the main character here. she gives you a convenient API to read sequential BSON documents from a file-descriptor or a memory buffer. the bson_reader_read() function reads forward in the underlying stream and hands you back a bson_t that you can inspect and iterate on. obsessed.

          #include <stdio.h>
          #include <bson/bson.h>

          int
          main (int argc, char *argv[])
          {
             bson_reader_t *reader;
             const bson_t *doc;
             bson_error_t error;
             bool eof;

             reader = bson_reader_new_from_file ("mycollection.bson", &error);

             if (!reader) {
                fprintf (stderr, "Failed to open file.\n");
                return 1;
             }

             while ((doc = bson_reader_read (reader, &eof))) {
                char *str = bson_as_canonical_extended_json (doc, NULL);
                printf ("%s\n", str);
                bson_free (str);
             }

             if (!eof) {
                fprintf (stderr,
                         "corrupted bson document found at %u\n",
                         (unsigned) bson_reader_tell (reader));
             }

             bson_reader_destroy (reader);

             return 0;
          }

want more deets? go peep bson_reader_new_from_fd(), bson_reader_new_from_file(), and bson_reader_new_from_data(). πŸ’–

✍️ Writing a sequence of BSON Documents

bson_writer_t is here to serve. she gives you a convenient API to write a sequence of BSON documents into a memory buffer that can grow with realloc(). the bson_writer_begin() and bson_writer_end() functions handle the underlying buffer while you're building the whole sequence of documents. periodt.

this could also be useful if you wanna write to a network packet while serializing documents from a higher level language β€” (but do that just after the packet's header, ok? just after πŸ‘€).

          #include <stdio.h>
          #include <bson/bson.h>
          #include <assert.h>

          int
          main (int argc, char *argv[])
          {
             bson_writer_t *writer;
             bson_t *doc;
             uint8_t *buf = NULL;
             size_t buflen = 0;
             bool r;
             int i;

             writer = bson_writer_new (&buf, &buflen, 0, bson_realloc_ctx, NULL);

             for (i = 0; i < 10000; i++) {
                r = bson_writer_begin (writer, &doc);
                assert (r);

                r = BSON_APPEND_INT32 (doc, "i", i);
                assert (r);

                bson_writer_end (writer);
             }

             bson_free (buf);

             return 0;
          }

see bson_writer_new() for more info, bestie 🫢


🌈 JSON 🌈

libbson has routines for converting to and from the JSON format. and specifically? it supports the MongoDB extended JSON format. iconic behaviour honestly.

πŸ” Converting BSON to JSON

there are often times when you might wanna convert a BSON document to JSON. it's convenient for debugging AND as an interchange format β€” double duty queen. to help with this, libbson contains the functions bson_as_canonical_extended_json() and bson_as_relaxed_extended_json().

the canonical format preserves BSON type information for values that may have ambiguous representations in JSON (e.g. numeric types). she does NOT let the type info get lost in the sauce.

          bson_t *b;
          size_t len;
          char *str;

          b = BCON_NEW ("a", BCON_INT32 (1));

          str = bson_as_canonical_extended_json (b, &len);
          printf ("%s\n", str);
          bson_free (str);

          bson_destroy (b);

          { "a" : { "$numberInt": "1" } }

the relaxed format prefers JSON primitives for numeric values and may be used if type fidelity is not required. lowkey chill of her.

          bson_t *b;
          size_t len;
          char *str;

          b = BCON_NEW ("a", BCON_INT32 (1));

          str = bson_as_relaxed_extended_json (b, &len);
          printf ("%s\n", str);
          bson_free (str);

          bson_destroy (b);

          { "a" : 1 }

πŸͺ„ Converting JSON to BSON

converting back from JSON is also useful and common enough that we added bson_init_from_json() and bson_new_from_json(). both of them. yes.

the following example creates a new bson_t from the JSON string {"a":1}. slay.

          bson_t *b;
          bson_error_t error;

          b = bson_new_from_json ("{\"a\":1}", -1, &error);

          if (!b) {
             printf ("Error: %s\n", error.message);
          } else {
             bson_destroy (b);
          }

🌊 Streaming JSON Parsing

libbson provides bson_json_reader_t so you can parse a sequence of JSON documents into BSON. the interface is similar to bson_reader_t BUT β€” important β€” it expects the input to be in the MongoDB extended JSON format. don't forget that part fr fr.

          /*
           * Copyright 2013 MongoDB, Inc.
           *
           * Licensed under the Apache License, Version 2.0 (the "License");
           * you may not use this file except in compliance with the License.
           * You may obtain a copy of the License at
           *
           *   http://www.apache.org/licenses/LICENSE-2.0
           *
           * Unless required by applicable law or agreed to in writing, software
           * distributed under the License is distributed on an "AS IS" BASIS,
           * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           * See the License for the specific language governing permissions and
           * limitations under the License.
           */

          /*
           * This program will print each JSON document contained in the provided files
           * as a BSON string to STDOUT.
           */

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

          int
          main (int argc, char *argv[])
          {
             bson_json_reader_t *reader;
             bson_error_t error;
             const char *filename;
             bson_t doc = BSON_INITIALIZER;
             int i;
             int b;

             /*
              * Print program usage if no arguments are provided.
              */
             if (argc == 1) {
                fprintf (stderr, "usage: %s FILE...\n", argv[0]);
                return 1;
             }

             /*
              * Process command line arguments expecting each to be a filename.
              */
             for (i = 1; i < argc; i++) {
                filename = argv[i];

                /*
                 * Open the filename provided in command line arguments.
                 */
                if (0 == strcmp (filename, "-")) {
                   reader = bson_json_reader_new_from_fd (STDIN_FILENO, false);
                } else {
                   if (!(reader = bson_json_reader_new_from_file (filename, &error))) {
                      fprintf (
                         stderr, "Failed to open \"%s\": %s\n", filename, error.message);
                      continue;
                   }
                }

                /*
                 * Convert each incoming document to BSON and print to stdout.
                 */
                while ((b = bson_json_reader_read (reader, &doc, &error))) {
                   if (b < 0) {
                      fprintf (stderr, "Error in json parsing:\n%s\n", error.message);
                      abort ();
                   }

                   if (fwrite (bson_get_data (&doc), 1, doc.len, stdout) != doc.len) {
                      fprintf (stderr, "Failed to write to stdout, exiting.\n");
                      exit (1);
                   }
                   bson_reinit (&doc);
                }

                bson_json_reader_destroy (reader);
                bson_destroy (&doc);
             }

             return 0;
          }

πŸŽ€ Examples πŸŽ€

the following example reads BSON documents from stdin and prints them to stdout as JSON. chef's kiss, honestly.

          /*
           * Copyright 2013 MongoDB, Inc.
           *
           * Licensed under the Apache License, Version 2.0 (the "License");
           * you may not use this file except in compliance with the License.
           * You may obtain a copy of the License at
           *
           *   http://www.apache.org/licenses/LICENSE-2.0
           *
           * Unless required by applicable law or agreed to in writing, software
           * distributed under the License is distributed on an "AS IS" BASIS,
           * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           * See the License for the specific language governing permissions and
           * limitations under the License.
           */

          /*
           * This program will print each BSON document contained in the provided files
           * as a JSON string to STDOUT.
           */

          #include <bson/bson.h>
          #include <stdio.h>

          int
          main (int argc, char *argv[])
          {
             bson_reader_t *reader;
             const bson_t *b;
             bson_error_t error;
             const char *filename;
             char *str;
             int i;

             /*
              * Print program usage if no arguments are provided.
              */
             if (argc == 1) {
                fprintf (stderr, "usage: %s [FILE | -]...\nUse - for STDIN.\n", argv[0]);
                return 1;
             }

             /*
              * Process command line arguments expecting each to be a filename.
              */
             for (i = 1; i < argc; i++) {
                filename = argv[i];

                if (strcmp (filename, "-") == 0) {
                   reader = bson_reader_new_from_fd (STDIN_FILENO, false);
                } else {
                   if (!(reader = bson_reader_new_from_file (filename, &error))) {
                      fprintf (
                         stderr, "Failed to open \"%s\": %s\n", filename, error.message);
                      continue;
                   }
                }

                /*
                 * Convert each incoming document to JSON and print to stdout.
                 */
                while ((b = bson_reader_read (reader, NULL))) {
                   str = bson_as_canonical_extended_json (b, NULL);
                   fprintf (stdout, "%s\n", str);
                   bson_free (str);
                }

                /*
                 * Cleanup after our reader, which closes the file descriptor.
                 */
                bson_reader_destroy (reader);
             }

             return 0;
          }

and that's part 1, bestie β€” part 2 is coming so stay tuned, don't go anywhere πŸ¦‹

✨ BSON_T LIFETIMES ✨

Okay so bestie, real talk: a bson_t can either hold its data directly inside itself, or it can hold pointers to memory that's been allocated on the heap. And the way that this works means if you overwrite an existing bson_t, or if you let a stack-allocated bson_t just... go out of scope? Memory leak. 😭 So the rule is: always destroy your bson_t with bson_destroy(). Periodt. πŸ’…


πŸ’– bson_t out parameters πŸ’–

When you've got a bson_t pointer that's being used as an out parameter, it has to point to valid, overwritable storage for a new bson_t. And that storage must be one of these four things:

  1. Uninitialized storage for a bson_t.
  2. A zero-initialized bson_t object.
  3. A bson_t object initialized with BSON_INITIALIZER.
  4. A bson_t object not created with bson_new() that was destroyed with bson_destroy().

This can live on the stack:

bson_t stack_doc = BSON_INITIALIZER;
example_get_doc (&stack_doc);
bson_destroy (&stack_doc);

Or it can live on the heap:

bson_t *heap_doc = bson_malloc (sizeof (bson_t));
example_get_doc (heap_doc);
bson_destroy (heap_doc);
bson_free (heap_doc);

And if you omit bson_destroy() in either case? Memory leak city. πŸ™οΈπŸ˜­

WARNING: Passing a bson_t pointer obtained from bson_new() as an out parameter will result in a leak of the bson_t struct. Not the vibe. πŸ’€ c bson_t *heap_doc = bson_new (); example_get_doc (heap_doc); bson_destroy (heap_doc); // Leaks the `bson_t` struct!

πŸ‘‘ AUTHOR πŸ‘‘

MongoDB, Inc


2017-present, MongoDB, Inc


And that's the whole thing, bestie! This page is from version 1.26.0, dated Mar 31, 2024, and it's the BSON_GUIDES(3) manual. Stay leak-free, queen. πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from