From b8bb078cc291a387ecce03a7a62ded593f76b85e Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Mon, 27 Feb 2017 14:56:55 -0500 Subject: [PATCH] Add JSON_EMBED encoding flag The JSON_EMBED encoding flag causes the opening and closing characters of the top-level array ('[', ']') or object ('{', '}') to be omitted during encoding. This feature makes it possible to concatenate multiple arrays or objects in the stream output. It also makes it possible to perform outputs of partial composes. One such example of a partial compose is when outputting a JWE object. The output is a JSON object. But it has one top-level attribute ("ciphertext") that can grow out of proportion with the rest of the metadata. With the JSON_EMBED flag, the other metadata can be composed ahead of time and dumped during the beginning of output, where the "ciphertext" and "tag" attributes can be streamed out in chunks. Thus, the header material can be composed with Jansson and the ciphertext itself can be composed manually. --- doc/apiref.rst | 7 +++++++ src/dump.c | 16 ++++++++++------ src/jansson.h | 1 + test/suites/api/test_dump.c | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/doc/apiref.rst b/doc/apiref.rst index f104ad5..bec4088 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -940,6 +940,13 @@ can be ORed together to obtain *flags*. .. versionadded:: 2.7 +``JSON_EMBED`` + If this flag is used, the opening and closing characters of the top-level + array ('[', ']') or object ('{', '}') are omitted during encoding. This + flag is useful when concatenating multiple arrays or objects into a stream. + + .. versionadded:: 2.10 + These functions output UTF-8: .. function:: char *json_dumps(const json_t *json, size_t flags) diff --git a/src/dump.c b/src/dump.c index cac2790..a23fabb 100644 --- a/src/dump.c +++ b/src/dump.c @@ -199,6 +199,10 @@ static int compare_keys(const void *key1, const void *key2) static int do_dump(const json_t *json, size_t flags, int depth, json_dump_callback_t dump, void *data) { + int embed = flags & JSON_EMBED; + + flags &= ~JSON_EMBED; + if(!json) return -1; @@ -258,11 +262,11 @@ static int do_dump(const json_t *json, size_t flags, int depth, n = json_array_size(json); - if(dump("[", 1, data)) + if(!embed && dump("[", 1, data)) goto array_error; if(n == 0) { array->visited = 0; - return dump("]", 1, data); + return embed ? 0 : dump("]", 1, data); } if(dump_indent(flags, depth + 1, 0, dump, data)) goto array_error; @@ -286,7 +290,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, } array->visited = 0; - return dump("]", 1, data); + return embed ? 0 : dump("]", 1, data); array_error: array->visited = 0; @@ -317,11 +321,11 @@ static int do_dump(const json_t *json, size_t flags, int depth, iter = json_object_iter((json_t *)json); - if(dump("{", 1, data)) + if(!embed && dump("{", 1, data)) goto object_error; if(!iter) { object->visited = 0; - return dump("}", 1, data); + return embed ? 0 : dump("}", 1, data); } if(dump_indent(flags, depth + 1, 0, dump, data)) goto object_error; @@ -417,7 +421,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, } object->visited = 0; - return dump("}", 1, data); + return embed ? 0 : dump("}", 1, data); object_error: object->visited = 0; diff --git a/src/jansson.h b/src/jansson.h index b5a4d15..cbe27a8 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -289,6 +289,7 @@ json_t *json_load_callback(json_load_callback_t callback, void *data, size_t fla #define JSON_ENCODE_ANY 0x200 #define JSON_ESCAPE_SLASH 0x400 #define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11) +#define JSON_EMBED 0x10000 typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); diff --git a/test/suites/api/test_dump.c b/test/suites/api/test_dump.c index 8d59d40..17c495a 100644 --- a/test/suites/api/test_dump.c +++ b/test/suites/api/test_dump.c @@ -278,6 +278,39 @@ static void dumpfd() #endif } +static void embed() +{ + static const char *plains[] = { + "{\"bar\":[],\"foo\":{}}", + "[[],{}]", + "{}", + "[]", + NULL + }; + + size_t i; + + for(i = 0; plains[i]; i++) { + const char *plain = plains[i]; + json_t *parse = NULL; + char *embed = NULL; + size_t psize = 0; + size_t esize = 0; + + psize = strlen(plain) - 2; + embed = calloc(1, psize); + parse = json_loads(plain, 0, NULL); + esize = json_dumpb(parse, embed, psize, + JSON_COMPACT | JSON_SORT_KEYS | JSON_EMBED); + json_decref(parse); + if(esize != psize) + fail("json_dumpb(JSON_EMBED) returned an invalid size"); + if(strncmp(plain + 1, embed, esize) != 0) + fail("json_dumps(JSON_EMBED) returned an invalid value"); + free(embed); + } +} + static void run_tests() { encode_null(); @@ -289,4 +322,5 @@ static void run_tests() dump_file(); dumpb(); dumpfd(); + embed(); }