77 Commits
v1.2.1 ... v1.3

Author SHA1 Message Date
Petri Lehtinen
2caac965d4 jansson 1.3 2010-06-13 20:37:33 +03:00
Petri Lehtinen
1347686dbf Remove the C++ interface 2010-06-12 22:45:49 +03:00
Petri Lehtinen
8b2b12e05f Merge branch '1.2' 2010-06-12 22:45:45 +03:00
Petri Lehtinen
1a090bbcd3 Fix a few memory leaks in tests
No changes to the actual library code.
2010-06-10 21:16:07 +03:00
Petri Lehtinen
dec3ad498e Merge branch '1.2' 2010-05-20 18:47:30 +03:00
Petri Lehtinen
978a47e2c5 Clarify the documentation on reference stealing
Provide an example usage pattern for reference stealing functions.
This way the user (hopely) sees more clearly how the reference
stealing functions are meant to be used.
2010-05-20 18:45:24 +03:00
Petri Lehtinen
453e4c0aa2 Zero the visited flag after an encoding error
When encoding an array or object ends in an error, the visited flag
wasn't zeroed, causing subsequent encoding attempts to fail. This
patch fixes the problem by always zeroing the visited flag.
2010-05-14 09:23:35 +03:00
Petri Lehtinen
2630980f49 Zero the visited flag after encoding an empty array or object
Encoding an empty array or object worked, but encoding it again
(possibly after adding some items) failed, because the visited flag
(used for detecting circular references) wasn't zeroed.
2010-05-14 09:05:56 +03:00
Petri Lehtinen
782acfe378 Merge branch '1.2' 2010-05-07 07:36:01 +03:00
Petri Lehtinen
f9475f9577 load.c: Make stream_init() static 2010-05-07 07:35:11 +03:00
Petri Lehtinen
8857aeadfd Merge branch '1.2'
Conflicts:
	CHANGES
	configure.ac
	doc/conf.py
2010-04-03 14:00:59 +03:00
Petri Lehtinen
4e63fcd55d Merge branch '1.2'
Conflicts:
	configure.ac
2010-03-28 21:44:41 +03:00
Petri Lehtinen
49880cbabe Merge branch '1.2' 2010-03-23 08:15:19 +02:00
Petri Lehtinen
66a69f3f10 Ignore temporary files *~ 2010-03-19 08:11:48 +02:00
Andres Freund
7d5982e6fe c++ wrapper: add missing 'inline' statements to various constructors
the missing 'inline' leads to duplicated symbols if the header is
included into two separately compiled files.
2010-03-18 14:35:09 +01:00
Petri Lehtinen
42621370c3 hashtable: Fix typo in comment 2010-02-11 21:17:19 +02:00
Petri Lehtinen
8e61b7c0f0 Merge branch 'c++-enhance-proxies' 2010-02-11 21:06:19 +02:00
Petri Lehtinen
35ddd2de20 Update CHANGES, change version to 1.2+ 2010-02-11 20:55:56 +02:00
Petri Lehtinen
f18ef5144a Implement JSON_PRESERVE_ORDER encoding flag
With this encoding flag, the object key-value pairs in output are in
the same order in which they were first inserted into the object.

To make this possible, a key of an object is now a serial number plus
a string. An object keeps an increasing counter which is used to
assign serial number to the keys. Hashing, comparison and public API
functions were changed to act only on the string part, i.e. the serial
number is ignored everywhere else but in the encoder, where it's used
to order object keys if JSON_PRESERVE_ORDER flag is used.
2010-02-11 20:48:56 +02:00
Petri Lehtinen
307167fb66 Optimize hashtable_set()
If a key already exists in the hashtable, use the existing pair
changing its value instead of removing the old one and allocating a
new pair.
2010-02-09 20:51:25 +02:00
Petri Lehtinen
7e8b128740 C++: Optimize PropertyProxy
When the property already exists in the object, we can store an
iterator pointing to that property, instead of duplicating the key.

When the property (key) is not present in the object, we still have to
duplicate the key.
2010-02-08 20:51:09 +02:00
Petri Lehtinen
acec2559a5 C++: Make proxies safer
If a user happens to store an ElementProxy or a PropertyProxy
instance, we need to take a reference to the JSON value they point to.
With PropertyProxy, the key needs to be copied as well.
2010-02-07 14:08:54 +02:00
Petri Lehtinen
8d75235ff2 Merge branch '1.2'
Conflicts:
	LICENSE
2010-02-04 21:13:57 +02:00
Petri Lehtinen
79e9dae9a0 Merge branch 'cleanup-c++-code' 2010-02-04 21:12:36 +02:00
Petri Lehtinen
f021ba00a2 C++: Fix test_cpp.cpp to work with VPATH builds
It reads an input file, and the file location is different with VPATH
builds. Read top_srcdir from environment and use it to find the file.
2010-02-04 21:10:04 +02:00
Petri Lehtinen
adb1b58627 C++: Add Value::dump_file(), load_file() and loads() that take an std::string 2010-02-04 21:08:42 +02:00
Petri Lehtinen
b8059a1880 C++: Rename some functions to better match the C API
Value::save_file -> Value::dump_file
Value::save_string -> Value::dumps
load_string -> loads
2010-02-04 21:08:38 +02:00
Petri Lehtinen
49d40f020b C++: #include <cstdio> in jansson.hpp
This is to avoid standard C functions ending up in namespace json, as
jansson.h is #included in there, and jansson.h in turn #includes
stdio.h.
2010-02-04 20:50:02 +02:00
Petri Lehtinen
910a2f318b C++: Rename test.json to test_cpp.json 2010-02-04 20:49:01 +02:00
Petri Lehtinen
c9fc055351 Add myself as another copyright holder for jansson.hpp and jansson.ipp 2010-02-02 21:14:50 +02:00
Petri Lehtinen
d1a0c3ffc2 C++: Rename jansson-impl.hpp to jansson.ipp
The .ipp suffix is for inlined template implementation code.

While at it, use #ifdef and #ifndef instead of #if defined().
2010-02-02 21:10:58 +02:00
Petri Lehtinen
b07e69c37a C++: Rename namespace json::_private to json::detail 2010-02-02 21:01:50 +02:00
Petri Lehtinen
2b43e7dbda C++: Untabify, reindent, delete trailing whitespace 2010-02-02 21:00:10 +02:00
Petri Lehtinen
5b1a666cf1 test/suites/api: Detect tests correctly
The C++ test case didn't work correctly in VPATH builds or with
VALGRIND=1.
2010-02-02 20:37:02 +02:00
Petri Lehtinen
b495b96547 Add functions json_object_iter_{at,set,set_new} 2010-02-01 21:07:19 +02:00
Petri Lehtinen
72e3948438 Merge branch '1.2' 2010-01-28 21:05:19 +02:00
Petri Lehtinen
04f7e27877 Update LICENSE 2010-01-21 22:31:06 +02:00
Petri Lehtinen
3dd29366b8 Merge branch 'c++-api' 2010-01-21 22:29:26 +02:00
Sean Middleditch
38950b081c add meaningful copyright to jansson-impl.hpp too 2010-01-18 21:55:41 -08:00
Sean Middleditch
56687e9b56 add meaningful copyright to jansson.hpp 2010-01-18 21:55:25 -08:00
Sean Middleditch
c9b33e3386 integrate jansson.hpp into build and test suite 2010-01-18 21:36:02 -08:00
Sean Middleditch
2ad4634de5 Merge branch 'master' of /home/elanthis/Source/janssonxx
Conflicts:
	.gitignore
2010-01-18 21:26:10 -08:00
Sean Middleditch
e080667729 replace json::from() with explicit Value() constructors 2010-01-18 19:24:25 -08:00
Sean Middleditch
ef6c35ae1b move static functions out of Value, add test driver to ensure linking works properly 2010-01-18 18:50:13 -08:00
Sean Middleditch
95bf762eeb rename jansson namespace to json 2010-01-18 18:37:13 -08:00
Sean Middleditch
dd36e4e838 rename files to match upstream's preferences 2010-01-16 20:27:13 -08:00
Sean Middleditch
df35adc438 add comments noting inefficiency of stream ops 2010-01-16 20:21:52 -08:00
Sean Middleditch
f88a5a0e6b added a couple minor comments 2010-01-16 20:17:48 -08:00
Sean Middleditch
cc06bc334a cleanup code 2010-01-16 20:15:33 -08:00
Sean Middleditch
2dc2b6bab7 rename ArrayProxy to ElementProxy and ObjectProxy to PropertyProxy 2010-01-16 01:40:16 -08:00
Sean Middleditch
49a64a6edf rename and move the _* private classes to _private namespace 2010-01-16 01:36:13 -08:00
Sean Middleditch
f0be52f9f8 add object property proxy support 2010-01-16 01:31:37 -08:00
Sean Middleditch
1bc0225441 add array element proxy support 2010-01-16 01:24:27 -08:00
Sean Middleditch
87df8bb0fe templatize janssonxx functionality to prepare for proxy setters 2010-01-16 01:13:19 -08:00
Sean Middleditch
69437a7183 dont attempt to create a std::string from NULL in as_string() 2010-01-13 18:35:07 -08:00
Sean Middleditch
63f762bc48 save flags default to 0 2010-01-13 18:34:17 -08:00
Sean Middleditch
5a0efe6536 add a safeguard against NULL return output stream 2010-01-13 18:33:19 -08:00
Sean Middleditch
01759517aa add Value::from(float) 2010-01-13 18:32:44 -08:00
Sean Middleditch
17805e5829 insert and remove methods 2010-01-12 16:17:11 -08:00
Sean Middleditch
492d95329a add proper attribution to janssonxx.h 2010-01-12 16:07:57 -08:00
Sean Middleditch
7ba18d3f0a use different temporaries in tests instead of reusing e3 2010-01-12 16:03:27 -08:00
Sean Middleditch
7d09af38c1 remove auto type conversion on array/object assignment 2010-01-12 15:38:47 -08:00
Sean Middleditch
8d3a9e347c rename as_json_t to as_json 2010-01-12 15:33:36 -08:00
Sean Middleditch
f79a81dad9 add (ugly) stream support 2010-01-12 15:29:45 -08:00
Sean Middleditch
b98e9d180c rename the set() methods 2010-01-12 15:14:57 -08:00
Sean Middleditch
8d5d2a93d5 remove some unnecessary checks 2010-01-12 04:30:02 -08:00
Sean Middleditch
d77c2e3fb0 cleanup the take_ownership function a bit 2010-01-12 04:26:30 -08:00
Sean Middleditch
7ef3202f83 added save_file and save_string methods 2010-01-12 04:20:17 -08:00
Sean Middleditch
36085ab49a include jansson.h inside the jansson namespace 2010-01-12 04:13:26 -08:00
Sean Middleditch
f743c4ee7f test object property assignment and clear 2010-01-12 01:41:17 -08:00
Sean Middleditch
c994eddec4 expand array assignment tests 2010-01-12 01:34:58 -08:00
Sean Middleditch
5a20e2695b add link to Jansson web to README 2010-01-12 01:31:08 -08:00
Sean Middleditch
cd18aa97f0 added README 2010-01-12 01:29:44 -08:00
Sean Middleditch
bd09127859 cleaner assignment behavior 2010-01-12 01:26:47 -08:00
Sean Middleditch
6818c117ee ignore test-bin output file 2010-01-12 01:10:38 -08:00
Sean Middleditch
39601c183a add tests 2010-01-12 01:10:20 -08:00
Sean Middleditch
1e3b41e8ea initial commit of janssonxx.h 2010-01-12 01:10:09 -08:00
23 changed files with 464 additions and 65 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*~
*.o
*.a
.libs

25
CHANGES
View File

@@ -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
=============

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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
View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View 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} \

View 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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
export JSON_PRESERVE_ORDER=1

View File

@@ -0,0 +1 @@
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}

View File

@@ -0,0 +1 @@
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}