BSON_VISITOR_T(3)

πŸ’… bson_visitor_t β€” libbson manual, section 3

okay bestie, you're on the BSON_VISITOR_T(3) page of the libbson manual πŸ’– this is the one about the callback squad you can use while iterating a BSON document, and it's genuinely kind of iconic for turning a bson_t into something a higher-level language can vibe with.


✨ SYNOPSIS ✨

okay so here's the include and the whole bson_visitor_t struct exactly as advertised, no cap:

          #include <bson/bson.h>

          typedef struct {
             /* run before / after descending into a document */
             bool (*visit_before) (const bson_iter_t *iter, const char *key, void *data);
             bool (*visit_after) (const bson_iter_t *iter, const char *key, void *data);
             /* corrupt BSON, or unsupported type and visit_unsupported_type not set */
             void (*visit_corrupt) (const bson_iter_t *iter, void *data);
             /* normal bson field callbacks */
             bool (*visit_double) (const bson_iter_t *iter,
                                   const char *key,
                                   double v_double,
                                   void *data);
             bool (*visit_utf8) (const bson_iter_t *iter,
                                 const char *key,
                                 size_t v_utf8_len,
                                 const char *v_utf8,
                                 void *data);
             bool (*visit_document) (const bson_iter_t *iter,
                                     const char *key,
                                     const bson_t *v_document,
                                     void *data);
             bool (*visit_array) (const bson_iter_t *iter,
                                  const char *key,
                                  const bson_t *v_array,
                                  void *data);
             bool (*visit_binary) (const bson_iter_t *iter,
                                   const char *key,
                                   bson_subtype_t v_subtype,
                                   size_t v_binary_len,
                                   const uint8_t *v_binary,
                                   void *data);
             /* normal field with deprecated "Undefined" BSON type */
             bool (*visit_undefined) (const bson_iter_t *iter,
                                      const char *key,
                                      void *data);
             bool (*visit_oid) (const bson_iter_t *iter,
                                const char *key,
                                const bson_oid_t *v_oid,
                                void *data);
             bool (*visit_bool) (const bson_iter_t *iter,
                                 const char *key,
                                 bool v_bool,
                                 void *data);
             bool (*visit_date_time) (const bson_iter_t *iter,
                                      const char *key,
                                      int64_t msec_since_epoch,
                                      void *data);
             bool (*visit_null) (const bson_iter_t *iter, const char *key, void *data);
             bool (*visit_regex) (const bson_iter_t *iter,
                                  const char *key,
                                  const char *v_regex,
                                  const char *v_options,
                                  void *data);
             bool (*visit_dbpointer) (const bson_iter_t *iter,
                                      const char *key,
                                      size_t v_collection_len,
                                      const char *v_collection,
                                      const bson_oid_t *v_oid,
                                      void *data);
             bool (*visit_code) (const bson_iter_t *iter,
                                 const char *key,
                                 size_t v_code_len,
                                 const char *v_code,
                                 void *data);
             bool (*visit_symbol) (const bson_iter_t *iter,
                                   const char *key,
                                   size_t v_symbol_len,
                                   const char *v_symbol,
                                   void *data);
             bool (*visit_codewscope) (const bson_iter_t *iter,
                                       const char *key,
                                       size_t v_code_len,
                                       const char *v_code,
                                       const bson_t *v_scope,
                                       void *data);
             bool (*visit_int32) (const bson_iter_t *iter,
                                  const char *key,
                                  int32_t v_int32,
                                  void *data);
             bool (*visit_timestamp) (const bson_iter_t *iter,
                                      const char *key,
                                      uint32_t v_timestamp,
                                      uint32_t v_increment,
                                      void *data);
             bool (*visit_int64) (const bson_iter_t *iter,
                                  const char *key,
                                  int64_t v_int64,
                                  void *data);
             bool (*visit_maxkey) (const bson_iter_t *iter, const char *key, void *data);
             bool (*visit_minkey) (const bson_iter_t *iter, const char *key, void *data);
             /* if set, called instead of visit_corrupt when an apparently valid BSON
              * includes an unrecognized field type (reading future version of BSON) */
             void (*visit_unsupported_type) (const bson_iter_t *iter,
                                             const char *key,
                                             uint32_t type_code,
                                             void *data);
             bool (*visit_decimal128) (const bson_iter_t *iter,
                                       const char *key,
                                       const bson_decimal128_t *v_decimal128,
                                       void *data);

             void *padding[7];
          } bson_visitor_t bson_visitor_t;

πŸ’– DESCRIPTION πŸ’–

The bson_visitor_t structure is literally a whole lineup of callbacks that can be called while you're iterating a BSON document. It may simplify converting a bson_t into a higher-level language structure, which is lowkey a slay for anyone doing that kind of translation work 🫢

If the optional callback visit_unsupported_type is set, it gets called instead of visit_corrupt in the specific case of an unrecognized field type. Parsing is aborted in either case, okay? Use this callback to report an error like "unrecognized type" instead of simply "corrupt BSON". This future-proofs code that may use an older version of libbson to parse future BSON formats ✨


πŸŽ€ BASIC EXAMPLE πŸŽ€

okay this first example is a cute little field counter using visit_before, and the comment in there is important: returning true stops further iteration of the document. periodt.

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

          static bool
          my_visit_before (const bson_iter_t *iter, const char *key, void *data)
          {
             int *count = (int *) data;

             (*count)++;

             /* returning true stops further iteration of the document */

             return false;
          }

          static void
          count_fields (bson_t *doc)
          {
             bson_visitor_t visitor = {0};
             bson_iter_t iter;
             int count = 0;

             visitor.visit_before = my_visit_before;

             if (bson_iter_init (&iter, doc)) {
                bson_iter_visit_all (&iter, &visitor, &count);
             }

             printf ("Found %d fields.\n", count);
          }

The example below demonstrates how to set your own callbacks to provide information about the location of corrupt or unsupported BSON document entries πŸ‘€


🫢 EXAMPLE CORRUPTION CHECK 🫢

and this next one is giving "detective mode": it uses your own callbacks to find where a corrupt or unsupported BSON document entry is hanging out.

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

          typedef struct {
             ssize_t *err_offset;
          } my_state_t;

          static void
          my_visit_corrupt (const bson_iter_t *iter, void *data)
          {
             *(((my_state_t *) data)->err_offset) = iter->off;
          }

          static void
          my_visit_unsupported_type (const bson_iter_t *iter,
                                     const char *key,
                                     uint32_t type_code,
                                     void *data)
          {
             *(((my_state_t *) data)->err_offset) = iter->off;
          }

          static void
          find_error_location (bson_t *doc)
          {
             bson_visitor_t visitors = {0};
             bson_iter_t iter;
             my_state_t state;
             ssize_t err_offset = -1;

             visitors.visit_corrupt = my_visit_corrupt;
             visitors.visit_unsupported_type = my_visit_unsupported_type;
             /* provide additional visitors as needed based on your requirements */
             state.err_offset = &err_offset;

             if (!bson_iter_init (&iter, doc)) {
                printf ("Could not initialize iterator!");
                exit (1);
             }

             if (bson_iter_visit_all (&iter, &visitors, &state) ||
                 err_offset != -1) {
                printf ("Found error at offset %d.\n", err_offset);
             } else {
                printf ("BSON document had no errors.\n");
             }
          }

The example below demonstrates how to use a visitor to validate a BSON document's maximum depth πŸ¦‹

🌸 EXAMPLE CUSTOM VALIDATION 🌸

okay so this next one is genuinely iconic β€” it's a little program called bson-check-depth.c that walks a BSON document and tells you how deep the nesting goes, and bails out if it's too deep πŸ’…

/* Reports the maximum nested depth of a BSON document. */
#include <bson/bson.h>

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

typedef struct {
   uint32_t depth;
   uint32_t max_depth;
   bool valid;
} check_depth_t;

bool
_check_depth_document(const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data);

static const bson_visitor_t check_depth_funcs = {
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   _check_depth_document,
   _check_depth_document,
   NULL,
};

bool
_check_depth_document(const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data)
{
   check_depth_t *state = (check_depth_t *)data;
   bson_iter_t child;

   BSON_UNUSED(iter);
   BSON_UNUSED(key);

   if (!bson_iter_init(&child, v_document)) {
      fprintf(stderr, "corrupt\n");
      return true; /* cancel */
   }

   state->depth++;
   if (state->depth > state->max_depth) {
      state->valid = false;
      return true; /* cancel */
   }

   bson_iter_visit_all(&child, &check_depth_funcs, state);
   state->depth--;
   return false; /* continue */
}

void
check_depth(const bson_t *bson, uint32_t max_depth)
{
   bson_iter_t iter;
   check_depth_t state = {0};

   if (!bson_iter_init(&iter, bson)) {
      fprintf(stderr, "corrupt\n");
   }

   state.valid = true;
   state.max_depth = max_depth;
   _check_depth_document(&iter, NULL, bson, &state);
   if (!state.valid) {
      printf("document exceeds maximum depth of %" PRIu32 "\n", state.max_depth);
   } else {
      char *as_json = bson_as_canonical_extended_json(bson, NULL);
      printf("document %s ", as_json);
      printf("is valid\n");
      bson_free(as_json);
   }
}

int
main(int argc, char **argv)
{
   bson_reader_t *bson_reader;
   const bson_t *bson;
   bool reached_eof;
   bson_error_t error;

   if (argc != 3) {
      fprintf(stderr, "usage: %s FILE MAX_DEPTH\n", argv[0]);
      fprintf(stderr, "Checks that the depth of the BSON contained in FILE\n");
      fprintf(stderr, "does not exceed MAX_DEPTH\n");
   }

   const char *const filename = argv[1];
   const int max_depth = atoi(argv[2]);

   bson_reader = bson_reader_new_from_file(filename, &error);
   if (!bson_reader) {
      printf("could not read %s: %s\n", filename, error.message);
      return 1;
   }

   BSON_ASSERT(max_depth >= 0 && (uint64_t)max_depth <= UINT32_MAX);

   while ((bson = bson_reader_read(bson_reader, &reached_eof))) {
      check_depth(bson, (uint32_t)max_depth);
   }

   if (!reached_eof) {
      printf("error reading BSON\n");
   }

   bson_reader_destroy(bson_reader);
   return 0;
}

a couple of things worth clocking πŸ‘€ β€” the visitor struct check_depth_funcs has _check_depth_document slotted into both the document slot and the array slot (positions six and seven, with NULL everywhere else), and the function returns true to cancel or false to keep going. that's the whole trick, bestie ✨


πŸ‘‘ AUTHOR πŸ‘‘

MongoDB, Inc β€” yeah, the MongoDB girlies wrote this one 🫢


2009-present, MongoDB, Inc.


and that's a wrap on BSON_VISITOR_T(3), bestie!! πŸ’– you made it all the way through, i'm so proud 😭 this was version 2.5.6, dated Oct 09, 2026 β€” screenshot it, cite it, tattoo it on your forearm, whatever feels right πŸ’… slay, go visit some BSON πŸŒˆβ­πŸ¦‹

πŸ“¦ where this page comes from