Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2caac965d4 | ||
|
|
1347686dbf | ||
|
|
8b2b12e05f | ||
|
|
1a090bbcd3 | ||
|
|
dec3ad498e | ||
|
|
978a47e2c5 | ||
|
|
453e4c0aa2 | ||
|
|
2630980f49 | ||
|
|
782acfe378 | ||
|
|
f9475f9577 | ||
|
|
8857aeadfd | ||
|
|
4e63fcd55d | ||
|
|
49880cbabe | ||
|
|
66a69f3f10 | ||
|
|
7d5982e6fe | ||
|
|
42621370c3 | ||
|
|
8e61b7c0f0 | ||
|
|
35ddd2de20 | ||
|
|
f18ef5144a | ||
|
|
307167fb66 | ||
|
|
7e8b128740 | ||
|
|
acec2559a5 | ||
|
|
8d75235ff2 | ||
|
|
79e9dae9a0 | ||
|
|
f021ba00a2 | ||
|
|
adb1b58627 | ||
|
|
b8059a1880 | ||
|
|
49d40f020b | ||
|
|
910a2f318b | ||
|
|
c9fc055351 | ||
|
|
d1a0c3ffc2 | ||
|
|
b07e69c37a | ||
|
|
2b43e7dbda | ||
|
|
5b1a666cf1 | ||
|
|
b495b96547 | ||
|
|
72e3948438 | ||
|
|
04f7e27877 | ||
|
|
3dd29366b8 | ||
|
|
38950b081c | ||
|
|
56687e9b56 | ||
|
|
c9b33e3386 | ||
|
|
2ad4634de5 | ||
|
|
e080667729 | ||
|
|
ef6c35ae1b | ||
|
|
95bf762eeb | ||
|
|
dd36e4e838 | ||
|
|
df35adc438 | ||
|
|
f88a5a0e6b | ||
|
|
cc06bc334a | ||
|
|
2dc2b6bab7 | ||
|
|
49a64a6edf | ||
|
|
f0be52f9f8 | ||
|
|
1bc0225441 | ||
|
|
87df8bb0fe | ||
|
|
69437a7183 | ||
|
|
63f762bc48 | ||
|
|
5a0efe6536 | ||
|
|
01759517aa | ||
|
|
17805e5829 | ||
|
|
492d95329a | ||
|
|
7ba18d3f0a | ||
|
|
7d09af38c1 | ||
|
|
8d3a9e347c | ||
|
|
f79a81dad9 | ||
|
|
b98e9d180c | ||
|
|
8d5d2a93d5 | ||
|
|
d77c2e3fb0 | ||
|
|
7ef3202f83 | ||
|
|
36085ab49a | ||
|
|
f743c4ee7f | ||
|
|
c994eddec4 | ||
|
|
5a20e2695b | ||
|
|
cd18aa97f0 | ||
|
|
bd09127859 | ||
|
|
6818c117ee | ||
|
|
39601c183a | ||
|
|
1e3b41e8ea |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*~
|
||||
*.o
|
||||
*.a
|
||||
.libs
|
||||
|
||||
25
CHANGES
25
CHANGES
@@ -1,3 +1,28 @@
|
||||
Version 1.3
|
||||
===========
|
||||
|
||||
Released 2010-06-13
|
||||
|
||||
* New encoding flags:
|
||||
|
||||
- ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object
|
||||
keys.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix an error that occured when an array or object was first
|
||||
encoded as empty, then populated with some data, and then
|
||||
re-encoded
|
||||
|
||||
- Fix the situation like above, but when the first encoding resulted
|
||||
in an error
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Clarify the documentation on reference stealing, providing an
|
||||
example usage pattern
|
||||
|
||||
|
||||
Version 1.2.1
|
||||
=============
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [1.2.1], [petri@digip.org])
|
||||
AC_INIT([jansson], [1.3], [petri@digip.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
@@ -8,6 +8,7 @@ AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
@@ -154,9 +154,31 @@ Normally, all functions accepting a JSON value as an argument will
|
||||
manage the reference, i.e. increase and decrease the reference count
|
||||
as needed. However, some functions **steal** the reference, i.e. they
|
||||
have the same result as if the user called :cfunc:`json_decref()` on
|
||||
the argument right after calling the function. These are usually
|
||||
convenience functions for adding new references to containers and not
|
||||
to worry about the reference count.
|
||||
the argument right after calling the function. These functions are
|
||||
suffixed with ``_new`` or have ``_new_`` somewhere in their name.
|
||||
|
||||
For example, the following code creates a new JSON array and appends
|
||||
an integer to it::
|
||||
|
||||
json_t *array, *integer;
|
||||
|
||||
array = json_array();
|
||||
integer = json_integer(42);
|
||||
|
||||
json_array_append(array, integer);
|
||||
json_decref(integer);
|
||||
|
||||
Note how the caller has to release the reference to the integer value
|
||||
by calling :cfunc:`json_decref()`. By using a reference stealing
|
||||
function :cfunc:`json_array_append_new()` instead of
|
||||
:cfunc:`json_array_append()`, the code becomes much simpler::
|
||||
|
||||
json_t *array = json_array();
|
||||
json_array_append_new(array, json_integer(42));
|
||||
|
||||
In this case, the user doesn't have to explicitly release the
|
||||
reference to the integer value, as :cfunc:`json_array_append_new()`
|
||||
steals the reference when appending the value to the array.
|
||||
|
||||
In the following sections it is clearly documented whether a function
|
||||
will return a new or borrowed reference or steal a reference to its
|
||||
@@ -493,6 +515,16 @@ The following functions implement an iteration protocol for objects:
|
||||
Returns an opaque iterator which can be used to iterate over all
|
||||
key-value pairs in *object*, or *NULL* if *object* is empty.
|
||||
|
||||
.. cfunction:: void *json_object_iter_at(json_t *object, const char *key)
|
||||
|
||||
Like :cfunc:`json_object_iter()`, but returns an iterator to the
|
||||
key-value pair in *object* whose key is equal to *key*, or NULL if
|
||||
*key* is not found in *object*. Iterating forward to the end of
|
||||
*object* only yields all key-value pairs of the object if *key*
|
||||
happens to be the first key in the underlying hash table.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. cfunction:: void *json_object_iter_next(json_t *object, void *iter)
|
||||
|
||||
Returns an iterator pointing to the next key-value pair in *object*
|
||||
@@ -509,6 +541,21 @@ The following functions implement an iteration protocol for objects:
|
||||
|
||||
Extract the associated value from *iter*.
|
||||
|
||||
.. cfunction:: int json_object_iter_set(json_t *object, void *iter, json_t *value)
|
||||
|
||||
Set the value of the key-value pair in *object*, that is pointed to
|
||||
by *iter*, to *value*.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. cfunction:: int json_object_iter_set_new(json_t *object, void *iter, json_t *value)
|
||||
|
||||
Like :cfunc:`json_object_iter_set()`, but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The iteration protocol can be used for example as follows::
|
||||
|
||||
/* obj is a JSON object */
|
||||
@@ -570,6 +617,14 @@ can be ORed together to obtain *flags*.
|
||||
|
||||
.. 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.
|
||||
|
||||
|
||||
@@ -50,9 +50,9 @@ copyright = u'2009, 2010 Petri Lehtinen'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.2'
|
||||
version = '1.3'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.2.1'
|
||||
release = '1.3'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -15,6 +15,6 @@ libjansson_la_SOURCES = \
|
||||
value.c
|
||||
libjansson_la_LDFLAGS = \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 2:1:2
|
||||
-version-info 3:0:3
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra -Werror
|
||||
|
||||
79
src/dump.c
79
src/dump.c
@@ -153,9 +153,16 @@ 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)
|
||||
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,
|
||||
@@ -224,38 +231,44 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
/* detect circular references */
|
||||
array = json_to_array(json);
|
||||
if(array->visited)
|
||||
return -1;
|
||||
goto array_error;
|
||||
array->visited = 1;
|
||||
|
||||
n = json_array_size(json);
|
||||
|
||||
if(dump("[", 1, data))
|
||||
return -1;
|
||||
if(n == 0)
|
||||
goto array_error;
|
||||
if(n == 0) {
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
return -1;
|
||||
goto array_error;
|
||||
|
||||
for(i = 0; i < n; ++i) {
|
||||
if(do_dump(json_array_get(json, i), flags, depth + 1,
|
||||
dump, data))
|
||||
return -1;
|
||||
goto array_error;
|
||||
|
||||
if(i < n - 1)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
return -1;
|
||||
goto array_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
return -1;
|
||||
goto array_error;
|
||||
}
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
|
||||
array_error:
|
||||
array->visited = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
case JSON_OBJECT:
|
||||
@@ -277,48 +290,54 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
/* detect circular references */
|
||||
object = json_to_object(json);
|
||||
if(object->visited)
|
||||
return -1;
|
||||
goto object_error;
|
||||
object->visited = 1;
|
||||
|
||||
iter = json_object_iter((json_t *)json);
|
||||
|
||||
if(dump("{", 1, data))
|
||||
return -1;
|
||||
if(!iter)
|
||||
goto object_error;
|
||||
if(!iter) {
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
return -1;
|
||||
goto object_error;
|
||||
|
||||
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;
|
||||
goto object_error;
|
||||
|
||||
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);
|
||||
|
||||
@@ -327,7 +346,7 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
{
|
||||
free(keys);
|
||||
return -1;
|
||||
goto object_error;
|
||||
}
|
||||
|
||||
if(i < size - 1)
|
||||
@@ -336,7 +355,7 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
{
|
||||
free(keys);
|
||||
return -1;
|
||||
goto object_error;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -344,7 +363,7 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
{
|
||||
free(keys);
|
||||
return -1;
|
||||
goto object_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,18 +382,18 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
return -1;
|
||||
goto object_error;
|
||||
|
||||
if(next)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
return -1;
|
||||
goto object_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
return -1;
|
||||
goto object_error;
|
||||
}
|
||||
|
||||
iter = next;
|
||||
@@ -383,6 +402,10 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
|
||||
object_error:
|
||||
object->visited = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -249,31 +249,39 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *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;
|
||||
|
||||
hashtable->size++;
|
||||
pair->key = key;
|
||||
pair->value = value;
|
||||
pair->hash = hash;
|
||||
list_init(&pair->list);
|
||||
|
||||
insert_to_bucket(hashtable, bucket, &pair->list);
|
||||
|
||||
hashtable->size++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -320,6 +328,22 @@ void *hashtable_iter(hashtable_t *hashtable)
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
}
|
||||
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
|
||||
{
|
||||
pair_t *pair;
|
||||
unsigned int hash;
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hashtable->hash_key(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
return NULL;
|
||||
|
||||
return &pair->list;
|
||||
}
|
||||
|
||||
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
|
||||
{
|
||||
list_t *list = (list_t *)iter;
|
||||
@@ -339,3 +363,13 @@ void *hashtable_iter_value(void *iter)
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
|
||||
pair->value = value;
|
||||
}
|
||||
|
||||
@@ -160,6 +160,17 @@ void hashtable_clear(hashtable_t *hashtable);
|
||||
*/
|
||||
void *hashtable_iter(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_iter_at - Return an iterator at a specific key
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key that the iterator should point to
|
||||
*
|
||||
* Like hashtable_iter() but returns an iterator pointing to a
|
||||
* specific key.
|
||||
*/
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
|
||||
|
||||
/**
|
||||
* hashtable_iter_next - Advance an iterator
|
||||
*
|
||||
@@ -185,4 +196,12 @@ void *hashtable_iter_key(void *iter);
|
||||
*/
|
||||
void *hashtable_iter_value(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_set - Set the value pointed by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
* @value: The value to set
|
||||
*/
|
||||
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -88,9 +88,11 @@ int json_object_del(json_t *object, const char *key);
|
||||
int json_object_clear(json_t *object);
|
||||
int json_object_update(json_t *object, json_t *other);
|
||||
void *json_object_iter(json_t *object);
|
||||
void *json_object_iter_at(json_t *object, const char *key);
|
||||
void *json_object_iter_next(json_t *object, void *iter);
|
||||
const char *json_object_iter_key(void *iter);
|
||||
json_t *json_object_iter_value(void *iter);
|
||||
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
|
||||
static JSON_INLINE
|
||||
int json_object_set(json_t *object, const char *key, json_t *value)
|
||||
@@ -104,6 +106,12 @@ int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
|
||||
return json_object_set_new_nocheck(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
static inline
|
||||
int json_object_iter_set(json_t *object, void *iter, json_t *value)
|
||||
{
|
||||
return json_object_iter_set_new(object, iter, json_incref(value));
|
||||
}
|
||||
|
||||
unsigned int json_array_size(const json_t *array);
|
||||
json_t *json_array_get(const json_t *array, unsigned int index);
|
||||
int json_array_set_new(json_t *array, unsigned int index, json_t *value);
|
||||
@@ -170,6 +178,7 @@ json_t *json_load_file(const char *path, json_error_t *error);
|
||||
#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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
unsigned long serial;
|
||||
int visited;
|
||||
} json_object_t;
|
||||
|
||||
@@ -49,4 +50,11 @@ typedef struct {
|
||||
#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
|
||||
|
||||
@@ -113,7 +113,8 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
||||
|
||||
/*** lexical analyzer ***/
|
||||
|
||||
void stream_init(stream_t *stream, get_func get, eof_func eof, void *data)
|
||||
static void
|
||||
stream_init(stream_t *stream, get_func get, eof_func eof, void *data)
|
||||
{
|
||||
stream->get = get;
|
||||
stream->eof = eof;
|
||||
|
||||
67
src/value.c
67
src/value.c
@@ -28,9 +28,16 @@ static inline void json_init(json_t *json, json_type type)
|
||||
|
||||
/*** 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;
|
||||
|
||||
@@ -43,9 +50,10 @@ static unsigned int hash_string(const void *key)
|
||||
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)
|
||||
@@ -60,13 +68,14 @@ json_t *json_object(void)
|
||||
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;
|
||||
@@ -97,12 +106,13 @@ json_t *json_object_get(const json_t *json, const char *key)
|
||||
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;
|
||||
@@ -114,7 +124,14 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
}
|
||||
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;
|
||||
@@ -142,7 +159,7 @@ int json_object_del(json_t *json, const char *key)
|
||||
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)
|
||||
@@ -193,6 +210,17 @@ void *json_object_iter(json_t *json)
|
||||
return hashtable_iter(&object->hashtable);
|
||||
}
|
||||
|
||||
void *json_object_iter_at(json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!key || !json_is_object(json))
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
return hashtable_iter_at(&object->hashtable, string_to_key(key));
|
||||
}
|
||||
|
||||
void *json_object_iter_next(json_t *json, void *iter)
|
||||
{
|
||||
json_object_t *object;
|
||||
@@ -204,12 +232,20 @@ 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)
|
||||
@@ -220,6 +256,19 @@ json_t *json_object_iter_value(void *iter)
|
||||
return (json_t *)hashtable_iter_value(iter);
|
||||
}
|
||||
|
||||
int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json) || !iter || !value)
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
hashtable_iter_set(&object->hashtable, iter, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_object_equal(json_t *object1, json_t *object2)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
2
test/.gitignore
vendored
2
test/.gitignore
vendored
@@ -3,7 +3,9 @@ bin/json_process
|
||||
suites/api/test_array
|
||||
suites/api/test_equal
|
||||
suites/api/test_copy
|
||||
suites/api/test_dump
|
||||
suites/api/test_load
|
||||
suites/api/test_number
|
||||
suites/api/test_object
|
||||
suites/api/test_simple
|
||||
suites/api/test_cpp
|
||||
|
||||
@@ -53,6 +53,9 @@ int main(int argc, char *argv[])
|
||||
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;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ check_PROGRAMS = \
|
||||
test_array \
|
||||
test_equal \
|
||||
test_copy \
|
||||
test_dump \
|
||||
test_load \
|
||||
test_simple \
|
||||
test_number \
|
||||
@@ -11,6 +12,7 @@ check_PROGRAMS = \
|
||||
|
||||
test_array_SOURCES = test_array.c util.h
|
||||
test_copy_SOURCES = test_copy.c util.h
|
||||
test_dump_SOURCES = test_dump.c util.h
|
||||
test_load_SOURCES = test_load.c util.h
|
||||
test_simple_SOURCES = test_simple.c util.h
|
||||
test_number_SOURCES = test_number.c util.h
|
||||
|
||||
@@ -39,9 +39,11 @@ json_object_del
|
||||
json_object_clear
|
||||
json_object_update
|
||||
json_object_iter
|
||||
json_object_iter_at
|
||||
json_object_iter_next
|
||||
json_object_iter_key
|
||||
json_object_iter_value
|
||||
json_object_iter_set_new
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
|
||||
@@ -6,13 +6,18 @@
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
is_test() {
|
||||
[ "${test_name%.c}" != "$test_name" ] && return 0
|
||||
[ -x $test_path -a ! -f $test_path.c ] && return 0
|
||||
return 1
|
||||
case "$test_name" in
|
||||
*.c|check-exports)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_test() {
|
||||
if [ -x $test_path ]; then
|
||||
if [ "$test_name" = "check-exports" ]; then
|
||||
test_log=$test_log $test_path >$test_log/stdout 2>$test_log/stderr
|
||||
else
|
||||
$test_runner $suite_builddir/${test_name%.c} \
|
||||
|
||||
91
test/suites/api/test_dump.c
Normal file
91
test/suites/api/test_dump.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
|
||||
json = json_object();
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{}"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_object_set_new(json, "foo", json_integer(5));
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{\"foo\": 5}"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
json = json_array();
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "[]"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_array_append_new(json, json_integer(5));
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "[5]"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
/* Construct a JSON object/array with a circular reference:
|
||||
|
||||
object: {"a": {"b": {"c": <circular reference to $.a>}}}
|
||||
array: [[[<circular reference to the $[0] array>]]]
|
||||
|
||||
Encode it, remove the circular reference and encode again.
|
||||
*/
|
||||
json = json_object();
|
||||
json_object_set_new(json, "a", json_object());
|
||||
json_object_set_new(json_object_get(json, "a"), "b", json_object());
|
||||
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
|
||||
json_object_get(json, "a"));
|
||||
|
||||
if(json_dumps(json, 0))
|
||||
fail("json_dumps encoded a circular reference!");
|
||||
|
||||
json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");
|
||||
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{\"a\": {\"b\": {}}}"))
|
||||
fail("json_dumps failed!");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
json = json_array();
|
||||
json_array_append_new(json, json_array());
|
||||
json_array_append_new(json_array_get(json, 0), json_array());
|
||||
json_array_append(json_array_get(json_array_get(json, 0), 0),
|
||||
json_array_get(json, 0));
|
||||
|
||||
if(json_dumps(json, 0))
|
||||
fail("json_dumps encoded a circular reference!");
|
||||
|
||||
json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);
|
||||
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "[[[]]]"))
|
||||
fail("json_dumps failed!");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -258,6 +258,36 @@ static void test_iterators()
|
||||
if(json_object_iter_next(object, iter) != NULL)
|
||||
fail("able to iterate over the end");
|
||||
|
||||
if(json_object_iter_at(object, "foo"))
|
||||
fail("json_object_iter_at() succeeds for non-existent key");
|
||||
|
||||
iter = json_object_iter_at(object, "b");
|
||||
if(!iter)
|
||||
fail("json_object_iter_at() fails for an existing key");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "b"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
if(json_object_iter_set(object, iter, bar))
|
||||
fail("unable to set value at iterator");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("json_object_iter_key() fails after json_object_iter_set()");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("json_object_iter_value() fails after json_object_iter_set()");
|
||||
if(json_object_get(object, "c") != bar)
|
||||
fail("json_object_get() fails after json_object_iter_set()");
|
||||
|
||||
json_decref(object);
|
||||
json_decref(foo);
|
||||
json_decref(bar);
|
||||
@@ -372,6 +402,41 @@ static void test_misc()
|
||||
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();
|
||||
@@ -380,6 +445,7 @@ int main()
|
||||
test_circular();
|
||||
test_set_nocheck();
|
||||
test_iterators();
|
||||
test_preserve_order();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
1
test/suites/encoding-flags/preserve-order/env
Normal file
1
test/suites/encoding-flags/preserve-order/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_PRESERVE_ORDER=1
|
||||
1
test/suites/encoding-flags/preserve-order/input
Normal file
1
test/suites/encoding-flags/preserve-order/input
Normal file
@@ -0,0 +1 @@
|
||||
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
|
||||
1
test/suites/encoding-flags/preserve-order/output
Normal file
1
test/suites/encoding-flags/preserve-order/output
Normal file
@@ -0,0 +1 @@
|
||||
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
|
||||
Reference in New Issue
Block a user