62 Commits

Author SHA1 Message Date
Petri Lehtinen
842bc2128b jansson 1.1.2 2009-11-08 14:13:32 +02:00
Petri Lehtinen
ca6d26a1c2 Don't include stdint.h in jansson.h
It's not needed
2009-11-07 14:45:46 +02:00
Petri Lehtinen
d3959a8ce7 load: Parse a badly put - sign correctly
Thanks to Manolis Delakis for reporting.
2009-10-29 15:45:16 +02:00
Petri Lehtinen
f243930b68 json_load_file: Initialize the error struct properly
Failing to do this has the effect that the error message is not
returned when the input file cannot be opened (e.g. if it doesn't
exist).

Thanks to Martin Vopatek for reporting.
2009-10-27 17:56:02 +02:00
Petri Lehtinen
15d992cb6a jansson 1.1.1 2009-10-26 21:27:10 +02:00
Petri Lehtinen
59c58ea26c Build documentation in distcheck-hook
This is to check that all the documentation files are distributed
before releasing.
2009-10-25 00:20:23 +03:00
Petri Lehtinen
f95bb423a3 Really distribute all the docs
The tutorial example github_commits.c was still left out.
2009-10-25 00:17:16 +03:00
Petri Lehtinen
9448ed3ef7 Distribute all the docs 2009-10-24 14:16:12 +03:00
Petri Lehtinen
7c7a1ed01e Fix version 1.1 release date in CHANGES 2009-10-24 14:16:11 +03:00
Petri Lehtinen
325101e525 jansson 1.1 2009-10-20 21:29:16 +03:00
Petri Lehtinen
1b02f3546c Distribute stripped unit test data
Apparently, it was forgotten to add these files to
test/testdata/Makefile.am in commit
04d550b02e.
2009-10-20 21:23:49 +03:00
Petri Lehtinen
cd96049d10 Enhance documentation
- Add more information to the documentation front page
- Document how to build the documentation
- Clarify the tutorial a bit and remove some quirks
- Bring README.rst up-to-date
- Small enhancements here and there
2009-10-19 21:56:11 +03:00
raoulh
a83cd30c31 Add C++ guards to jansson.h
Signed-off-by: Petri Lehtinen <petri@digip.org>
2009-10-19 12:22:21 +03:00
Petri Lehtinen
a00988f663 doc: Add tutorial 2009-10-17 14:18:40 +03:00
Petri Lehtinen
86dc1d629b Fix indentation
No functional changes.
2009-10-16 21:20:38 +03:00
Petri Lehtinen
b67e130fda json_dumpf: Document the output shortage on error 2009-10-15 21:06:59 +03:00
Petri Lehtinen
4cd777712b Enhance handling of circular references
It's now an error to try to add an object or array to itself. The
encoder checks for circular references and fails with an error status
if one is detected.
2009-10-15 21:02:27 +03:00
Petri Lehtinen
79009e62c1 json_dumps: Close the strbuffer if dumping fails 2009-10-15 20:54:32 +03:00
Petri Lehtinen
76999799ed doc: Fix a small typo in apiref 2009-10-15 17:39:49 +03:00
Petri Lehtinen
22af193a51 doc/Makefile.am: Remove *.pyc in clean 2009-10-14 22:09:14 +03:00
Petri Lehtinen
951d091f07 Make integer, real and string mutable
Added functions:

  json_string_set
  json_integer_set
  json_real_set

While at it, clarify the documentation and parameter naming of
json_{string,integer,real}_value() a bit.
2009-10-14 08:23:02 +03:00
Petri Lehtinen
185e107d24 Don't use non-portable asprintf()
Thanks to Adam Strzelecki for reporting.
2009-10-13 15:40:26 +03:00
Petri Lehtinen
ca7703fbd1 Merge branch '1.0'
Conflicts:
	configure.ac
	doc/conf.py
2009-10-11 21:51:54 +03:00
Petri Lehtinen
12cd4e8c09 jansson 1.0.4 2009-10-11 21:38:42 +03:00
Petri Lehtinen
bad16ea52a Fix API tests for object
Because of a typo in test/testprogs/Makefile.am, the tests for object
were never compiled or run.
2009-10-11 21:38:42 +03:00
Petri Lehtinen
30015bde90 Remove config.h.in
It doesn't have to be in version control.
2009-10-11 21:38:41 +03:00
Petri Lehtinen
1e00cd58a5 Extend object API
Added functions:

  json_object_size
  json_object_clear
  json_object_update
2009-10-11 20:44:01 +03:00
Petri Lehtinen
40bb7bf437 Fix API tests for object
Because of a typo in test/testprogs/Makefile.am, the tests for object
were never compiled or run.
2009-10-10 22:55:15 +03:00
Petri Lehtinen
9d648a87cb Check json_object_set_new arguments
A segfault was caused by NULL key.
2009-10-10 22:53:36 +03:00
Petri Lehtinen
8de850be95 Remove config.h.in
It doesn't have to be in version control.
2009-10-10 21:21:22 +03:00
Petri Lehtinen
19588c2d69 Fix a few more compilation issues
These were left out from the previous commit.
2009-10-06 13:22:27 +03:00
Petri Lehtinen
9c5a8430db Make it compile on platforms where char is unsigned
Linux on powerpc seems to be one such platform.
2009-10-01 21:54:45 +03:00
Petri Lehtinen
afc9c1a23a Relax Autoconf version prereq
From 2.63 to 2.59, which is more widely supported.
2009-10-01 21:54:29 +03:00
Petri Lehtinen
cbacac5975 Extend array API
Added functions:

  json_array_insert
  json_array_insert_new
  json_array_remove
  json_array_clear
  json_array_extend
2009-09-29 22:17:58 +03:00
Petri Lehtinen
b3e1fe2ec5 doc: Add getting started guide 2009-09-20 21:56:12 +03:00
Petri Lehtinen
7728716759 Merge branch '1.0'
Conflicts:
	configure.ac
	doc/conf.py
2009-09-14 15:06:06 +03:00
Petri Lehtinen
b7bf96996f jansson 1.0.3 2009-09-14 14:32:41 +03:00
Petri Lehtinen
6d8c287032 load: Check for integer and real overlfows and underflows
Backported from master, commit 5406c2b3d3:
  * deleted test/testdata/invalid-stripped because the stripped tests
    don't exist in 1.0
2009-09-14 10:28:03 +03:00
Petri Lehtinen
ab3764ed0a test/json-compare.py: Use json module from Python >=2.6 or simplejson
Backported from master, commit 9d16ec755c
2009-09-14 10:27:51 +03:00
Petri Lehtinen
5406c2b3d3 load: Check for integer and real overlfows and underflows 2009-09-13 13:18:01 +03:00
Petri Lehtinen
743af38e7f Use unsigned long instead of uint32_t
Some day we will have ANSI C compatibility... This change doesn't make
the API backwards incompatible because uint32_t was only used in flags
to json_dump*() and the flags are meant to be used only by ORing
constants and macro output, and actually currently only JSON_INDENT
can be used.
2009-09-13 13:16:34 +03:00
Petri Lehtinen
9d16ec755c test/json-compare.py: Use json module from Python >=2.6 or simplejson 2009-09-11 22:22:34 +03:00
Petri Lehtinen
04d550b02e Add stripped unit tests for decoder
That is, test cases where there's no newline or other whitespace at
the beginning or end of input. This was implemented by adding a
--strip option to split-testfile to strip the input file after writing
it.

The actual test JSON texts are the same as testdata/invalid and
testdata/valid. The expected output of the invalid cases had to be
adjusted a bit: because there's no newline at the end, some of the
line numbers needed to be changed.
2009-09-08 17:03:01 +03:00
Petri Lehtinen
55d2566539 Merge branch '1.0' into HEAD
Conflicts:
	configure.ac
	doc/conf.py
2009-09-08 17:02:39 +03:00
Petri Lehtinen
9cc6fbe580 Distribute CHANGES 2009-09-08 16:57:45 +03:00
Petri Lehtinen
827a01937f jansson 1.0.2 2009-09-08 16:48:26 +03:00
Petri Lehtinen
0f62dac627 load: Handle EOF correctly
In stream_get(), EOF never got it to stream->buffer and because of
this, stream_unget() failed on some situations. This patch makes
stream_get() handle EOF just like any other byte.

As a "side effect", lex_scan_string() now needs to unget the EOF, or
otherwise it ends up in error message on premature end of input.
2009-09-08 16:41:07 +03:00
Petri Lehtinen
7ee974e91c Don't perform reference counting on true, false and null
This makes their constructors thread safe. A special reference count
-1 is used to test whether to perform reference counting on a value or
not.
2009-09-07 21:40:14 +03:00
Petri Lehtinen
c288188a0f test/.gitignore: Add testprogs/test_simple 2009-09-07 21:25:22 +03:00
Petri Lehtinen
057ba29a27 doc apiref: Enhancements
Constructors are described more clearly and return values on error
situations more compehensively. Also add an example of the object
iteration protocol.
2009-09-07 21:09:55 +03:00
Petri Lehtinen
234ee47281 Better argument validation
All pointer arguments are now tested for NULL. json_string() now also
tests that strdup() succeeds. This is to ensure that no NULL values
end up in data structures.

Also desribe the different sources of errors in documentation.
2009-09-06 22:24:55 +03:00
Petri Lehtinen
98a8c1aebf Don't include unistd.h
It's not needed.
2009-09-06 22:24:22 +03:00
Petri Lehtinen
42e546d065 doc apiref: json_incref() returns its argument 2009-09-06 22:16:38 +03:00
Petri Lehtinen
6ffb04f0d4 Expand test coverage
Now all public API functions are tested (at least on some level) in
the test-api suite.
2009-09-06 14:06:02 +03:00
Petri Lehtinen
fd259ff68c Merge branch '1.0'
Conflicts:
	configure.ac
	doc/conf.py
2009-09-06 12:53:38 +03:00
Petri Lehtinen
ab2d93b724 Add CHANGES 2009-09-06 12:45:47 +03:00
Petri Lehtinen
9611780907 dump: Optimize indenting
Don't alloca() a whitespace buffer and fill it with spaces in each
call to dump_indent. Instead, use a static whitespace buffer.

As a bonus, this saves the use of poorly portable alloca().
2009-09-04 09:52:40 +03:00
Petri Lehtinen
93c5892bc3 load: Factor out an unneeded strdup
By "stealing" the string parsed out in lexer, one strdup can be saved.
2009-09-04 09:52:40 +03:00
Petri Lehtinen
1095ca956b Don't define -std=c99 in AM_CFLAGS
It is not needed.
2009-09-04 09:52:40 +03:00
Petri Lehtinen
05654bfcba doc: Fix typo 2009-09-04 09:52:40 +03:00
Petri Lehtinen
89d09813c3 Add reference stealing functions for inserting values to objects and arrays
The non-stealing functions are now just simple wrappers around these.
2009-09-04 09:52:37 +03:00
Petri Lehtinen
5ac4914642 Change the version to 1.0+ 2009-09-02 23:10:46 +03:00
38 changed files with 2507 additions and 304 deletions

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@ aclocal.m4
autom4te.cache
config.guess
config.h
config.h.in
config.log
config.status
config.sub

64
CHANGES Normal file
View File

@@ -0,0 +1,64 @@
Version 1.1.2, released 2009-11-08
* Fix a bug where an error message was not produced if the input file
could not be opened in json_load_file()
* Fix an assertion failure in decoder caused by a minus sign without a
digit after it
* Remove an unneeded include for stdint.h in jansson.h
Version 1.1.1, released 2009-10-26
* All documentation files were not distributed with v1.1; build
documentation in make distcheck to prevent this in the future
* Fix v1.1 release date in CHANGES
Version 1.1, released 2009-10-20
* API additions and improvements:
- Extend array and object APIs
- Add functions to modify integer, real and string values
- Improve argument validation
- Use unsigned int instead of uint32_t for encoding flags
* Enhance documentation
- Add getting started guide and tutorial
- Fix some typos
- General clarifications and cleanup
* Check for integer and real overflows and underflows in decoder
* Make singleton values thread-safe (true, false and null)
* Enhance circular reference handling
* Don't define -std=c99 in AM_CFLAGS
* Add C++ guards to jansson.h
* Minor performance and portability improvements
* Expand test coverage
Version 1.0.4, released 2009-10-11
* Relax Autoconf version requirement to 2.59
* Make Jansson compile on platforms where plain char is unsigned
* Fix API tests for object
Version 1.0.3, released 2009-09-14
* Check for integer and real overflows and underflows in decoder
* Use the Python json module for tests, or simplejson if the json
module is not found
* Distribute changelog (this file)
Version 1.0.2, released 2009-09-08
* Handle EOF correctly in decoder
Version 1.0.1, released 2009-09-04
* Fixed broken json_is_boolean()
Version 1.0, released 2009-08-25
* Initial release

View File

@@ -1,2 +1,7 @@
EXTRA_DIST = LICENSE README.rst
EXTRA_DIST = CHANGES LICENSE README.rst
SUBDIRS = doc src test
distcheck-hook:
sphinx-build -b html -W \
$(distdir)/doc \
$(distdir)/_build/doc/.build/html

View File

@@ -6,14 +6,14 @@ data. Its main features and design principles are:
- Simple and intuitive API and data model
- Good documentation
- Comprehensive documentation
- No dependencies on other libraries
- Full Unicode support (UTF-8)
- Extensive test suite
- No dependencies on other libraries
Jansson is licensed under the `MIT license`_; see LICENSE in the
source distribution for details.

View File

@@ -1,59 +0,0 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION

View File

@@ -1,5 +1,5 @@
AC_PREREQ([2.63])
AC_INIT([jansson], [1.0.1], [petri@digip.org])
AC_PREREQ([2.59])
AC_INIT([jansson], [1.1.2], [petri@digip.org])
AM_INIT_AUTOMAKE([1.10 foreign])

View File

@@ -1,4 +1,7 @@
EXTRA_DIST = conf.py apiref.rst index.rst ext/refcounting.py
EXTRA_DIST = \
conf.py apiref.rst gettingstarted.rst github_commits.c index.rst \
tutorial.rst ext/refcounting.py
clean-local:
rm -rf .build
rm -f ext/refcounting.pyc

View File

@@ -1,3 +1,5 @@
.. _apiref:
*************
API Reference
*************
@@ -41,6 +43,12 @@ Objects of :ctype:`json_t` are always used through a pointer. There
are APIs for querying the type, manipulating the reference count, and
for constructing and manipulating values of different types.
Unless noted otherwise, all API functions return an error value if an
error occurs. Depending on the function's signature, the error value
is either *NULL* or -1. Invalid arguments or invalid input are
apparent sources for errors. Memory allocation and I/O operations may
also cause errors.
Type
----
@@ -80,8 +88,8 @@ functions:
.. cfunction:: int json_typeof(const json_t *json)
Return the type of the JSON value (a :ctype:`json_type` cast to
:ctype:`int`). This function is actually implemented as a macro for
speed.
:ctype:`int`). *json* MUST NOT be *NULL*. This function is actually
implemented as a macro for speed.
.. cfunction:: json_is_object(const json_t *json)
json_is_array(const json_t *json)
@@ -93,19 +101,22 @@ functions:
json_is_null(const json_t *json)
These functions (actually macros) return true (non-zero) for values
of the given type, and false (zero) for values of other types.
of the given type, and false (zero) for values of other types and
for *NULL*.
.. cfunction:: json_is_number(const json_t *json)
Returns true for values of types :const:`JSON_INTEGER` and
:const:`JSON_REAL`, and false for other types.
:const:`JSON_REAL`, and false for other types and for *NULL*.
.. cfunction:: json_is_boolean(const json_t *json)
Returns true for types :const:`JSON_TRUE` and :const:`JSON_FALSE`,
and false for values of other types.
and false for values of other types and for *NULL*.
.. _apiref-reference-count:
Reference Count
---------------
@@ -121,7 +132,8 @@ The following functions are used to manipulate the reference count.
.. cfunction:: json_t *json_incref(json_t *json)
Increment the reference count of *json*.
Increment the reference count of *json* if it's not non-*NULL*.
Returns *json*.
.. cfunction:: void json_decref(json_t *json)
@@ -151,29 +163,60 @@ will return a new or borrowed reference or steal a reference to its
argument.
Circular References
-------------------
A circular reference is created when an object or an array is,
directly or indirectly, inserted inside itself. The direct case is
simple::
json_t *obj = json_object();
json_object_set(obj, "foo", obj);
Jansson will refuse to do this, and :cfunc:`json_object_set()` (and
all the other such functions for objects and arrays) will return with
an error status. The indirect case is the dangerous one::
json_t *arr1 = json_array(), *arr2 = json_array();
json_array_append(arr1, arr2);
json_array_append(arr2, arr1);
In this example, the array ``arr2`` is contained in the array
``arr1``, and vice versa. Jansson cannot check for this kind of
indirect circular references without a performance hit, so it's up to
the user to avoid them.
If a circular reference is created, the memory consumed by the values
cannot be freed by :cfunc:`json_decref()`. The reference counts never
drops to zero because the values are keeping the circular reference to
themselves. Moreover, trying to encode the values with any of the
encoding functions will fail. The encoder detects circular references
and returns an error status.
True, False and Null
====================
These values are implemented as singletons, so each of these functions
returns the same value each time.
.. cfunction:: json_t *json_true(void)
.. refcounting:: new
Returns a value of the type :const:`JSON_TRUE`, or *NULL* on
error.
Returns the JSON true value.
.. cfunction:: json_t *json_false(void)
.. refcounting:: new
Returns a value of the type :const:`JSON_FALSE`, or *NULL* on
error.
Returns the JSON false value.
.. cfunction:: json_t *json_null(void)
.. refcounting:: new
Returns a value of the type :const:`JSON_NULL`, or *NULL* on
error.
Returns the JSON null value.
String
@@ -183,13 +226,21 @@ String
.. refcounting:: new
Returns a new value of the type :const:`JSON_STRING`, or *NULL* on
error. *value* must be a valid UTF-8 encoded Unicode string.
Returns a new JSON string, or *NULL* on error. *value* must be a
valid UTF-8 encoded Unicode string.
.. cfunction:: const char *json_string_value(const json_t *json)
.. cfunction:: const char *json_string_value(const json_t *string)
Returns the associated value of a :const:`JSON_STRING` value as a
null terminated UTF-8 encoded string.
Returns the associated value of *string* as a null terminated UTF-8
encoded string, or *NULL* if *string* is not a JSON string.
.. cfunction:: int json_string_set(const json_t *string, const char *value)
Sets the associated value of *string* to *value*. *value* must be a
valid UTF-8 encoded Unicode string. Returns 0 on success and -1 on
error.
.. versionadded:: 1.1
Number
@@ -199,33 +250,46 @@ Number
.. refcounting:: new
Returns a new value of the type :const:`JSON_INTEGER`, or *NULL* on
error.
Returns a new JSON integer, or *NULL* on error.
.. cfunction:: int json_integer_value(const json_t *json)
.. cfunction:: int json_integer_value(const json_t *integer)
Returns the associated integer value of values of the type
:const:`JSON_INTEGER`, or 0 for values of other types.
Returns the associated value of *integer*, or 0 if *json* is not a
JSON integer.
.. cfunction:: int json_integer_set(const json_t *integer, int value)
Sets the associated value of *integer* to *value*. Returns 0 on
success and -1 if *integer* is not a JSON integer.
.. versionadded:: 1.1
.. cfunction:: json_t *json_real(double value)
.. refcounting:: new
Returns a new value of the type :const:`JSON_REAL`, or *NULL* on
error.
Returns a new JSON real, or *NULL* on error.
.. cfunction:: double json_real_value(const json_t *json)
.. cfunction:: double json_real_value(const json_t *real)
Returns the associated real value of values of the type
:const:`JSON_INTEGER`, or 0 for values of other types.
Returns the associated value of *real*, or 0.0 if *real* is not a
JSON real.
.. cfunction:: int json_real_set(const json_t *real, double value)
Sets the associated value of *real* to *value*. Returns 0 on
success and -1 if *real* is not a JSON real.
.. versionadded:: 1.1
In addition to the functions above, there's a common query function
for integers and reals:
.. cfunction:: double json_number_value(const json_t *json)
Returns the value of either ``JSON_INTEGER`` or ``JSON_REAL``, cast
to double regardless of the actual type.
Returns the associated value of the JSON integer or JSON real
*json*, cast to double regardless of the actual type. If *json* is
neither JSON real nor JSON integer, 0.0 is returned.
Array
@@ -237,33 +301,90 @@ A JSON array is an ordered collection of other JSON values.
.. refcounting:: new
Returns a new value of the type :const:`JSON_ARRAY`, or *NULL* on
error. Initially, the array is empty.
Returns a new JSON array, or *NULL* on error. Initially, the array
is empty.
.. cfunction:: unsigned int json_array_size(const json_t *array)
Returns the number of elements in *array*.
Returns the number of elements in *array*, or 0 if *array* is NULL
or not a JSON array.
.. cfunction:: json_t *json_array_get(const json_t *array, unsigned int index)
.. refcounting:: borrow
Returns the element in *array* at position *index*, or *NULL* if
*index* is out of range. The valid range for *index* is from 0 to
the return value of :cfunc:`json_array_size()` minus 1.
Returns the element in *array* at position *index*. The valid range
for *index* is from 0 to the return value of
:cfunc:`json_array_size()` minus 1. If *array* is not a JSON array,
if *array* is *NULL*, or if *index* is out of range, *NULL* is
returned.
.. cfunction:: int json_array_set(json_t *array, unsigned int index, json_t *value)
Replaces the element in *array* at position *index* with *value*.
Returns 0 on success, or -1 if *index* is out of range. The valid
range for *index* is from 0 to the return value of
:cfunc:`json_array_size()` minus 1.
The valid range for *index* is from 0 to the return value of
:cfunc:`json_array_size()` minus 1. Returns 0 on success and -1 on
error.
.. cfunction:: int json_array_set_new(json_t *array, unsigned int index, json_t *value)
Like :cfunc:`json_array_set()` but steals the reference to *value*.
This is useful when *value* is newly created and not used after
the call.
.. versionadded:: 1.1
.. cfunction:: int json_array_append(json_t *array, json_t *value)
Appends *value* to the end of *array*, growing the size of *array*
by 1. Returns 0 on success and -1 on error.
.. cfunction:: int json_array_append_new(json_t *array, json_t *value)
Like :cfunc:`json_array_append()` but steals the reference to
*value*. This is useful when *value* is newly created and not used
after the call.
.. versionadded:: 1.1
.. cfunction:: int json_array_insert(json_t *array, unsigned int index, json_t *value)
Inserts *value* to *array* at position *index*, shifting the
elements at *index* and after it one position towards the end of
the array. Returns 0 on success and -1 on error.
.. versionadded:: 1.1
.. cfunction:: int json_array_insert_new(json_t *array, unsigned int index, json_t *value)
Like :cfunc:`json_array_insert()` but steals the reference to
*value*. This is useful when *value* is newly created and not used
after the call.
.. versionadded:: 1.1
.. cfunction:: int json_array_remove(json_t *array, unsigned int index)
Removes the element in *array* at position *index*, shifting the
elements after *index* one position towards the start of the array.
Returns 0 on success and -1 on error.
.. versionadded:: 1.1
.. cfunction:: int json_array_clear(json_t *array)
Removes all elements from *array*. Returns 0 on sucess and -1 on
error.
.. versionadded:: 1.1
.. cfunction:: int json_array_extend(json_t *array, json_t *other_array)
Appends all elements in *other_array* to the end of *array*.
Returns 0 on success and -1 on error.
.. versionadded:: 1.1
Object
======
@@ -275,8 +396,15 @@ Unicode string and the value is any JSON value.
.. refcounting:: new
Returns a new value of the type :const:`JSON_OBJECT`, or *NULL* on
error. Initially, the object is empty.
Returns a new JSON object, or *NULL* on error. Initially, the
object is empty.
.. cfunction:: unsigned int json_object_size(const json_t *object)
Returns the number of elements in *object*, or 0 if *object* is not
a JSON object.
.. versionadded:: 1.1
.. cfunction:: json_t *json_object_get(const json_t *object, const char *key)
@@ -288,9 +416,17 @@ Unicode string and the value is any JSON value.
.. cfunction:: int json_object_set(json_t *object, const char *key, json_t *value)
Set the value of *key* to *value* in *object*. *key* must be a
valid terminated UTF-8 encoded Unicode string. If there already is
a value for *key*, it is replaced by the new value. Returns 0 on
success and -1 on error.
valid null terminated UTF-8 encoded Unicode string. If there
already is a value for *key*, it is replaced by the new value.
Returns 0 on success and -1 on error.
.. cfunction:: int json_object_set_new(json_t *object, const char *key, json_t *value)
Like :cfunc:`json_object_set()` but steals the reference to
*value*. This is useful when *value* is newly created and not used
after the call.
.. versionadded:: 1.1
.. cfunction:: int json_object_del(json_t *object, const char *key)
@@ -298,6 +434,21 @@ Unicode string and the value is any JSON value.
-1 if *key* was not found.
.. cfunction:: int json_object_clear(json_t *object)
Remove all elements from *object*. Returns 0 on success and -1 if
*object* is not a JSON object.
.. versionadded:: 1.1
.. cfunction:: int json_object_update(json_t *object, json_t *other)
Update *object* with the key-value pairs from *other*, overwriting
existing keys. Returns 0 on success or -1 on error.
.. versionadded:: 1.1
The following functions implement an iteration protocol for objects:
.. cfunction:: void *json_object_iter(json_t *object)
@@ -321,6 +472,20 @@ The following functions implement an iteration protocol for objects:
Extract the associated value from *iter*.
The iteration protocol can be used for example as follows::
/* obj is a JSON object */
const char *key;
json_t *value;
void *iter = json_object_iter(obj);
while(iter)
{
key = json_object_iter_key(iter);
value = json_object_iter_value(iter);
/* use key and value ... */
iter = json_object_iter_next(obj, iter);
}
Encoding
========
@@ -343,18 +508,21 @@ can be ORed together to obtain *flags*.
The following functions perform the actual JSON encoding. The result
is in UTF-8.
.. cfunction:: char *json_dumps(const json_t *root, uint32_t flags)
.. cfunction:: char *json_dumps(const json_t *root, unsigned long flags)
Returns the JSON representation of *root* as a string, or *NULL* on
error. *flags* is described above. The return value must be freed
by the caller using :cfunc:`free()`.
.. cfunction:: int json_dumpf(const json_t *root, FILE *output, uint32_t flags)
.. cfunction:: int json_dumpf(const json_t *root, FILE *output, unsigned long flags)
Write the JSON representation of *root* to the stream *output*.
*flags* is described above. Returns 0 on success and -1 on error.
If an error occurs, something may have already been written to
*output*. In this case, the output is undefined and most likely not
valid JSON.
.. cfunction:: int json_dump_file(const json_t *json, const char *path, uint32_t flags)
.. cfunction:: int json_dump_file(const json_t *json, const char *path, unsigned long flags)
Write the JSON representation of *root* to the file *path*. If
*path* already exists, it is overwritten. *flags* is described

View File

@@ -50,9 +50,9 @@ copyright = u'2009, Petri Lehtinen'
# built documents.
#
# The short X.Y version.
version = '1.0'
version = '1.1'
# The full version, including alpha/beta/rc tags.
release = '1.0.1'
release = '1.1.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

104
doc/gettingstarted.rst Normal file
View File

@@ -0,0 +1,104 @@
***************
Getting Started
***************
.. highlight:: c
Compiling and Installing Jansson
================================
The Jansson source is available at
http://www.digip.org/jansson/releases/.
Unpack the source tarball and change to the source directory:
.. parsed-literal::
bunzip2 -c jansson-|release|.tar.bz2 | tar xf -
cd jansson-|release|
The source uses GNU Autotools (autoconf_, automake_, libtool_), so
compiling and installing is extremely simple::
./configure
make
make check
make install
To change the destination directory (``/usr/local`` by default), use
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
--help`` for the list of all possible installation options. (There are
no options to customize the resulting Jansson binary.)
The command ``make check`` runs the test suite distributed with
Jansson. Python_ is required to run the tests. This step is not
strictly necessary, but it may find possible problems that Jansson has
on your platform. If any problems are found, please report them.
If you obtained the source from a Git repository (or any other source
control system), there's no ``./configure`` script as it's not kept in
version control. To create the script, Autotools needs to be
bootstrapped. There are many ways to do this, but the easiest one is
to use ``autoreconf``::
autoreconf -vi
This command creates the ``./configure`` script, which can then be
used as described in the previous section.
.. _autoconf: http://www.gnu.org/software/autoconf/
.. _automake: http://www.gnu.org/software/automake/
.. _libtool: http://www.gnu.org/software/libtool/
.. _Python: http://www.python.org/
Installing Prebuilt Binary Packages
-----------------------------------
Binary ``.deb`` packages for Ubuntu are available in `this PPA`_ at
Launchpad_. Follow the instructions in the PPA ("Technical details
about this PPA" link) to take the PPA into use. Then install the -dev
package::
sudo apt-get install libjansson-dev
.. _this PPA: http://launchpad.net/~petri/+archive/ppa
.. _Launchpad: http://launchpad.net/
Building the Documentation
--------------------------
(This subsection describes how to build the HTML documentation you are
currently reading, so it can be safely skipped.)
Documentation is in the ``doc/`` subdirectory. It's written in
reStructuredText_ with Sphinx_ annotations. To generate the HTML
documentation, invoke::
cd doc/
sphinx-build . .build/html
... and point your browser to ``.build/html/index.html``. Sphinx_ is
required to generate the documentation.
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _Sphinx: http://sphinx.pocoo.org/
Compiling Programs Using Jansson
================================
Jansson involves one C header file, :file:`jansson.h`, so it's enough
to put the line
::
#include <jansson.h>
in the beginning of every source file that uses Jansson.
There's also just one library to link with, ``libjansson``. Compile and
link the program as follows::
cc -o prog prog.c -ljansson

164
doc/github_commits.c Normal file
View File

@@ -0,0 +1,164 @@
#include <stdlib.h>
#include <string.h>
#include <jansson.h>
#include <curl/curl.h>
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
#define URL_SIZE 256
/* Return the offset of the first newline in text or the length of
text if there's no newline */
static int newline_offset(const char *text)
{
const char *newline = strchr(text, '\n');
if(!newline)
return strlen(text);
else
return (int)(newline - text);
}
struct write_result
{
char *data;
int pos;
};
static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
{
struct write_result *result = (struct write_result *)stream;
if(result->pos + size * nmemb >= BUFFER_SIZE - 1)
{
fprintf(stderr, "error: too small buffer\n");
return 0;
}
memcpy(result->data + result->pos, ptr, size * nmemb);
result->pos += size * nmemb;
return size * nmemb;
}
static char *request(const char *url)
{
CURL *curl;
CURLcode status;
char *data;
long code;
curl = curl_easy_init();
data = malloc(BUFFER_SIZE);
if(!curl || !data)
return NULL;
struct write_result write_result = {
.data = data,
.pos = 0
};
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
status = curl_easy_perform(curl);
if(status != 0)
{
fprintf(stderr, "error: unable to request data from %s:\n", url);
fprintf(stderr, "%s\n", curl_easy_strerror(status));
return NULL;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if(code != 200)
{
fprintf(stderr, "error: server responded with code %ld\n", code);
return NULL;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
/* zero-terminate the result */
data[write_result.pos] = '\0';
return data;
}
int main(int argc, char *argv[])
{
unsigned int i;
char *text;
char url[URL_SIZE];
json_t *root;
json_error_t error;
json_t *commits;
if(argc != 3)
{
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
return 2;
}
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
text = request(url);
if(!text)
return 1;
root = json_loads(text, &error);
free(text);
if(!root)
{
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
return 1;
}
commits = json_object_get(root, "commits");
if(!json_is_array(commits))
{
fprintf(stderr, "error: commits is not an array\n");
return 1;
}
for(i = 0; i < json_array_size(commits); i++)
{
json_t *commit, *id, *message;
const char *message_text;
commit = json_array_get(commits, i);
if(!json_is_object(commit))
{
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
return 1;
}
id = json_object_get(commit, "id");
if(!json_is_string(id))
{
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
return 1;
}
message = json_object_get(commit, "message");
if(!json_is_string(message))
{
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
return 1;
}
message_text = json_string_value(message);
printf("%.8s %.*s\n",
json_string_value(id),
newline_offset(message_text),
message_text);
}
json_decref(root);
return 0;
}

View File

@@ -1,15 +1,39 @@
Overview
========
Jansson Documentation
=====================
This is the documentation for Jansson_ |release|, last updated |today|.
Introduction
------------
Jansson_ is a C library for encoding, decoding and manipulating JSON
data. Its main features and design principles are:
- Simple and intuitive API and data model
- Comprehensive documentation
- No dependencies on other libraries
- Full Unicode support (UTF-8)
- Extensive test suite
Jansson is licensed under the `MIT license`_; see LICENSE in the
source distribution for details.
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
.. _Jansson: http://www.digip.org/jansson/
**Contents:**
Contents
--------
.. toctree::
:maxdepth: 2
gettingstarted
tutorial
apiref

275
doc/tutorial.rst Normal file
View File

@@ -0,0 +1,275 @@
.. _tutorial:
********
Tutorial
********
.. highlight:: c
In this tutorial, we create a program that fetches the latest commits
of a repository in GitHub_ over the web. One of the response formats
supported by `GitHub API`_ is JSON, so the result can be parsed using
Jansson.
To stick to the the scope of this tutorial, we will only cover the the
parts of the program related to handling JSON data. For the best user
experience, the full source code is available:
:download:`github_commits.c`. To compile it (on Unix-like systems with
gcc), use the following command::
gcc -o github_commits github_commits.c -ljansson -lcurl
libcurl_ is used to communicate over the web, so it is required to
compile the program.
The command line syntax is::
github_commits USER REPOSITORY
``USER`` is a GitHub user ID and ``REPOSITORY`` is the repository
name. Please note that the GitHub API is rate limited, so if you run
the program too many times within a short period of time, the sever
starts to respond with an error.
.. _GitHub: http://github.com/
.. _GitHub API: http://develop.github.com/
.. _libcurl: http://curl.haxx.se/
.. _tutorial-github-commits-api:
The GitHub Commits API
======================
The `GitHub commits API`_ is used by sending HTTP requests to URLs
starting with ``http://github.com/api/v2/json/commits/``. Our program
only lists the latest commits, so the rest of the URL is
``list/USER/REPOSITORY/BRANCH``, where ``USER``, ``REPOSITORY`` and
``BRANCH`` are the GitHub user ID, the name of the repository, and the
name of the branch whose commits are to be listed, respectively.
GitHub responds with a JSON object of the following form:
.. code-block:: none
{
"commits": [
{
"id": "<the commit ID>",
"message": "<the commit message>",
<more fields, not important to this tutorial>
},
{
"id": "<the commit ID>",
"message": "<the commit message>",
<more fields, not important to this tutorial>
},
<more commits...>
]
}
In our program, the HTTP request is sent using the following
function::
static char *request(const char *url);
It takes the URL as a parameter, preforms a HTTP GET request, and
returns a newly allocated string that contains the response body. If
the request fails, an error message is printed to stderr and the
return value is *NULL*. For full details, refer to :download:`the code
<github_commits.c>`, as the actual implementation is not important
here.
.. _GitHub commits API: http://develop.github.com/p/commits.html
.. _tutorial-the-program:
The Program
===========
First the includes::
#include <string.h>
#include <jansson.h>
Like all the programs using Jansson, we need to include
:file:`jansson.h`.
The following definitions are used to build the GitHub commits API
request URL::
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
#define URL_SIZE 256
The following function is used when formatting the result to find the
first newline in the commit message::
/* Return the offset of the first newline in text or the length of
text if there's no newline */
static int newline_offset(const char *text)
{
const char *newline = strchr(text, '\n');
if(!newline)
return strlen(text);
else
return (int)(newline - text);
}
The main function follows. In the beginning, we first declare a bunch
of variables and check the command line parameters::
unsigned int i;
char *text;
char url[URL_SIZE];
json_t *root;
json_error_t error;
json_t *commits;
if(argc != 3)
{
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
return 2;
}
Then we build the request URL using the user and repository names
given as command line parameters::
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above.
Now we're ready to actually request the JSON data over the web::
text = request(url);
if(!text)
return 1;
If an error occurs, our function ``request`` prints the error and
returns *NULL*, so it's enough to just return 1 from the main
function.
Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
as a response::
root = json_loads(text, &error);
free(text);
if(!root)
{
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
return 1;
}
We don't need the JSON text anymore, so we can free the ``text``
variable right after decoding it. If :cfunc:`json_loads()` fails, it
returns *NULL* and sets error information to the :ctype:`json_error_t`
structure given as the second parameter. In this case, our program
prints the error information out and returns 1 from the main function.
Now we're ready to extract the data out of the decoded JSON response.
The structure of the response JSON was explained in section
:ref:`tutorial-github-commits-api`.
First, we'll extract the ``commits`` array from the JSON response::
commits = json_object_get(root, "commits");
if(!json_is_array(commits))
{
fprintf(stderr, "error: commits is not an array\n");
return 1;
}
This is the array that contains objects describing latest commits in
the repository. We check that the returned value really is an array.
If the key ``commits`` doesn't exist, :cfunc:`json_object_get()`
returns *NULL*, but :cfunc:`json_is_array()` handles this case, too.
Then we proceed to loop over all the commits in the array::
for(i = 0; i < json_array_size(commits); i++)
{
json_t *commit, *id, *message;
const char *message_text;
commit = json_array_get(commits, i);
if(!json_is_object(commit))
{
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
return 1;
}
...
The function :cfunc:`json_array_size()` returns the size of a JSON
array. First, we again declare some variables and then extract the
i'th element of the ``commits`` array using :cfunc:`json_array_get()`.
We also check that the resulting value is a JSON object.
Next we'll extract the commit ID and commit message, and check that
they both are JSON strings::
id = json_object_get(commit, "id");
if(!json_is_string(id))
{
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
return 1;
}
message = json_object_get(commit, "message");
if(!json_is_string(message))
{
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
return 1;
}
...
And finally, we'll print the first 8 characters of the commit ID and
the first line of the commit message. A C-style string is extracted
from a JSON string using :cfunc:`json_string_value()`::
message_text = json_string_value(message);
printf("%.8s %.*s\n",
json_string_value(id),
newline_offset(message_text),
message_text);
}
After sending the HTTP request, we decoded the JSON text using
:cfunc:`json_loads()`, remember? It returns a *new reference* to the
JSON value it decodes. When we're finished with the value, we'll need
to decrease the reference count using :cfunc:`json_decref()`. This way
Jansson can release the resources::
json_decref(root);
return 0;
For a detailed explanation of reference counting in Jansson, see
:ref:`apiref-reference-count` in :ref:`apiref`.
The program's ready, let's test it and view the latest commits in
Jansson's repository::
$ ./github_commits akheron jansson
86dc1d62 Fix indentation
b67e130f json_dumpf: Document the output shortage on error
4cd77771 Enhance handling of circular references
79009e62 json_dumps: Close the strbuffer if dumping fails
76999799 doc: Fix a small typo in apiref
22af193a doc/Makefile.am: Remove *.pyc in clean
951d091f Make integer, real and string mutable
185e107d Don't use non-portable asprintf()
ca7703fb Merge branch '1.0'
12cd4e8c jansson 1.0.4
<etc...>
Conclusion
==========
In this tutorial, we implemented a program that fetches the latest
commits of a GitHub repository using the GitHub commits API. Jansson
was used to decode the JSON response and to extract the commit data.
This tutorial only covered a small part of Jansson. For example, we
did not create or manipulate JSON values at all. Proceed to
:ref:`apiref` to explore all features of Jansson.

View File

@@ -13,6 +13,6 @@ libjansson_la_SOURCES = \
utf.h \
util.h \
value.c
libjansson_la_LDFLAGS = -version-info 0:1:0
libjansson_la_LDFLAGS = -version-info 1:1:1
AM_CFLAGS = -Wall -Wextra -Werror -std=c99
AM_CFLAGS = -Wall -Wextra -Werror

View File

@@ -9,11 +9,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <jansson.h>
#include "jansson_private.h"
#include "strbuffer.h"
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
typedef int (*dump_func)(const char *buffer, int size, void *data);
struct string
@@ -36,22 +39,23 @@ static int dump_to_file(const char *buffer, int size, void *data)
return 0;
}
static int dump_indent(uint32_t flags, int depth, dump_func dump, void *data)
/* 256 spaces (the maximum indentation size) */
static char whitespace[] = " ";
static int dump_indent(unsigned long flags, int depth, dump_func dump, void *data)
{
if(JSON_INDENT(flags) > 0)
{
char *ws_buffer;
int ws_count = JSON_INDENT(flags) * depth;
int i, ws_count = JSON_INDENT(flags);
if(dump("\n", 1, data))
return -1;
if(ws_count == 0)
return 0;
ws_buffer = alloca(ws_count);
memset(ws_buffer, ' ', ws_count);
return dump(ws_buffer, ws_count, data);
for(i = 0; i < depth; i++)
{
if(dump(whitespace, ws_count, data))
return -1;
}
}
return 0;
}
@@ -70,7 +74,7 @@ static int dump_string(const char *str, dump_func dump, void *data)
char seq[7];
int length;
while(*end && *end != '\\' && *end != '"' && (*end < 0 || *end > 0x1F))
while(*end && *end != '\\' && *end != '"' && (unsigned char)*end > 0x1F)
end++;
if(end != str) {
@@ -111,7 +115,7 @@ static int dump_string(const char *str, dump_func dump, void *data)
return dump("\"", 1, data);
}
static int do_dump(const json_t *json, uint32_t flags, int depth,
static int do_dump(const json_t *json, unsigned long flags, int depth,
dump_func dump, void *data)
{
switch(json_typeof(json)) {
@@ -126,30 +130,26 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
case JSON_INTEGER:
{
char *buffer;
int size, ret;
char buffer[MAX_INTEGER_STR_LENGTH];
int size;
size = asprintf(&buffer, "%d", json_integer_value(json));
if(size == -1)
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
if(size >= MAX_INTEGER_STR_LENGTH)
return -1;
ret = dump(buffer, size, data);
free(buffer);
return ret;
return dump(buffer, size, data);
}
case JSON_REAL:
{
char *buffer;
int size, ret;
char buffer[MAX_REAL_STR_LENGTH];
int size;
size = asprintf(&buffer, "%.17f", json_real_value(json));
if(size == -1)
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%0.17f", json_real_value(json));
if(size >= MAX_REAL_STR_LENGTH)
return -1;
ret = dump(buffer, size, data);
free(buffer);
return ret;
return dump(buffer, size, data);
}
case JSON_STRING:
@@ -158,7 +158,16 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
case JSON_ARRAY:
{
int i;
int n = json_array_size(json);
int n;
json_array_t *array;
/* detect circular references */
array = json_to_array(json);
if(array->visited)
return -1;
array->visited = 1;
n = json_array_size(json);
if(dump("[", 1, data))
return -1;
@@ -184,12 +193,23 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
return -1;
}
}
array->visited = 0;
return dump("]", 1, data);
}
case JSON_OBJECT:
{
void *iter = json_object_iter((json_t *)json);
json_object_t *object;
void *iter;
/* detect circular references */
object = json_to_object(json);
if(object->visited)
return -1;
object->visited = 1;
iter = json_object_iter((json_t *)json);
if(dump("{", 1, data))
return -1;
@@ -222,6 +242,8 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
iter = next;
}
object->visited = 0;
return dump("}", 1, data);
}
@@ -232,7 +254,7 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
}
char *json_dumps(const json_t *json, uint32_t flags)
char *json_dumps(const json_t *json, unsigned long flags)
{
strbuffer_t strbuff;
char *result;
@@ -241,13 +263,17 @@ char *json_dumps(const json_t *json, uint32_t flags)
return NULL;
if(strbuffer_init(&strbuff))
return NULL;
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff))
return NULL;
if(dump_to_strbuffer("\n", 1, (void *)&strbuff))
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
strbuffer_close(&strbuff);
return NULL;
}
if(dump_to_strbuffer("\n", 1, (void *)&strbuff)) {
strbuffer_close(&strbuff);
return NULL;
}
result = strdup(strbuffer_value(&strbuff));
strbuffer_close(&strbuff);
@@ -255,7 +281,7 @@ char *json_dumps(const json_t *json, uint32_t flags)
return result;
}
int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
{
if(!json_is_array(json) && !json_is_object(json))
return -1;
@@ -265,7 +291,7 @@ int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
return dump_to_file("\n", 1, (void *)output);
}
int json_dump_file(const json_t *json, const char *path, uint32_t flags)
int json_dump_file(const json_t *json, const char *path, unsigned long flags)
{
int result;

View File

@@ -133,6 +133,23 @@ static int hashtable_do_del(hashtable_t *hashtable,
return 0;
}
static void hashtable_do_clear(hashtable_t *hashtable)
{
list_t *list, *next;
pair_t *pair;
for(list = hashtable->list.next; list != &hashtable->list; list = next)
{
next = list->next;
pair = list_to_pair(list);
if(hashtable->free_key)
hashtable->free_key(pair->key);
if(hashtable->free_value)
hashtable->free_value(pair->value);
free(pair);
}
}
static int hashtable_do_rehash(hashtable_t *hashtable)
{
list_t *list, *next;
@@ -220,19 +237,7 @@ int hashtable_init(hashtable_t *hashtable,
void hashtable_close(hashtable_t *hashtable)
{
list_t *list, *next;
pair_t *pair;
for(list = hashtable->list.next; list != &hashtable->list; list = next)
{
next = list->next;
pair = list_to_pair(list);
if(hashtable->free_key)
hashtable->free_key(pair->key);
if(hashtable->free_value)
hashtable->free_value(pair->value);
free(pair);
}
hashtable_do_clear(hashtable);
free(hashtable->buckets);
}
@@ -292,6 +297,22 @@ int hashtable_del(hashtable_t *hashtable, const void *key)
return hashtable_do_del(hashtable, key, hash);
}
void hashtable_clear(hashtable_t *hashtable)
{
unsigned int i;
hashtable_do_clear(hashtable);
for(i = 0; i < num_buckets(hashtable); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
}
list_init(&hashtable->list);
hashtable->size = 0;
}
void *hashtable_iter(hashtable_t *hashtable)
{
return hashtable_iter_next(hashtable, &hashtable->list);

View File

@@ -13,8 +13,8 @@ typedef int (*key_cmp_fn)(const void *key1, const void *key2);
typedef void (*free_fn)(void *key);
struct hashtable_list {
struct hashtable_list *prev;
struct hashtable_list *next;
struct hashtable_list *prev;
struct hashtable_list *next;
};
struct hashtable_pair {
@@ -134,6 +134,15 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
*/
int hashtable_del(hashtable_t *hashtable, const void *key);
/**
* hashtable_clear - Clear hashtable
*
* @hashtable: The hashtable object
*
* Removes all items from the hashtable.
*/
void hashtable_clear(hashtable_t *hashtable);
/**
* hashtable_iter - Iterate over hashtable
*

View File

@@ -9,7 +9,10 @@
#define JANSSON_H
#include <stdio.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* types */
@@ -54,7 +57,7 @@ json_t *json_null(void);
static inline json_t *json_incref(json_t *json)
{
if(json)
if(json && json->refcount != (unsigned int)-1)
++json->refcount;
return json;
}
@@ -64,31 +67,66 @@ void json_delete(json_t *json);
static inline void json_decref(json_t *json)
{
if(json && --json->refcount == 0)
if(json && json->refcount != (unsigned int)-1 && --json->refcount == 0)
json_delete(json);
}
/* getters, setters, manipulation */
unsigned int json_object_size(const json_t *object);
json_t *json_object_get(const json_t *object, const char *key);
int json_object_set(json_t *object, const char *key, json_t *value);
int json_object_set_new(json_t *object, const char *key, json_t *value);
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_next(json_t *object, void *iter);
const char *json_object_iter_key(void *iter);
json_t *json_object_iter_value(void *iter);
static inline
int json_object_set(json_t *object, const char *key, json_t *value)
{
return json_object_set_new(object, key, 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(json_t *array, unsigned int index, json_t *value);
int json_array_append(json_t *array, json_t *value);
int json_array_set_new(json_t *array, unsigned int index, json_t *value);
int json_array_append_new(json_t *array, json_t *value);
int json_array_insert_new(json_t *array, unsigned int index, json_t *value);
int json_array_remove(json_t *array, unsigned int index);
int json_array_clear(json_t *array);
int json_array_extend(json_t *array, json_t *other);
const char *json_string_value(const json_t *json);
int json_integer_value(const json_t *json);
double json_real_value(const json_t *json);
static inline
int json_array_set(json_t *array, unsigned int index, json_t *value)
{
return json_array_set_new(array, index, json_incref(value));
}
static inline
int json_array_append(json_t *array, json_t *value)
{
return json_array_append_new(array, json_incref(value));
}
static inline
int json_array_insert(json_t *array, unsigned int index, json_t *value)
{
return json_array_insert_new(array, index, json_incref(value));
}
const char *json_string_value(const json_t *string);
int json_integer_value(const json_t *integer);
double json_real_value(const json_t *real);
double json_number_value(const json_t *json);
int json_string_set(const json_t *string, const char *value);
int json_integer_set(const json_t *integer, int value);
int json_real_set(const json_t *real, double value);
/* loading, printing */
@@ -105,8 +143,12 @@ json_t *json_load_file(const char *path, json_error_t *error);
#define JSON_INDENT(n) (n & 0xFF)
char *json_dumps(const json_t *json, uint32_t flags);
int json_dumpf(const json_t *json, FILE *output, uint32_t flags);
int json_dump_file(const json_t *json, const char *path, uint32_t flags);
char *json_dumps(const json_t *json, unsigned long flags);
int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
int json_dump_file(const json_t *json, const char *path, unsigned long flags);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -8,8 +8,48 @@
#ifndef JANSSON_PRIVATE_H
#define JANSSON_PRIVATE_H
#include "jansson.h"
#include "hashtable.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
typedef struct {
json_t json;
hashtable_t hashtable;
int visited;
} json_object_t;
typedef struct {
json_t json;
unsigned int size;
unsigned int entries;
json_t **table;
int visited;
} json_array_t;
typedef struct {
json_t json;
char *value;
} json_string_t;
typedef struct {
json_t json;
double value;
} json_real_t;
typedef struct {
json_t json;
int value;
} json_integer_t;
#define json_to_object(json_) container_of(json_, json_object_t, json)
#define json_to_array(json_) container_of(json_, json_array_t, json)
#define json_to_string(json_) container_of(json_, json_string_t, json)
#define json_to_real(json_) container_of(json_, json_real_t, json)
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
int json_object_set_nocheck(json_t *json, const char *key, json_t *value);
json_t *json_string_nocheck(const char *value);
#endif

View File

@@ -8,11 +8,11 @@
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <assert.h>
#include <jansson.h>
@@ -134,10 +134,7 @@ static char stream_get(stream_t *stream, json_error_t *error)
c = stream->buffer[0];
if(c == EOF && stream->eof(stream->data))
return EOF;
if(c < 0)
if((unsigned char)c >= 0x80 && c != (char)EOF)
{
/* multi-byte UTF-8 sequence */
int i, count;
@@ -257,17 +254,17 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
lex->value.string = NULL;
lex->token = TOKEN_INVALID;
/* skip the " */
c = lex_get_save(lex, error);
while(c != '"') {
if(c == EOF) {
if(c == (char)EOF) {
lex_unget_unsave(lex, c);
if(lex_eof(lex))
error_set(error, lex, "premature end of input");
goto out;
}
else if(0 <= c && c <= 0x1F) {
else if((unsigned char)c <= 0x1F) {
/* control character */
lex_unget_unsave(lex, c);
if(c == '\n')
@@ -341,9 +338,10 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
/* valid second surrogate */
value = ((value - 0xD800) << 10) +
(value2 - 0xDC00) +
0x10000;
value =
((value - 0xD800) << 10) +
(value2 - 0xDC00) +
0x10000;
}
else {
/* invalid second surrogate */
@@ -402,10 +400,11 @@ out:
free(lex->value.string);
}
static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
static int lex_scan_number(lex_t *lex, char c, json_error_t *error)
{
const char *saved_text;
char *end;
double value;
lex->token = TOKEN_INVALID;
@@ -419,21 +418,37 @@ static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
goto out;
}
}
else /* c != '0' */ {
else if(isdigit(c)) {
c = lex_get_save(lex, error);
while(isdigit(c))
c = lex_get_save(lex, error);
}
else {
lex_unget_unsave(lex, c);
goto out;
}
if(c != '.' && c != 'E' && c != 'e') {
long value;
lex_unget_unsave(lex, c);
lex->token = TOKEN_INTEGER;
saved_text = strbuffer_value(&lex->saved_text);
lex->value.integer = strtol(saved_text, &end, 10);
value = strtol(saved_text, &end, 10);
assert(end == saved_text + lex->saved_text.length);
return;
if((value == LONG_MAX && errno == ERANGE) || value > INT_MAX) {
error_set(error, lex, "too big integer");
goto out;
}
else if((value == LONG_MIN && errno == ERANGE) || value < INT_MIN) {
error_set(error, lex, "too big negative integer");
goto out;
}
lex->token = TOKEN_INTEGER;
lex->value.integer = (int)value;
return 0;
}
if(c == '.') {
@@ -463,14 +478,29 @@ static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
}
lex_unget_unsave(lex, c);
lex->token = TOKEN_REAL;
saved_text = strbuffer_value(&lex->saved_text);
lex->value.real = strtod(saved_text, &end);
value = strtod(saved_text, &end);
assert(end == saved_text + lex->saved_text.length);
if(value == 0 && errno == ERANGE) {
error_set(error, lex, "real number underflow");
goto out;
}
/* Cannot test for +/-HUGE_VAL because the HUGE_VAL constant is
only defined in C99 mode. So let's trust in sole errno. */
else if(errno == ERANGE) {
error_set(error, lex, "real number overflow");
goto out;
}
lex->token = TOKEN_REAL;
lex->value.real = value;
return 0;
out:
return;
return -1;
}
static int lex_scan(lex_t *lex, json_error_t *error)
@@ -480,8 +510,8 @@ static int lex_scan(lex_t *lex, json_error_t *error)
strbuffer_clear(&lex->saved_text);
if(lex->token == TOKEN_STRING) {
free(lex->value.string);
lex->value.string = NULL;
free(lex->value.string);
lex->value.string = NULL;
}
c = lex_get(lex, error);
@@ -493,7 +523,7 @@ static int lex_scan(lex_t *lex, json_error_t *error)
c = lex_get(lex, error);
}
if(c == EOF) {
if(c == (char)EOF) {
if(lex_eof(lex))
lex->token = TOKEN_EOF;
else
@@ -509,8 +539,10 @@ static int lex_scan(lex_t *lex, json_error_t *error)
else if(c == '"')
lex_scan_string(lex, error);
else if(isdigit(c) || c == '-')
lex_scan_number(lex, c, error);
else if(isdigit(c) || c == '-') {
if(lex_scan_number(lex, c, error))
goto out;
}
else if(isupper(c) || islower(c)) {
/* eat up the whole identifier for clearer error messages */
@@ -544,6 +576,17 @@ out:
return lex->token;
}
static char *lex_steal_string(lex_t *lex)
{
char *result = NULL;
if(lex->token == TOKEN_STRING)
{
result = lex->value.string;
lex->value.string = NULL;
}
return result;
}
static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
{
stream_init(&lex->stream, get, eof, data);
@@ -587,7 +630,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
goto error;
}
key = strdup(lex->value.string);
key = lex_steal_string(lex);
if(!key)
return NULL;
@@ -706,7 +749,7 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
break;
case '{':
json = parse_object(lex, error);
json = parse_object(lex, error);
break;
case '[':
@@ -825,6 +868,8 @@ json_t *json_load_file(const char *path, json_error_t *error)
json_t *result;
FILE *fp;
error_init(error);
fp = fopen(path, "r");
if(!fp)
{

View File

@@ -15,41 +15,6 @@
#include "utf.h"
#include "util.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
typedef struct {
json_t json;
hashtable_t hashtable;
} json_object_t;
typedef struct {
json_t json;
unsigned int size;
unsigned int entries;
json_t **table;
} json_array_t;
typedef struct {
json_t json;
char *value;
} json_string_t;
typedef struct {
json_t json;
double value;
} json_real_t;
typedef struct {
json_t json;
int value;
} json_integer_t;
#define json_to_object(json_) container_of(json_, json_object_t, json)
#define json_to_array(json_) container_of(json_, json_array_t, json)
#define json_to_string(json_) container_of(json_, json_string_t, json)
#define json_to_real(json_) container_of(json_, json_real_t, json)
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
static inline void json_init(json_t *json, json_type type)
{
@@ -98,6 +63,9 @@ json_t *json_object(void)
free(object);
return NULL;
}
object->visited = 0;
return &object->json;
}
@@ -107,6 +75,17 @@ static void json_delete_object(json_object_t *object)
free(object);
}
unsigned int json_object_size(const json_t *json)
{
json_object_t *object;
if(!json_is_object(json))
return -1;
object = json_to_object(json);
return object->hashtable.size;
}
json_t *json_object_get(const json_t *json, const char *key)
{
json_object_t *object;
@@ -118,23 +97,43 @@ json_t *json_object_get(const json_t *json, const char *key)
return hashtable_get(&object->hashtable, key);
}
int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
{
json_object_t *object;
if(!json_is_object(json))
if(!key || !value)
return -1;
if(!json_is_object(json) || json == value)
{
json_decref(value);
return -1;
}
object = json_to_object(json);
return hashtable_set(&object->hashtable, strdup(key), json_incref(value));
if(hashtable_set(&object->hashtable, strdup(key), value))
{
json_decref(value);
return -1;
}
return 0;
}
int json_object_set(json_t *json, const char *key, json_t *value)
int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
{
if(!utf8_check_string(key, -1))
return -1;
return json_object_set_new_nocheck(json, key, json_incref(value));
}
return json_object_set_nocheck(json, key, value);
int json_object_set_new(json_t *json, const char *key, json_t *value)
{
if(!key || !utf8_check_string(key, -1))
{
json_decref(value);
return -1;
}
return json_object_set_new_nocheck(json, key, value);
}
int json_object_del(json_t *json, const char *key)
@@ -148,6 +147,43 @@ int json_object_del(json_t *json, const char *key)
return hashtable_del(&object->hashtable, key);
}
int json_object_clear(json_t *json)
{
json_object_t *object;
if(!json_is_object(json))
return -1;
object = json_to_object(json);
hashtable_clear(&object->hashtable);
return 0;
}
int json_object_update(json_t *object, json_t *other)
{
void *iter;
if(!json_is_object(object) || !json_is_object(other))
return -1;
iter = json_object_iter(other);
while(iter) {
const char *key;
json_t *value;
key = json_object_iter_key(iter);
value = json_object_iter_value(iter);
if(json_object_set(object, key, value))
return -1;
iter = json_object_iter_next(other, iter);
}
return 0;
}
void *json_object_iter(json_t *json)
{
json_object_t *object;
@@ -193,12 +229,19 @@ json_t *json_array(void)
{
json_array_t *array = malloc(sizeof(json_array_t));
if(!array)
return NULL;
return NULL;
json_init(&array->json, JSON_ARRAY);
array->entries = 0;
array->size = 0;
array->table = NULL;
array->size = 8;
array->table = malloc(array->size * sizeof(json_t *));
if(!array->table) {
free(array);
return NULL;
}
array->visited = 0;
return &array->json;
}
@@ -235,9 +278,143 @@ json_t *json_array_get(const json_t *json, unsigned int index)
return array->table[index];
}
int json_array_set(json_t *json, unsigned int index, json_t *value)
int json_array_set_new(json_t *json, unsigned int index, json_t *value)
{
json_array_t *array;
if(!value)
return -1;
if(!json_is_array(json) || json == value)
{
json_decref(value);
return -1;
}
array = json_to_array(json);
if(index >= array->entries)
{
json_decref(value);
return -1;
}
json_decref(array->table[index]);
array->table[index] = value;
return 0;
}
static void array_move(json_array_t *array, unsigned int dest,
unsigned int src, unsigned int count)
{
memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *));
}
static void array_copy(json_t **dest, unsigned int dpos,
json_t **src, unsigned int spos,
unsigned int count)
{
memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *));
}
static json_t **json_array_grow(json_array_t *array,
unsigned int amount,
int copy)
{
unsigned int new_size;
json_t **old_table, **new_table;
if(array->entries + amount <= array->size)
return array->table;
old_table = array->table;
new_size = max(array->size + amount, array->size * 2);
new_table = malloc(new_size * sizeof(json_t *));
if(!new_table)
return NULL;
array->size = new_size;
array->table = new_table;
if(copy) {
array_copy(array->table, 0, old_table, 0, array->entries);
free(old_table);
return array->table;
}
return old_table;
}
int json_array_append_new(json_t *json, json_t *value)
{
json_array_t *array;
if(!value)
return -1;
if(!json_is_array(json) || json == value)
{
json_decref(value);
return -1;
}
array = json_to_array(json);
if(!json_array_grow(array, 1, 1)) {
json_decref(value);
return -1;
}
array->table[array->entries] = value;
array->entries++;
return 0;
}
int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
{
json_array_t *array;
json_t **old_table;
if(!value)
return -1;
if(!json_is_array(json) || json == value) {
json_decref(value);
return -1;
}
array = json_to_array(json);
if(index > array->entries) {
json_decref(value);
return -1;
}
old_table = json_array_grow(array, 1, 0);
if(!old_table) {
json_decref(value);
return -1;
}
if(old_table != array->table) {
array_copy(array->table, 0, old_table, 0, index);
array_copy(array->table, index + 1, old_table, index,
array->entries - index);
free(old_table);
}
else
array_move(array, index + 1, index, array->entries - index);
array->table[index] = value;
array->entries++;
return 0;
}
int json_array_remove(json_t *json, unsigned int index)
{
json_array_t *array;
if(!json_is_array(json))
return -1;
array = json_to_array(json);
@@ -246,28 +423,48 @@ int json_array_set(json_t *json, unsigned int index, json_t *value)
return -1;
json_decref(array->table[index]);
array->table[index] = json_incref(value);
array_move(array, index, index + 1, array->entries - index);
array->entries--;
return 0;
}
int json_array_append(json_t *json, json_t *value)
int json_array_clear(json_t *json)
{
json_array_t *array;
unsigned int i;
if(!json_is_array(json))
return -1;
array = json_to_array(json);
if(array->entries == array->size) {
array->size = max(8, array->size * 2);
array->table = realloc(array->table, array->size * sizeof(json_t *));
if(!array->table)
return -1;
}
for(i = 0; i < array->entries; i++)
json_decref(array->table[i]);
array->table[array->entries] = json_incref(value);
array->entries++;
array->entries = 0;
return 0;
}
int json_array_extend(json_t *json, json_t *other_json)
{
json_array_t *array, *other;
unsigned int i;
if(!json_is_array(json) || !json_is_array(other_json))
return -1;
array = json_to_array(json);
other = json_to_array(other_json);
if(!json_array_grow(array, other->entries, 1))
return -1;
for(i = 0; i < other->entries; i++)
json_incref(other->table[i]);
array_copy(array->table, array->entries, other->table, 0, other->entries);
array->entries += other->entries;
return 0;
}
@@ -276,18 +473,28 @@ int json_array_append(json_t *json, json_t *value)
json_t *json_string_nocheck(const char *value)
{
json_string_t *string = malloc(sizeof(json_string_t));
json_string_t *string;
if(!value)
return NULL;
string = malloc(sizeof(json_string_t));
if(!string)
return NULL;
return NULL;
json_init(&string->json, JSON_STRING);
string->value = strdup(value);
if(!string->value) {
free(string);
return NULL;
}
return &string->json;
}
json_t *json_string(const char *value)
{
if(!utf8_check_string(value, -1))
if(!value || !utf8_check_string(value, -1))
return NULL;
return json_string_nocheck(value);
@@ -301,6 +508,25 @@ const char *json_string_value(const json_t *json)
return json_to_string(json)->value;
}
int json_string_set(const json_t *json, const char *value)
{
char *dup;
json_string_t *string;
if(!json_is_string(json) || !value || !utf8_check_string(value, -1))
return -1;
dup = strdup(value);
if(!dup)
return -1;
string = json_to_string(json);
free(string->value);
string->value = dup;
return 0;
}
static void json_delete_string(json_string_t *string)
{
free(string->value);
@@ -314,7 +540,7 @@ json_t *json_integer(int value)
{
json_integer_t *integer = malloc(sizeof(json_integer_t));
if(!integer)
return NULL;
return NULL;
json_init(&integer->json, JSON_INTEGER);
integer->value = value;
@@ -329,6 +555,16 @@ int json_integer_value(const json_t *json)
return json_to_integer(json)->value;
}
int json_integer_set(const json_t *json, int value)
{
if(!json_is_integer(json))
return -1;
json_to_integer(json)->value = value;
return 0;
}
static void json_delete_integer(json_integer_t *integer)
{
free(integer);
@@ -341,7 +577,7 @@ json_t *json_real(double value)
{
json_real_t *real = malloc(sizeof(json_real_t));
if(!real)
return NULL;
return NULL;
json_init(&real->json, JSON_REAL);
real->value = value;
@@ -356,7 +592,17 @@ double json_real_value(const json_t *json)
return json_to_real(json)->value;
}
static void json_delete_real (json_real_t *real)
int json_real_set(const json_t *json, double value)
{
if(!json_is_real(json))
return 0;
json_to_real(json)->value = value;
return 0;
}
static void json_delete_real(json_real_t *real)
{
free(real);
}
@@ -381,9 +627,9 @@ json_t *json_true(void)
{
static json_t the_true = {
.type = JSON_TRUE,
.refcount = 1
.refcount = (unsigned int)1
};
return json_incref(&the_true);
return &the_true;
}
@@ -391,9 +637,9 @@ json_t *json_false(void)
{
static json_t the_false = {
.type = JSON_FALSE,
.refcount = 1
.refcount = (unsigned int)1
};
return json_incref(&the_false);
return &the_false;
}
@@ -401,9 +647,9 @@ json_t *json_null(void)
{
static json_t the_null = {
.type = JSON_NULL,
.refcount = 1
.refcount = (unsigned int)1
};
return json_incref(&the_null);
return &the_null;
}

2
test/.gitignore vendored
View File

@@ -3,5 +3,7 @@ loads_dumps
load_file_dump_file
testlogs
testprogs/test_array
testprogs/test_load
testprogs/test_number
testprogs/test_object
testprogs/test_simple

View File

@@ -5,8 +5,11 @@
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
import simplejson
import sys
try:
import json
except ImportError:
import simplejson as json
def load(filename):
try:
@@ -17,14 +20,14 @@ def load(filename):
sys.exit(1)
try:
json = simplejson.load(jsonfile)
jsondata = json.load(jsonfile)
except ValueError, err:
print "%s is malformed: %s" % (filename, err)
sys.exit(1)
finally:
jsonfile.close()
return json
return jsondata
def main():
if len(sys.argv) != 3:

View File

@@ -26,8 +26,8 @@ int main(int argc, char *argv[])
count = fread(buffer, 1, BUFFER_SIZE, stdin);
if(count < 0 || count >= BUFFER_SIZE) {
fprintf(stderr, "unable to read input\n");
return 1;
fprintf(stderr, "unable to read input\n");
return 1;
}
buffer[count] = '\0';

View File

@@ -44,7 +44,10 @@ for testfile in $TESTFILES; do
tmpdir="testlogs/`basename $testfile`"
rm -rf $tmpdir
mkdir -p $tmpdir
${srcdir}/split-testfile.py $testfile $tmpdir | while read name; do
if echo "$testfile" | grep -q -E -e '-strip$'; then
opts="--strip"
fi
${srcdir}/split-testfile.py $opts $testfile $tmpdir | while read name; do
run_test loadf_dumpf $tmpdir/$name
run_test loads_dumps $tmpdir/$name
run_test load_file_dump_file $tmpdir/$name

View File

@@ -7,6 +7,13 @@
import os
import sys
from optparse import OptionParser
def strip_file(filename):
with open(filename) as fobj:
data = fobj.read()
with open(filename, 'w') as fobj:
fobj.write(data.strip())
def open_files(outdir, i, name):
basename = '%02d_%s' % (i, name)
@@ -16,12 +23,17 @@ def open_files(outdir, i, name):
return open(input_path, 'w'), open(output_path, 'w')
def main():
if len(sys.argv) != 3:
print 'usage: %s input-file output-directory' % sys.argv[0]
parser = OptionParser('usage: %prog [options] inputfile outputdir')
parser.add_option('--strip', help='strip whitespace from input',
action='store_true', default=False)
options, args = parser.parse_args()
if len(args) != 2:
parser.print_help()
return 2
infile = os.path.normpath(sys.argv[1])
outdir = os.path.normpath(sys.argv[2])
infile = os.path.normpath(args[0])
outdir = os.path.normpath(args[1])
if not os.path.exists(outdir):
print >>sys.stderr, 'output directory %r does not exist!' % outdir
@@ -37,6 +49,8 @@ def main():
if input is not None and output is not None:
input.close()
output.close()
if options.strip:
strip_file(input.name)
input, output = open_files(outdir, n, line[5:line.find(' ====\n')])
current = input
elif line == '====\n':

View File

@@ -5,7 +5,7 @@
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
TESTFILES="${srcdir}/testdata/invalid ${srcdir}/testdata/invalid-unicode"
TESTFILES="${srcdir}/testdata/invalid ${srcdir}/testdata/invalid-strip ${srcdir}/testdata/invalid-unicode"
run_test() {
local prog=$1

View File

@@ -5,7 +5,7 @@
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
TESTFILES="${srcdir}/testdata/valid"
TESTFILES="${srcdir}/testdata/valid ${srcdir}/testdata/valid-strip"
run_test() {
local prog=$1

View File

@@ -1 +1 @@
EXTRA_DIST = invalid invalid-unicode valid
EXTRA_DIST = invalid invalid-strip invalid-unicode valid valid-strip

42
test/testdata/invalid vendored
View File

@@ -127,6 +127,21 @@ invalid token near '1e'
====
1
invalid token near '1e'
==== real-positive-overflow ====
[123123e100000]
====
1
real number overflow near '123123e100000'
==== real-negative-overflow ====
[-123123e100000]
====
1
real number overflow near '-123123e100000'
==== real-underflow ====
[123e-10000000]
====
1
real number underflow near '123e-10000000'
==== integer-starting-with-zero ====
[012]
====
@@ -137,12 +152,37 @@ invalid token near '0'
====
1
invalid token near '-0'
==== too-big-positive-integer ====
[123123123123123]
====
1
too big integer near '123123123123123'
==== too-big-negative-integer ====
[-123123123123123]
====
1
too big negative integer near '-123123123123123'
==== invalid-identifier ====
[troo
====
1
invalid token near 'troo'
==== invalid-escap ====
==== minus-sign-without-number ====
[-foo]
====
1
invalid token near '-'
==== invalid-negative-integerr ====
[-123foo]
====
1
']' expected near 'foo'
==== invalid-negative-real ====
[-123.123foo]
====
1
']' expected near 'foo'
==== invalid-escape ====
["\a <-- invalid escape"]
====
1

235
test/testdata/invalid-strip vendored Normal file
View File

@@ -0,0 +1,235 @@
==== empty ====
====
1
'[' or '{' expected near end of file
==== null ====
null
====
1
'[' or '{' expected near 'null'
==== lone-open-brace ====
{
====
1
string or '}' expected near end of file
==== lone-open-bracket ====
[
====
1
']' expected near end of file
==== bracket-comma ====
[,
====
1
unexpected token near ','
==== bracket-one-comma ====
[1,
====
1
']' expected near end of file
==== unterminated-string ====
["a
====
1
premature end of input near '"a'
==== unterminated-array ====
["a"
====
1
']' expected near end of file
==== apostrophe ====
['
====
1
invalid token near '''
==== brace-comma ====
{,
====
1
string or '}' expected near ','
==== unterminated-empty-key ====
{"
====
1
premature end of input near '"'
==== unterminated-key ====
{"a
====
1
premature end of input near '"a'
==== object-no-colon ====
{"a"
====
1
':' expected near end of file
==== object-apostrophes ====
{'a'
====
1
string or '}' expected near '''
==== object-no-value ====
{"a":
====
1
unexpected token near end of file
==== object-unterminated-value ====
{"a":"a
====
1
premature end of input near '"a'
==== object-garbage-at-end ====
{"a":"a" 123}
====
1
'}' expected near '123'
==== unterminated-object-and-array ====
{[
====
1
string or '}' expected near '['
==== unterminated-array-and-object ====
[{
====
1
string or '}' expected near end of file
==== object-in-unterminated-array ====
[{}
====
1
']' expected near end of file
==== extra-comma-in-array ====
[1,]
====
1
unexpected token near ']'
==== extra-command-in-multiline-array ====
[1,
2,
3,
4,
5,
]
====
6
unexpected token near ']'
==== real-truncated-at-point ====
[1.]
====
1
invalid token near '1.'
==== real-truncated-at-e ====
[1e]
====
1
invalid token near '1e'
==== real-garbage-after-e ====
[1ea]
====
1
invalid token near '1e'
==== real-positive-overflow ====
[123123e100000]
====
1
real number overflow near '123123e100000'
==== real-negative-overflow ====
[-123123e100000]
====
1
real number overflow near '-123123e100000'
==== real-underflow ====
[123e-10000000]
====
1
real number underflow near '123e-10000000'
==== integer-starting-with-zero ====
[012]
====
1
invalid token near '0'
==== negative-integer-starting-with-zero ====
[-012]
====
1
invalid token near '-0'
==== too-big-positive-integer ====
[123123123123123]
====
1
too big integer near '123123123123123'
==== too-big-negative-integer ====
[-123123123123123]
====
1
too big negative integer near '-123123123123123'
==== invalid-identifier ====
[troo
====
1
invalid token near 'troo'
==== minus-sign-without-number ====
[-foo]
====
1
invalid token near '-'
==== invalid-negative-integerr ====
[-123foo]
====
1
']' expected near 'foo'
==== invalid-negative-real ====
[-123.123foo]
====
1
']' expected near 'foo'
==== invalid-escape ====
["\a <-- invalid escape"]
====
1
invalid escape near '"\'
==== tab-character-in-string ====
[" <-- tab character"]
====
1
control character 0x9 near '"'
==== null-byte-in-string ====
["\u0000 (null byte not allowed)"]
====
1
\u0000 is not allowed
==== truncated-unicode-surrogate ====
["\uDADA (first surrogate without the second)"]
====
1
invalid Unicode '\uDADA'
==== invalid-second-surrogate ====
["\uD888\u3210 (first surrogate and invalid second surrogate)"]
====
1
invalid Unicode '\uD888\u3210'
==== lone-second-surrogate ====
["\uDFAA (second surrogate on it's own)"]
====
1
invalid Unicode '\uDFAA'
==== unicode-identifier ====
å
====
1
'[' or '{' expected near 'å'
==== ascii-unicode-identifier ====
====
1
'[' or '{' expected near 'a'
==== garbage-at-the-end ====
[1,2,3]foo
====
1
end of file expected near 'foo'
==== garbage-after-newline ====
[1,2,3]
foo
====
2
end of file expected near 'foo'

68
test/testdata/valid-strip vendored Normal file
View File

@@ -0,0 +1,68 @@
==== empty-string ====
[""]
==== short-string ====
["a"]
==== simple-ascii-string ====
["abcdefghijklmnopqrstuvwxyz1234567890 "]
==== utf-8-string ====
["€þıœəßð some utf-8 ĸʒ×ŋµåäö𝄞"]
==== string-escapes ====
["\"\\\/\b\f\n\r\t"]
==== one-byte-utf-8 ====
["\u002c one-byte UTF-8"]
==== two-byte-utf-8 ====
["\u0123 two-byte UTF-8"]
==== three-byte-utf-8 ====
["\u0821 three-byte UTF-8"]
==== utf-surrogate-four-byte-encoding ====
["\uD834\uDD1E surrogate, four-byte UTF-8"]
==== escaped-utf-control-char ====
["\u0012 escaped control character"]
==== simple-int-0 ====
[0]
==== simple-int-1 ====
[1]
==== simple-int-123 ====
[123]
==== negative-zero ====
[-0]
==== negative-one ====
[-1]
==== negative-int ====
[-123]
==== simple-real ====
[123.456789]
==== real-exponent ====
[123e45]
==== real-capital-e ====
[1E22]
==== real-positive-exponent ====
[1e+2]
==== real-negative-exponent ====
[1e-2]
==== real-capital-e-positive-exponent ====
[1E+2]
==== real-capital-e-negative-exponent ====
[1E-2]
==== real-fraction-exponent ====
[123.456e78]
==== true ====
[true]
==== false ====
[false]
==== null ====
[null]
==== empty-array ====
[]
==== empty-object-in-array ====
[{}]
==== complex-array ====
[1,2,3,4,
"a", "b", "c",
{"foo": "bar", "core": "dump"},
true, false, true, true, null, false
]
==== empty-object ====
{}
==== simple-object ====
{"a":[]}

View File

@@ -1,8 +1,10 @@
check_PROGRAMS = test_array test_number test_object
check_PROGRAMS = test_array test_load test_simple test_number test_object
test_array_SOURCES = test_array.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
test_object_SOURCES = test_number.c util.h
test_object_SOURCES = test_object.c util.h
AM_CPPFLAGS = -I$(top_srcdir)/src
AM_CFLAGS = -Wall -Werror

View File

@@ -8,7 +8,7 @@
#include <jansson.h>
#include "util.h"
int main()
static void test_misc(void)
{
json_t *array, *five, *seven, *value;
int i;
@@ -19,14 +19,15 @@ int main()
if(!array)
fail("unable to create array");
if(!five)
fail("unable to create integer");
if(!seven)
if(!five || !seven)
fail("unable to create integer");
if(json_array_size(array) != 0)
fail("empty array has nonzero size");
if(!json_array_append(array, NULL))
fail("able to append NULL");
if(json_array_append(array, five))
fail("unable to append");
@@ -54,6 +55,9 @@ int main()
if(json_array_set(array, 0, seven))
fail("unable to set value");
if(!json_array_set(array, 0, NULL))
fail("able to set NULL");
if(json_array_size(array) != 2)
fail("wrong array size");
@@ -85,9 +89,312 @@ int main()
fail("got wrong value");
}
if(json_array_set_new(array, 15, json_integer(123)))
fail("unable to set new value");
value = json_array_get(array, 15);
if(!json_is_integer(value) || json_integer_value(value) != 123)
fail("json_array_set_new works incorrectly");
if(!json_array_set_new(array, 15, NULL))
fail("able to set_new NULL value");
if(json_array_append_new(array, json_integer(321)))
fail("unable to append new value");
value = json_array_get(array, json_array_size(array) - 1);
if(!json_is_integer(value) || json_integer_value(value) != 321)
fail("json_array_append_new works incorrectly");
if(!json_array_append_new(array, NULL))
fail("able to append_new NULL value");
json_decref(five);
json_decref(seven);
json_decref(array);
}
static void test_insert(void)
{
json_t *array, *five, *seven, *eleven, *value;
int i;
array = json_array();
five = json_integer(5);
seven = json_integer(7);
eleven = json_integer(11);
if(!array)
fail("unable to create array");
if(!five || !seven || !eleven)
fail("unable to create integer");
if(!json_array_insert(array, 1, five))
fail("able to insert value out of bounds");
if(json_array_insert(array, 0, five))
fail("unable to insert value in an empty array");
if(json_array_get(array, 0) != five)
fail("json_array_insert works incorrectly");
if(json_array_size(array) != 1)
fail("array size is invalid after insertion");
if(json_array_insert(array, 1, seven))
fail("unable to insert value at the end of an array");
if(json_array_get(array, 0) != five)
fail("json_array_insert works incorrectly");
if(json_array_get(array, 1) != seven)
fail("json_array_insert works incorrectly");
if(json_array_size(array) != 2)
fail("array size is invalid after insertion");
if(json_array_insert(array, 1, eleven))
fail("unable to insert value in the middle of an array");
if(json_array_get(array, 0) != five)
fail("json_array_insert works incorrectly");
if(json_array_get(array, 1) != eleven)
fail("json_array_insert works incorrectly");
if(json_array_get(array, 2) != seven)
fail("json_array_insert works incorrectly");
if(json_array_size(array) != 3)
fail("array size is invalid after insertion");
if(json_array_insert_new(array, 2, json_integer(123)))
fail("unable to insert value in the middle of an array");
value = json_array_get(array, 2);
if(!json_is_integer(value) || json_integer_value(value) != 123)
fail("json_array_insert_new works incorrectly");
if(json_array_size(array) != 4)
fail("array size is invalid after insertion");
for(i = 0; i < 20; i++) {
if(json_array_insert(array, 0, seven))
fail("unable to insert value at the begining of an array");
}
for(i = 0; i < 20; i++) {
if(json_array_get(array, i) != seven)
fail("json_aray_insert works incorrectly");
}
if(json_array_size(array) != 24)
fail("array size is invalid after loop insertion");
json_decref(five);
json_decref(seven);
json_decref(eleven);
json_decref(array);
}
static void test_remove(void)
{
json_t *array, *five, *seven;
array = json_array();
five = json_integer(5);
seven = json_integer(7);
if(!array)
fail("unable to create array");
if(!five)
fail("unable to create integer");
if(!seven)
fail("unable to create integer");
if(!json_array_remove(array, 0))
fail("able to remove an unexisting index");
if(json_array_append(array, five))
fail("unable to append");
if(!json_array_remove(array, 1))
fail("able to remove an unexisting index");
if(json_array_remove(array, 0))
fail("unable to remove");
if(json_array_size(array) != 0)
fail("array size is invalid after removing");
if(json_array_append(array, five) ||
json_array_append(array, seven) ||
json_array_append(array, five) ||
json_array_append(array, seven))
fail("unable to append");
if(json_array_remove(array, 2))
fail("unable to remove");
if(json_array_size(array) != 3)
fail("array size is invalid after removing");
if(json_array_get(array, 0) != five ||
json_array_get(array, 1) != seven ||
json_array_get(array, 2) != seven)
fail("remove works incorrectly");
json_decref(five);
json_decref(seven);
json_decref(array);
}
static void test_clear(void)
{
json_t *array, *five, *seven;
int i;
array = json_array();
five = json_integer(5);
seven = json_integer(7);
if(!array)
fail("unable to create array");
if(!five || !seven)
fail("unable to create integer");
for(i = 0; i < 10; i++) {
if(json_array_append(array, five))
fail("unable to append");
}
for(i = 0; i < 10; i++) {
if(json_array_append(array, seven))
fail("unable to append");
}
if(json_array_size(array) != 20)
fail("array size is invalid after appending");
if(json_array_clear(array))
fail("unable to clear");
if(json_array_size(array) != 0)
fail("array size is invalid after clearing");
json_decref(five);
json_decref(seven);
json_decref(array);
}
static void test_extend(void)
{
json_t *array1, *array2, *five, *seven;
int i;
array1 = json_array();
array2 = json_array();
five = json_integer(5);
seven = json_integer(7);
if(!array1 || !array2)
fail("unable to create array");
if(!five || !seven)
fail("unable to create integer");
for(i = 0; i < 10; i++) {
if(json_array_append(array1, five))
fail("unable to append");
}
for(i = 0; i < 10; i++) {
if(json_array_append(array2, seven))
fail("unable to append");
}
if(json_array_size(array1) != 10 || json_array_size(array2) != 10)
fail("array size is invalid after appending");
if(json_array_extend(array1, array2))
fail("unable to extend");
for(i = 0; i < 10; i++) {
if(json_array_get(array1, i) != five)
fail("invalid array contents after extending");
}
for(i = 10; i < 20; i++) {
if(json_array_get(array1, i) != seven)
fail("invalid array contents after extending");
}
json_decref(five);
json_decref(seven);
json_decref(array1);
json_decref(array2);
}
static void test_circular()
{
json_t *array1, *array2;
/* the simple cases are checked */
array1 = json_array();
if(!array1)
fail("unable to create array");
if(json_array_append(array1, array1) == 0)
fail("able to append self");
if(json_array_insert(array1, 0, array1) == 0)
fail("able to insert self");
if(json_array_append_new(array1, json_true()))
fail("failed to append true");
if(json_array_set(array1, 0, array1) == 0)
fail("able to set self");
json_decref(array1);
/* create circular references */
array1 = json_array();
array2 = json_array();
if(!array1 || !array2)
fail("unable to create array");
if(json_array_append(array1, array2) ||
json_array_append(array2, array1))
fail("unable to append");
/* circularity is detected when dumping */
if(json_dumps(array1, 0) != NULL)
fail("able to dump circulars");
/* decref twice to deal with the circular references */
json_decref(array1);
json_decref(array2);
json_decref(array1);
}
int main()
{
test_misc();
test_insert();
test_remove();
test_clear();
test_extend();
test_circular();
return 0;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2009 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;
json_error_t error;
json = json_load_file("/path/to/nonexistent/file.json", &error);
if(error.line != -1)
fail("json_load_file returned an invalid line number");
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
fail("json_load_file returned an invalid error message");
return 0;
}

View File

@@ -6,11 +6,171 @@
*/
#include <jansson.h>
#include <string.h>
#include "util.h"
int main()
static void test_clear()
{
json_t *object, *ten;
object = json_object();
ten = json_integer(10);
if(!object)
fail("unable to create object");
if(!ten)
fail("unable to create integer");
if(json_object_set(object, "a", ten) ||
json_object_set(object, "b", ten) ||
json_object_set(object, "c", ten) ||
json_object_set(object, "d", ten) ||
json_object_set(object, "e", ten))
fail("unable to set value");
if(json_object_size(object) != 5)
fail("invalid size");
json_object_clear(object);
if(json_object_size(object) != 0)
fail("invalid size after clear");
json_decref(ten);
json_decref(object);
}
static void test_update()
{
json_t *object, *other, *nine, *ten;
object = json_object();
other = json_object();
nine = json_integer(9);
ten = json_integer(10);
if(!object || !other)
fail("unable to create object");
if(!nine || !ten)
fail("unable to create integer");
/* update an empty object with an empty object */
if(json_object_update(object, other))
fail("unable to update an emtpy object with an empty object");
if(json_object_size(object) != 0)
fail("invalid size after update");
if(json_object_size(other) != 0)
fail("invalid size for updater after update");
/* update an empty object with a nonempty object */
if(json_object_set(other, "a", ten) ||
json_object_set(other, "b", ten) ||
json_object_set(other, "c", ten) ||
json_object_set(other, "d", ten) ||
json_object_set(other, "e", ten))
fail("unable to set value");
if(json_object_update(object, other))
fail("unable to update an empty object");
if(json_object_size(object) != 5)
fail("invalid size after update");
if(json_object_get(object, "a") != ten ||
json_object_get(object, "b") != ten ||
json_object_get(object, "c") != ten ||
json_object_get(object, "d") != ten ||
json_object_get(object, "e") != ten)
fail("update works incorrectly");
/* perform the same update again */
if(json_object_update(object, other))
fail("unable to update an empty object");
if(json_object_size(object) != 5)
fail("invalid size after update");
if(json_object_get(object, "a") != ten ||
json_object_get(object, "b") != ten ||
json_object_get(object, "c") != ten ||
json_object_get(object, "d") != ten ||
json_object_get(object, "e") != ten)
fail("update works incorrectly");
/* update a nonempty object with a nonempty object with both old
and new keys */
if(json_object_clear(other))
fail("clear failed");
if(json_object_set(other, "a", nine) ||
json_object_set(other, "b", nine) ||
json_object_set(other, "f", nine) ||
json_object_set(other, "g", nine) ||
json_object_set(other, "h", nine))
fail("unable to set value");
if(json_object_update(object, other))
fail("unable to update a nonempty object");
if(json_object_size(object) != 8)
fail("invalid size after update");
if(json_object_get(object, "a") != nine ||
json_object_get(object, "b") != nine ||
json_object_get(object, "f") != nine ||
json_object_get(object, "g") != nine ||
json_object_get(object, "h") != nine)
fail("update works incorrectly");
json_decref(nine);
json_decref(ten);
json_decref(other);
json_decref(object);
}
static void test_circular()
{
json_t *object1, *object2;
object1 = json_object();
object2 = json_object();
if(!object1 || !object2)
fail("unable to create object");
/* the simple case is checked */
if(json_object_set(object1, "a", object1) == 0)
fail("able to set self");
/* create circular references */
if(json_object_set(object1, "a", object2) ||
json_object_set(object2, "a", object1))
fail("unable to set value");
/* circularity is detected when dumping */
if(json_dumps(object1, 0) != NULL)
fail("able to dump circulars");
/* decref twice to deal with the circular references */
json_decref(object1);
json_decref(object2);
json_decref(object1);
}
static void test_misc()
{
json_t *object, *string, *other_string, *value;
void *iter;
object = json_object();
string = json_string("test");
@@ -18,9 +178,7 @@ int main()
if(!object)
fail("unable to create object");
if(!string)
fail("unable to create string");
if(!other_string)
if(!string || !other_string)
fail("unable to create string");
if(json_object_get(object, "a"))
@@ -29,6 +187,23 @@ int main()
if(json_object_set(object, "a", string))
fail("unable to set value");
if(!json_object_set(object, NULL, string))
fail("able to set NULL key");
if(!json_object_set(object, "a", NULL))
fail("able to set NULL value");
iter = json_object_iter(object);
if(!iter)
fail("unable to get iterator");
if(strcmp(json_object_iter_key(iter), "a"))
fail("iterating failed: wrong key");
if(json_object_iter_value(iter) != string)
fail("iterating failed: wrong value");
if(json_object_iter_next(object, iter) != NULL)
fail("able to iterate over the end");
/* invalid UTF-8 in key */
if(!json_object_set(object, "a\xefz", string))
fail("able to set invalid unicode key");
@@ -39,7 +214,7 @@ int main()
if(value != string)
fail("got different value than what was added");
/* "a", "lp" and "px" collide with a five-bucket hashtable */
/* "a", "lp" and "px" collide in a five-bucket hashtable */
if(json_object_set(object, "b", string) ||
json_object_set(object, "lp", string) ||
json_object_set(object, "px", string))
@@ -73,7 +248,7 @@ int main()
fail("unable to delete an existing key");
/* add many keys to rehashing */
/* add many keys to initiate rehashing */
if(json_object_set(object, "a", string))
fail("unable to set value");
@@ -93,9 +268,31 @@ int main()
if(json_object_set(object, "e", string))
fail("unable to set value");
if(json_object_set_new(object, "foo", json_integer(123)))
fail("unable to set new value");
value = json_object_get(object, "foo");
if(!json_is_integer(value) || json_integer_value(value) != 123)
fail("json_object_set_new works incorrectly");
if(!json_object_set_new(object, NULL, json_integer(432)))
fail("able to set_new NULL key");
if(!json_object_set_new(object, "foo", NULL))
fail("able to set_new NULL value");
json_decref(string);
json_decref(other_string);
json_decref(object);
}
int main()
{
test_misc();
test_clear();
test_update();
test_circular();
return 0;
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2009 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 <string.h>
#include <jansson.h>
#include "util.h"
/* Call the simple functions not covered by other tests of the public API */
int main()
{
json_t *value;
value = json_integer(1);
if(json_typeof(value) != JSON_INTEGER)
fail("json_typeof failed");
if(json_is_object(value))
fail("json_is_object failed");
if(json_is_array(value))
fail("json_is_array failed");
if(json_is_string(value))
fail("json_is_string failed");
if(!json_is_integer(value))
fail("json_is_integer failed");
if(json_is_real(value))
fail("json_is_real failed");
if(!json_is_number(value))
fail("json_is_number failed");
if(json_is_true(value))
fail("json_is_true failed");
if(json_is_false(value))
fail("json_is_false failed");
if(json_is_boolean(value))
fail("json_is_boolean failed");
if(json_is_null(value))
fail("json_is_null failed");
json_decref(value);
value = json_string("foo");
if(!value)
fail("json_string failed");
if(strcmp(json_string_value(value), "foo"))
fail("invalid string value");
if(json_string_set(value, "bar"))
fail("json_string_set failed");
if(strcmp(json_string_value(value), "bar"))
fail("invalid string value");
json_decref(value);
value = json_string(NULL);
if(value)
fail("json_string(NULL) failed");
/* invalid UTF-8 */
value = json_string("a\xefz");
if(value)
fail("json_string(<invalid utf-8>) failed");
value = json_integer(123);
if(!value)
fail("json_integer failed");
if(json_integer_value(value) != 123)
fail("invalid integer value");
if(json_number_value(value) != 123.0)
fail("invalid number value");
if(json_integer_set(value, 321))
fail("json_integer_set failed");
if(json_integer_value(value) != 321)
fail("invalid integer value");
if(json_number_value(value) != 321.0)
fail("invalid number value");
json_decref(value);
value = json_real(123.123);
if(!value)
fail("json_real failed");
if(json_real_value(value) != 123.123)
fail("invalid integer value");
if(json_number_value(value) != 123.123)
fail("invalid number value");
if(json_real_set(value, 321.321))
fail("json_real_set failed");
if(json_real_value(value) != 321.321)
fail("invalid real value");
if(json_number_value(value) != 321.321)
fail("invalid number value");
json_decref(value);
value = json_true();
if(!value)
fail("json_true failed");
json_decref(value);
value = json_false();
if(!value)
fail("json_false failed");
json_decref(value);
value = json_null();
if(!value)
fail("json_null failed");
json_decref(value);
return 0;
}

View File

@@ -8,10 +8,13 @@
#ifndef TESTPROGS_UTIL_H
#define TESTPROGS_UTIL_H
#include <stdlib.h>
#define fail(msg) \
do { \
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \
return 1; \
fprintf(stderr, "%s:%s:%d: %s\n", \
__FILE__, __FUNCTION__, __LINE__, msg); \
exit(1); \
} while(0)
#endif