From 19a606d7361e73ff2f5e0b240c2adb07b35e4273 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Mon, 7 Dec 2009 13:16:45 +0200 Subject: [PATCH] Implement JSON_SORT_KEYS encoding flag With this flag, the objects are sorted by key when encoding. --- doc/apiref.rst | 7 ++ src/dump.c | 99 +++++++++++++++++++++++++---- src/jansson.h | 1 + test/bin/json_process.c | 3 + test/suites/encoding-flags/run | 32 ++++++++++ test/suites/encoding-flags/sort-keys/env | 1 + test/suites/encoding-flags/sort-keys/input | 1 + test/suites/encoding-flags/sort-keys/output | 1 + 8 files changed, 132 insertions(+), 13 deletions(-) create mode 100755 test/suites/encoding-flags/run create mode 100644 test/suites/encoding-flags/sort-keys/env create mode 100644 test/suites/encoding-flags/sort-keys/input create mode 100644 test/suites/encoding-flags/sort-keys/output diff --git a/doc/apiref.rst b/doc/apiref.rst index dcd401c..fd2a7c9 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -526,6 +526,13 @@ can be ORed together to obtain *flags*. .. versionadded:: 1.2 +``JSON_SORT_KEYS`` + If this flag is used, all the objects in output are sorted by key. + This is useful e.g. if two JSON texts are diffed or visually + compared. + + .. versionadded:: 1.2 + The following functions perform the actual JSON encoding. The result is in UTF-8. diff --git a/src/dump.c b/src/dump.c index dc3fcbc..328e93b 100644 --- a/src/dump.c +++ b/src/dump.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "jansson_private.h" @@ -153,6 +154,11 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data) return dump("\"", 1, data); } +static int object_key_cmp(const void *key1, const void *key2) +{ + return strcmp(*(const char **)key1, *(const char **)key2); +} + static int do_dump(const json_t *json, unsigned long flags, int depth, dump_func dump, void *data) { @@ -269,29 +275,96 @@ static int do_dump(const json_t *json, unsigned long flags, int depth, if(dump_indent(flags, depth + 1, 0, dump, data)) return -1; - while(iter) + if(flags & JSON_SORT_KEYS) { - void *next = json_object_iter_next((json_t *)json, iter); + /* Sort keys */ - dump_string(json_object_iter_key(iter), ascii, dump, data); - if(dump(separator, separator_length, data) || - do_dump(json_object_iter_value(iter), flags, depth + 1, - dump, data)) + const char **keys; + unsigned int size; + unsigned int i; + + size = json_object_size(json); + keys = malloc(size * sizeof(const char *)); + if(!keys) return -1; - if(next) + i = 0; + while(iter) { - if(dump(",", 1, data) || - dump_indent(flags, depth + 1, 1, dump, data)) - return -1; + keys[i] = json_object_iter_key(iter); + iter = json_object_iter_next((json_t *)json, iter); + i++; } - else + assert(i == size); + + qsort(keys, size, sizeof(const char *), object_key_cmp); + + for(i = 0; i < size; i++) { - if(dump_indent(flags, depth, 0, dump, data)) + const char *key; + json_t *value; + + key = keys[i]; + value = json_object_get(json, key); + assert(value); + + dump_string(key, ascii, dump, data); + if(dump(separator, separator_length, data) || + do_dump(value, flags, depth + 1, dump, data)) + { + free(keys); return -1; + } + + if(i < size - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + { + free(keys); + return -1; + } + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + { + free(keys); + return -1; + } + } } - iter = next; + free(keys); + } + else + { + /* Don't sort keys */ + + while(iter) + { + void *next = json_object_iter_next((json_t *)json, iter); + + dump_string(json_object_iter_key(iter), ascii, dump, data); + if(dump(separator, separator_length, data) || + do_dump(json_object_iter_value(iter), flags, depth + 1, + dump, data)) + return -1; + + if(next) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + return -1; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + return -1; + } + + iter = next; + } } object->visited = 0; diff --git a/src/jansson.h b/src/jansson.h index d59fe10..607dfb5 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -144,6 +144,7 @@ json_t *json_load_file(const char *path, json_error_t *error); #define JSON_INDENT(n) (n & 0xFF) #define JSON_COMPACT 0x100 #define JSON_ENSURE_ASCII 0x200 +#define JSON_SORT_KEYS 0x400 char *json_dumps(const json_t *json, unsigned long flags); int json_dumpf(const json_t *json, FILE *output, unsigned long flags); diff --git a/test/bin/json_process.c b/test/bin/json_process.c index e7be614..794e307 100644 --- a/test/bin/json_process.c +++ b/test/bin/json_process.c @@ -53,6 +53,9 @@ int main(int argc, char *argv[]) if(getenv_int("JSON_ENSURE_ASCII")) flags |= JSON_ENSURE_ASCII; + if(getenv_int("JSON_SORT_KEYS")) + flags |= JSON_SORT_KEYS; + json = json_loadf(stdin, &error); if(!json) { fprintf(stderr, "%d\n%s\n", error.line, error.text); diff --git a/test/suites/encoding-flags/run b/test/suites/encoding-flags/run new file mode 100755 index 0000000..a65fe5b --- /dev/null +++ b/test/suites/encoding-flags/run @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Copyright (c) 2009 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +is_test() { + test -d $test_path +} + +run_test() { + ( + if [ -f $test_path/env ]; then + . $test_path/env + fi + $json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr + ) + valgrind_check $test_log/stderr || return 1 + cmp -s $test_path/output $test_log/stdout +} + +show_error() { + valgrind_show_error && return + + echo "EXPECTED OUTPUT:" + nl -bn $test_path/output + echo "ACTUAL OUTPUT:" + nl -bn $test_log/stdout +} + +. $top_srcdir/test/scripts/run-tests.sh diff --git a/test/suites/encoding-flags/sort-keys/env b/test/suites/encoding-flags/sort-keys/env new file mode 100644 index 0000000..00529e7 --- /dev/null +++ b/test/suites/encoding-flags/sort-keys/env @@ -0,0 +1 @@ +export JSON_SORT_KEYS=1 diff --git a/test/suites/encoding-flags/sort-keys/input b/test/suites/encoding-flags/sort-keys/input new file mode 100644 index 0000000..66951d6 --- /dev/null +++ b/test/suites/encoding-flags/sort-keys/input @@ -0,0 +1 @@ +{"foo": 1, "bar": 2, "baz": 3, "quux": 4} diff --git a/test/suites/encoding-flags/sort-keys/output b/test/suites/encoding-flags/sort-keys/output new file mode 100644 index 0000000..132d9df --- /dev/null +++ b/test/suites/encoding-flags/sort-keys/output @@ -0,0 +1 @@ +{"bar": 2, "baz": 3, "foo": 1, "quux": 4} \ No newline at end of file -- 2.1.4