+Version 1.3 (in development)
+============================
+
+* New encoding flags:
+
+ - ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object
+ keys.
+
+
Version 1.2
===========
AC_PREREQ([2.59])
-AC_INIT([jansson], [1.2], [petri@digip.org])
+AC_INIT([jansson], [1.2+], [petri@digip.org])
AM_INIT_AUTOMAKE([1.10 foreign])
.. versionadded:: 1.2
+``JSON_PRESERVE_ORDER``
+ If this flag is used, object keys in the output are sorted into the
+ same order in which they were first inserted to the object. For
+ example, decoding a JSON text and then encoding with this flag
+ preserves the order of object keys.
+
+ .. versionadded:: 1.3
+
The following functions perform the actual JSON encoding. The result
is in UTF-8.
# The short X.Y version.
version = '1.2'
# The full version, including alpha/beta/rc tags.
-release = '1.2'
+release = '1.2+'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
return dump("\"", 1, data);
}
-static int object_key_cmp(const void *key1, const void *key2)
+static int object_key_compare_keys(const void *key1, const void *key2)
{
- return strcmp(*(const char **)key1, *(const char **)key2);
+ return strcmp((*(const object_key_t **)key1)->key,
+ (*(const object_key_t **)key2)->key);
+}
+
+static int object_key_compare_serials(const void *key1, const void *key2)
+{
+ return (*(const object_key_t **)key1)->serial -
+ (*(const object_key_t **)key2)->serial;
}
static int do_dump(const json_t *json, unsigned long flags, int depth,
if(dump_indent(flags, depth + 1, 0, dump, data))
return -1;
- if(flags & JSON_SORT_KEYS)
+ if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
{
- /* Sort keys */
-
- const char **keys;
+ const object_key_t **keys;
unsigned int size;
unsigned int i;
+ int (*cmp_func)(const void *, const void *);
size = json_object_size(json);
- keys = malloc(size * sizeof(const char *));
+ keys = malloc(size * sizeof(object_key_t *));
if(!keys)
return -1;
i = 0;
while(iter)
{
- keys[i] = json_object_iter_key(iter);
+ keys[i] = jsonp_object_iter_fullkey(iter);
iter = json_object_iter_next((json_t *)json, iter);
i++;
}
assert(i == size);
- qsort(keys, size, sizeof(const char *), object_key_cmp);
+ if(flags & JSON_SORT_KEYS)
+ cmp_func = object_key_compare_keys;
+ else
+ cmp_func = object_key_compare_serials;
+
+ qsort(keys, size, sizeof(object_key_t *), cmp_func);
for(i = 0; i < size; i++)
{
const char *key;
json_t *value;
- key = keys[i];
+ key = keys[i]->key;
value = json_object_get(json, key);
assert(value);
bucket_t *bucket;
unsigned int hash, index;
- hash = hashtable->hash_key(key);
-
- /* if the key already exists, delete it */
- hashtable_do_del(hashtable, key, hash);
-
/* rehash if the load ratio exceeds 1 */
if(hashtable->size >= num_buckets(hashtable))
if(hashtable_do_rehash(hashtable))
return -1;
- pair = malloc(sizeof(pair_t));
- if(!pair)
- return -1;
-
- pair->key = key;
- pair->value = value;
- pair->hash = hash;
- list_init(&pair->list);
-
+ hash = hashtable->hash_key(key);
index = hash % num_buckets(hashtable);
bucket = &hashtable->buckets[index];
+ pair = hashtable_find_pair(hashtable, bucket, key, hash);
- insert_to_bucket(hashtable, bucket, &pair->list);
+ if(pair)
+ {
+ if(hashtable->free_key)
+ hashtable->free_key(key);
+ if(hashtable->free_value)
+ hashtable->free_value(pair->value);
+ pair->value = value;
+ }
+ else
+ {
+ pair = malloc(sizeof(pair_t));
+ if(!pair)
+ return -1;
+
+ pair->key = key;
+ pair->value = value;
+ pair->hash = hash;
+ list_init(&pair->list);
- hashtable->size++;
+ insert_to_bucket(hashtable, bucket, &pair->list);
+
+ hashtable->size++;
+ }
return 0;
}
#define JSON_COMPACT 0x100
#define JSON_ENSURE_ASCII 0x200
#define JSON_SORT_KEYS 0x400
+#define JSON_PRESERVE_ORDER 0x800
char *json_dumps(const json_t *json, unsigned long flags);
int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
typedef struct {
json_t json;
hashtable_t hashtable;
+ unsigned long serial;
int visited;
} json_object_t;
#define json_to_real(json_) container_of(json_, json_real_t, json)
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
+typedef struct {
+ unsigned long serial;
+ char key[];
+} object_key_t;
+
+const object_key_t *jsonp_object_iter_fullkey(void *iter);
+
#endif
/*** object ***/
-static unsigned int hash_string(const void *key)
+/* This macro just returns a pointer that's a few bytes backwards from
+ string. This makes it possible to pass a pointer to object_key_t
+ when only the string inside it is used, without actually creating
+ an object_key_t instance. */
+#define string_to_key(string) container_of(string, object_key_t, key)
+
+static unsigned int hash_key(const void *ptr)
{
- const char *str = (const char *)key;
+ const char *str = ((const object_key_t *)ptr)->key;
+
unsigned int hash = 5381;
unsigned int c;
return hash;
}
-static int string_equal(const void *key1, const void *key2)
+static int key_equal(const void *ptr1, const void *ptr2)
{
- return strcmp((const char *)key1, (const char *)key2) == 0;
+ return strcmp(((const object_key_t *)ptr1)->key,
+ ((const object_key_t *)ptr2)->key) == 0;
}
static void value_decref(void *value)
return NULL;
json_init(&object->json, JSON_OBJECT);
- if(hashtable_init(&object->hashtable, hash_string, string_equal,
+ if(hashtable_init(&object->hashtable, hash_key, key_equal,
free, value_decref))
{
free(object);
return NULL;
}
+ object->serial = 0;
object->visited = 0;
return &object->json;
return NULL;
object = json_to_object(json);
- return hashtable_get(&object->hashtable, key);
+ return hashtable_get(&object->hashtable, string_to_key(key));
}
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
{
json_object_t *object;
+ object_key_t *k;
if(!key || !value)
return -1;
}
object = json_to_object(json);
- if(hashtable_set(&object->hashtable, strdup(key), value))
+ k = malloc(sizeof(object_key_t) + strlen(key) + 1);
+ if(!k)
+ return -1;
+
+ k->serial = object->serial++;
+ strcpy(k->key, key);
+
+ if(hashtable_set(&object->hashtable, k, value))
{
json_decref(value);
return -1;
return -1;
object = json_to_object(json);
- return hashtable_del(&object->hashtable, key);
+ return hashtable_del(&object->hashtable, string_to_key(key));
}
int json_object_clear(json_t *json)
return NULL;
object = json_to_object(json);
- return hashtable_iter_at(&object->hashtable, key);
+ return hashtable_iter_at(&object->hashtable, string_to_key(key));
}
void *json_object_iter_next(json_t *json, void *iter)
return hashtable_iter_next(&object->hashtable, iter);
}
+const object_key_t *jsonp_object_iter_fullkey(void *iter)
+{
+ if(!iter)
+ return NULL;
+
+ return hashtable_iter_key(iter);
+}
+
const char *json_object_iter_key(void *iter)
{
if(!iter)
return NULL;
- return (const char *)hashtable_iter_key(iter);
+ return jsonp_object_iter_fullkey(iter)->key;
}
json_t *json_object_iter_value(void *iter)
if(getenv_int("JSON_ENSURE_ASCII"))
flags |= JSON_ENSURE_ASCII;
+ if(getenv_int("JSON_PRESERVE_ORDER"))
+ flags |= JSON_PRESERVE_ORDER;
+
if(getenv_int("JSON_SORT_KEYS"))
flags |= JSON_SORT_KEYS;
json_decref(object);
}
+static void test_preserve_order()
+{
+ json_t *object;
+ char *result;
+
+ const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": 3, \"sit amet\": 5, \"helicopter\": 7}";
+
+ object = json_object();
+
+ json_object_set_new(object, "foobar", json_integer(1));
+ json_object_set_new(object, "bazquux", json_integer(2));
+ json_object_set_new(object, "lorem ipsum", json_integer(3));
+ json_object_set_new(object, "dolor", json_integer(4));
+ json_object_set_new(object, "sit amet", json_integer(5));
+
+ /* changing a value should preserve the order */
+ json_object_set_new(object, "bazquux", json_integer(6));
+
+ /* deletion shouldn't change the order of others */
+ json_object_del(object, "dolor");
+
+ /* add a new item just to make sure */
+ json_object_set_new(object, "helicopter", json_integer(7));
+
+ result = json_dumps(object, JSON_PRESERVE_ORDER);
+
+ if(strcmp(expected, result) != 0) {
+ fprintf(stderr, "%s != %s", expected, result);
+ fail("JSON_PRESERVE_ORDER doesn't work");
+ }
+
+ free(result);
+ json_decref(object);
+}
+
int main()
{
test_misc();
test_circular();
test_set_nocheck();
test_iterators();
+ test_preserve_order();
return 0;
}
--- /dev/null
+export JSON_PRESERVE_ORDER=1
--- /dev/null
+{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
--- /dev/null
+{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
\ No newline at end of file