Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BSON-as-JSON indented output #985

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libbson/src/bson/bson-json-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
struct _bson_json_opts_t {
bson_json_mode_t mode;
int32_t max_len;
const char *initial_indent;
const char *subsequent_indent;
const char *level_indent;
};


Expand Down
3 changes: 1 addition & 2 deletions src/libbson/src/bson/bson-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,12 @@ _noop (void)
}



bson_json_opts_t *
bson_json_opts_new (bson_json_mode_t mode, int32_t max_len)
{
bson_json_opts_t *opts;

opts = (bson_json_opts_t *) bson_malloc (sizeof *opts);
opts = (bson_json_opts_t *) bson_malloc0 (sizeof *opts);
opts->mode = mode;
opts->max_len = max_len;

Expand Down
122 changes: 80 additions & 42 deletions src/libbson/src/bson/bson.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ typedef struct {
ssize_t *err_offset;
uint32_t depth;
bson_string_t *str;
bson_json_mode_t mode;
int32_t max_len;
bool max_len_reached;
const bson_json_opts_t *opts;
} bson_json_state_t;


Expand All @@ -84,8 +84,7 @@ _bson_as_json_visit_document (const bson_iter_t *iter,
static char *
_bson_as_json_visit_all (const bson_t *bson,
size_t *length,
bson_json_mode_t mode,
int32_t max_len);
const bson_json_opts_t *opts);

/*
* Globals.
Expand Down Expand Up @@ -2510,6 +2509,25 @@ bson_equal (const bson_t *bson, const bson_t *other)
}


static void
_bson_json_newline_indent (const bson_json_state_t *state)
{
if (!state->opts->level_indent) {
return;
}
bson_string_append (state->str, "\n");
if (state->opts->subsequent_indent) {
bson_string_append (state->str, state->opts->subsequent_indent);
}
if (state->opts->level_indent) {
int i = 0;
for (i = 0; i < state->depth + 1; ++i) {
bson_string_append (state->str, state->opts->level_indent);
}
}
}


static bool
_bson_as_json_visit_utf8 (const bson_iter_t *iter,
const char *key,
Expand Down Expand Up @@ -2542,7 +2560,7 @@ _bson_as_json_visit_int32 (const bson_iter_t *iter,
{
bson_json_state_t *state = data;

if (state->mode == BSON_JSON_MODE_CANONICAL) {
if (state->opts->mode == BSON_JSON_MODE_CANONICAL) {
bson_string_append_printf (
state->str, "{ \"$numberInt\" : \"%" PRId32 "\" }", v_int32);
} else {
Expand All @@ -2561,7 +2579,7 @@ _bson_as_json_visit_int64 (const bson_iter_t *iter,
{
bson_json_state_t *state = data;

if (state->mode == BSON_JSON_MODE_CANONICAL) {
if (state->opts->mode == BSON_JSON_MODE_CANONICAL) {
bson_string_append_printf (
state->str, "{ \"$numberLong\" : \"%" PRId64 "\" }", v_int64);
} else {
Expand Down Expand Up @@ -2604,8 +2622,8 @@ _bson_as_json_visit_double (const bson_iter_t *iter,
/* Determine if legacy (i.e. unwrapped) output should be used. Relaxed mode
* will use this for nan and inf values, which we check manually since old
* platforms may not have isinf or isnan. */
legacy = state->mode == BSON_JSON_MODE_LEGACY ||
(state->mode == BSON_JSON_MODE_RELAXED &&
legacy = state->opts->mode == BSON_JSON_MODE_LEGACY ||
(state->opts->mode == BSON_JSON_MODE_RELAXED &&
!(v_double != v_double || v_double * 0 != 0));

if (!legacy) {
Expand Down Expand Up @@ -2697,8 +2715,8 @@ _bson_as_json_visit_binary (const bson_iter_t *iter,
b64 = bson_malloc0 (b64_len);
BSON_ASSERT (mcommon_b64_ntop (v_binary, v_binary_len, b64, b64_len) != -1);

if (state->mode == BSON_JSON_MODE_CANONICAL ||
state->mode == BSON_JSON_MODE_RELAXED) {
if (state->opts->mode == BSON_JSON_MODE_CANONICAL ||
state->opts->mode == BSON_JSON_MODE_RELAXED) {
bson_string_append (state->str, "{ \"$binary\" : { \"base64\" : \"");
bson_string_append (state->str, b64);
bson_string_append (state->str, "\", \"subType\" : \"");
Expand Down Expand Up @@ -2740,12 +2758,12 @@ _bson_as_json_visit_date_time (const bson_iter_t *iter,
{
bson_json_state_t *state = data;

if (state->mode == BSON_JSON_MODE_CANONICAL ||
(state->mode == BSON_JSON_MODE_RELAXED && msec_since_epoch < 0)) {
if (state->opts->mode == BSON_JSON_MODE_CANONICAL ||
(state->opts->mode == BSON_JSON_MODE_RELAXED && msec_since_epoch < 0)) {
bson_string_append (state->str, "{ \"$date\" : { \"$numberLong\" : \"");
bson_string_append_printf (state->str, "%" PRId64, msec_since_epoch);
bson_string_append (state->str, "\" } }");
} else if (state->mode == BSON_JSON_MODE_RELAXED) {
} else if (state->opts->mode == BSON_JSON_MODE_RELAXED) {
bson_string_append (state->str, "{ \"$date\" : \"");
_bson_iso8601_date_format (msec_since_epoch, state->str);
bson_string_append (state->str, "\" }");
Expand Down Expand Up @@ -2774,8 +2792,8 @@ _bson_as_json_visit_regex (const bson_iter_t *iter,
return true;
}

if (state->mode == BSON_JSON_MODE_CANONICAL ||
state->mode == BSON_JSON_MODE_RELAXED) {
if (state->opts->mode == BSON_JSON_MODE_CANONICAL ||
state->opts->mode == BSON_JSON_MODE_RELAXED) {
bson_string_append (state->str,
"{ \"$regularExpression\" : { \"pattern\" : \"");
bson_string_append (state->str, escaped);
Expand Down Expand Up @@ -2832,8 +2850,8 @@ _bson_as_json_visit_dbpointer (const bson_iter_t *iter,
return true;
}

if (state->mode == BSON_JSON_MODE_CANONICAL ||
state->mode == BSON_JSON_MODE_RELAXED) {
if (state->opts->mode == BSON_JSON_MODE_CANONICAL ||
state->opts->mode == BSON_JSON_MODE_RELAXED) {
bson_string_append (state->str, "{ \"$dbPointer\" : { \"$ref\" : \"");
bson_string_append (state->str, escaped);
bson_string_append (state->str, "\"");
Expand Down Expand Up @@ -2909,6 +2927,8 @@ _bson_as_json_visit_before (const bson_iter_t *iter,
bson_string_append (state->str, ", ");
}

_bson_json_newline_indent (state);

if (state->keys) {
escaped = bson_utf8_escape_for_json (key, -1);
if (escaped) {
Expand Down Expand Up @@ -2947,6 +2967,8 @@ _bson_as_json_visit_after (const bson_iter_t *iter, const char *key, void *data)
return true;
}

_bson_json_newline_indent (state);

return false;
}

Expand Down Expand Up @@ -2997,8 +3019,8 @@ _bson_as_json_visit_symbol (const bson_iter_t *iter,
return true;
}

if (state->mode == BSON_JSON_MODE_CANONICAL ||
state->mode == BSON_JSON_MODE_RELAXED) {
if (state->opts->mode == BSON_JSON_MODE_CANONICAL ||
state->opts->mode == BSON_JSON_MODE_RELAXED) {
bson_string_append (state->str, "{ \"$symbol\" : \"");
bson_string_append (state->str, escaped);
bson_string_append (state->str, "\" }");
Expand All @@ -3025,7 +3047,6 @@ _bson_as_json_visit_codewscope (const bson_iter_t *iter,
bson_json_state_t *state = data;
char *code_escaped;
char *scope;
int32_t max_scope_len = BSON_MAX_LEN_UNLIMITED;

code_escaped = bson_utf8_escape_for_json (v_code, v_code_len);
if (!code_escaped) {
Expand All @@ -3038,12 +3059,7 @@ _bson_as_json_visit_codewscope (const bson_iter_t *iter,

bson_free (code_escaped);

/* Encode scope with the same mode */
if (state->max_len != BSON_MAX_LEN_UNLIMITED) {
max_scope_len = BSON_MAX (0, state->max_len - state->str->len);
}

scope = _bson_as_json_visit_all (v_scope, NULL, state->mode, max_scope_len);
scope = _bson_as_json_visit_all (v_scope, NULL, state->opts);

if (!scope) {
return true;
Expand Down Expand Up @@ -3090,10 +3106,12 @@ _bson_as_json_visit_document (const bson_iter_t *iter,
return false;
}

if (bson_iter_init (&child, v_document)) {
if (bson_empty (v_document)) {
child_state.str = bson_string_new ("{}");
} else if (bson_iter_init (&child, v_document)) {
child_state.str = bson_string_new ("{ ");
child_state.depth = state->depth + 1;
child_state.mode = state->mode;
child_state.opts = state->opts;
child_state.max_len = BSON_MAX_LEN_UNLIMITED;
if (state->max_len != BSON_MAX_LEN_UNLIMITED) {
child_state.max_len = BSON_MAX (0, state->max_len - state->str->len);
Expand All @@ -3114,7 +3132,12 @@ _bson_as_json_visit_document (const bson_iter_t *iter,
return !child_state.max_len_reached;
}

bson_string_append (child_state.str, " }");
child_state.depth -= 1;
_bson_json_newline_indent (&child_state);
bson_string_append (child_state.str,
state->opts->level_indent ? "}" : " }");
}
if (child_state.str) {
bson_string_append (state->str, child_state.str->str);
bson_string_free (child_state.str, true);
}
Expand All @@ -3141,7 +3164,7 @@ _bson_as_json_visit_array (const bson_iter_t *iter,
if (bson_iter_init (&child, v_array)) {
child_state.str = bson_string_new ("[ ");
child_state.depth = state->depth + 1;
child_state.mode = state->mode;
child_state.opts = state->opts;
child_state.max_len = BSON_MAX_LEN_UNLIMITED;
if (state->max_len != BSON_MAX_LEN_UNLIMITED) {
child_state.max_len = BSON_MAX (0, state->max_len - state->str->len);
Expand All @@ -3162,7 +3185,10 @@ _bson_as_json_visit_array (const bson_iter_t *iter,
return !child_state.max_len_reached;
}

bson_string_append (child_state.str, " ]");
child_state.depth -= 1;
_bson_json_newline_indent (&child_state);
bson_string_append (child_state.str,
state->opts->level_indent ? "]" : " ]");
bson_string_append (state->str, child_state.str->str);
bson_string_free (child_state.str, true);
}
Expand All @@ -3174,8 +3200,7 @@ _bson_as_json_visit_array (const bson_iter_t *iter,
static char *
_bson_as_json_visit_all (const bson_t *bson,
size_t *length,
bson_json_mode_t mode,
int32_t max_len)
const bson_json_opts_t *opts)
{
bson_json_state_t state;
bson_iter_t iter;
Expand All @@ -3202,11 +3227,16 @@ _bson_as_json_visit_all (const bson_t *bson,

state.count = 0;
state.keys = true;
state.str = bson_string_new ("{ ");
state.opts = opts;
if (state.opts->initial_indent) {
state.str = bson_string_new (state.opts->initial_indent);
bson_string_append_c (state.str, '{');
} else {
state.str = bson_string_new ("{ ");
}
state.depth = 0;
state.err_offset = &err_offset;
state.mode = mode;
state.max_len = max_len;
state.max_len = opts->max_len;
state.max_len_reached = false;

if ((bson_iter_visit_all (&iter, &bson_as_json_visitors, &state) ||
Expand All @@ -3222,11 +3252,16 @@ _bson_as_json_visit_all (const bson_t *bson,
return NULL;
}

/* Append closing space and } separately, in case we hit the max in between. */
/* Append closing space and } separately, in case we hit the max in between.
*/
remaining = state.max_len - state.str->len;
if (state.max_len == BSON_MAX_LEN_UNLIMITED ||
remaining > 1) {
bson_string_append (state.str, " }");
if (state.max_len == BSON_MAX_LEN_UNLIMITED || remaining > 1) {
if (state.opts->level_indent && state.opts->subsequent_indent) {
bson_string_append_printf (
state.str, "\n%s}", state.opts->subsequent_indent);
} else {
bson_string_append (state.str, " }");
}
} else if (remaining == 1) {
bson_string_append (state.str, " ");
}
Expand All @@ -3244,7 +3279,7 @@ bson_as_json_with_opts (const bson_t *bson,
size_t *length,
const bson_json_opts_t *opts)
{
return _bson_as_json_visit_all (bson, length, opts->mode, opts->max_len);
return _bson_as_json_visit_all (bson, length, opts);
}


Expand Down Expand Up @@ -3286,7 +3321,7 @@ bson_array_as_json (const bson_t *bson, size_t *length)

if (length) {
*length = 0;
}
}

if (bson_empty0 (bson)) {
if (length) {
Expand All @@ -3305,9 +3340,12 @@ bson_array_as_json (const bson_t *bson, size_t *length)
state.str = bson_string_new ("[ ");
state.depth = 0;
state.err_offset = &err_offset;
state.mode = BSON_JSON_MODE_LEGACY;
state.max_len = BSON_MAX_LEN_UNLIMITED;
state.max_len_reached = false;
bson_json_opts_t opts = {0};
opts.mode = BSON_JSON_MODE_LEGACY;
opts.max_len = BSON_MAX_LEN_UNLIMITED;
state.opts = &opts;

if ((bson_iter_visit_all (&iter, &bson_as_json_visitors, &state) ||
err_offset != -1) &&
Expand Down
36 changes: 24 additions & 12 deletions src/libmongoc/tests/json-test-monitoring.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "mongoc/mongoc-topology-private.h"
#include "mongoc/mongoc-util-private.h"
#include "mongoc/mongoc-util-private.h"
#include <bson/bson-json-private.h>

#include "TestSuite.h"
#include "test-conveniences.h"
Expand Down Expand Up @@ -425,22 +426,33 @@ apm_match_visitor (match_ctx_t *ctx,
return MATCH_ACTION_CONTINUE;
}

static void
_print_bson_array_as_json (FILE *out, const bson_t *arr)
{
bson_iter_t it;
for (bson_iter_init (&it, arr); bson_iter_next (&it);) {
bson_t elem;
bson_iter_bson (&it, &elem);

bson_json_opts_t opts = {0};
opts.initial_indent = "";
opts.level_indent = " ";
opts.subsequent_indent = " ";
opts.mode = BSON_JSON_MODE_CANONICAL;
opts.max_len = BSON_MAX_LEN_UNLIMITED;
char *str = bson_as_json_with_opts (&elem, NULL, &opts);
fprintf (out, " - %s\n", str);
bson_free (str);
}
}

static void
_apm_match_error_context (const bson_t *actual, const bson_t *expectations)
{
char *actual_str;
char *expectations_str;

actual_str = bson_as_canonical_extended_json (actual, NULL);
expectations_str = bson_as_canonical_extended_json (expectations, NULL);
fprintf (stderr,
"Error in APM matching\nFull list of captured events: %s\nFull "
"list of expectations: %s",
actual_str,
expectations_str);
bson_free (actual_str);
bson_free (expectations_str);
fprintf (stderr, "Error in APM matching.\nFull list of captured events:\n");
_print_bson_array_as_json (stderr, actual);
fprintf (stderr, "Expected events:\n");
_print_bson_array_as_json (stderr, expectations);
}

bool
Expand Down