Add JSON API for Asterisk.

This provides a JSON API by pulling in and wrapping the Jansson JSON
library[1]. The Asterisk API basically mirrors the Jansson
functionality, with a few minor tweaks.

 * Some names have been asteriskified to protect the innocent.
 * Jansson provides both reference-stealing and reference-borrowing
   versions of several API's. The Asterisk API is exclusively
   reference-stealing for operations that put elements into arrays and
   objects.
 * No support for doubles, since we usually don't need that.
 * Coming along for the ride is the ast_test_validate macro, which made
   the unit tests much easier to write.

 [1]: http://www.digip.org/jansson/

(issue ASTERISK-20887)
(closes issue ASTERISK-20888)
Review: https://reviewboard.asterisk.org/r/2264/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@378915 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
David M. Lee 2013-01-11 22:31:42 +00:00
parent 2db3cc2e26
commit 7695ea2643
12 changed files with 3169 additions and 3 deletions

View File

@ -25,6 +25,7 @@ IODBC=@PBX_IODBC@
ISDNNET=@PBX_ISDNNET@
IXJUSER=@PBX_IXJUSER@
JACK=@PBX_JACK@
JANSSON=@PBX_JANSSON@
KQUEUE=@PBX_KQUEUE@
LDAP=@PBX_LDAP@
LIBEDIT=@PBX_LIBEDIT@

145
configure vendored
View File

@ -1,5 +1,5 @@
#! /bin/sh
# From configure.ac Revision: 377977 .
# From configure.ac Revision: 377981 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for asterisk trunk.
#
@ -974,6 +974,10 @@ PBX_KQUEUE
KQUEUE_DIR
KQUEUE_INCLUDE
KQUEUE_LIB
PBX_JANSSON
JANSSON_DIR
JANSSON_INCLUDE
JANSSON_LIB
PBX_JACK
JACK_DIR
JACK_INCLUDE
@ -1245,6 +1249,7 @@ with_inotify
with_iodbc
with_isdnnet
with_jack
with_jansson
with_kqueue
with_ldap
with_libcurl
@ -1972,6 +1977,7 @@ Optional Packages:
--with-iodbc=PATH use iODBC files in PATH
--with-isdnnet=PATH use ISDN4Linux files in PATH
--with-jack=PATH use Jack Audio Connection Kit files in PATH
--with-jansson=PATH use Jansson JSON library files in PATH
--with-kqueue=PATH use kqueue support files in PATH
--with-ldap=PATH use OpenLDAP files in PATH
--with-libcurl=DIR look for the curl library in DIR
@ -8911,6 +8917,38 @@ fi
JANSSON_DESCRIP="Jansson JSON library"
JANSSON_OPTION="jansson"
PBX_JANSSON=0
# Check whether --with-jansson was given.
if test "${with_jansson+set}" = set; then :
withval=$with_jansson;
case ${withval} in
n|no)
USE_JANSSON=no
# -1 is a magic value used by menuselect to know that the package
# was disabled, other than 'not found'
PBX_JANSSON=-1
;;
y|ye|yes)
ac_mandatory_list="${ac_mandatory_list} JANSSON"
;;
*)
JANSSON_DIR="${withval}"
ac_mandatory_list="${ac_mandatory_list} JANSSON"
;;
esac
fi
KQUEUE_DESCRIP="kqueue support"
KQUEUE_OPTION="kqueue"
PBX_KQUEUE=0
@ -19865,6 +19903,111 @@ fi
if test "x${PBX_JANSSON}" != "x1" -a "${USE_JANSSON}" != "no"; then
pbxlibdir=""
# if --with-JANSSON=DIR has been specified, use it.
if test "x${JANSSON_DIR}" != "x"; then
if test -d ${JANSSON_DIR}/lib; then
pbxlibdir="-L${JANSSON_DIR}/lib"
else
pbxlibdir="-L${JANSSON_DIR}"
fi
fi
pbxfuncname="json_dumps"
if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
AST_JANSSON_FOUND=yes
else
ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} "
as_ac_Lib=`$as_echo "ac_cv_lib_jansson_${pbxfuncname}" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -ljansson" >&5
$as_echo_n "checking for ${pbxfuncname} in -ljansson... " >&6; }
if eval \${$as_ac_Lib+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ljansson ${pbxlibdir} $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char ${pbxfuncname} ();
int
main ()
{
return ${pbxfuncname} ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
eval "$as_ac_Lib=yes"
else
eval "$as_ac_Lib=no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
eval ac_res=\$$as_ac_Lib
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
AST_JANSSON_FOUND=yes
else
AST_JANSSON_FOUND=no
fi
CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
fi
# now check for the header.
if test "${AST_JANSSON_FOUND}" = "yes"; then
JANSSON_LIB="${pbxlibdir} -ljansson "
# if --with-JANSSON=DIR has been specified, use it.
if test "x${JANSSON_DIR}" != "x"; then
JANSSON_INCLUDE="-I${JANSSON_DIR}/include"
fi
JANSSON_INCLUDE="${JANSSON_INCLUDE} "
if test "xjansson.h" = "x" ; then # no header, assume found
JANSSON_HEADER_FOUND="1"
else # check for the header
ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
CPPFLAGS="${CPPFLAGS} ${JANSSON_INCLUDE}"
ac_fn_c_check_header_mongrel "$LINENO" "jansson.h" "ac_cv_header_jansson_h" "$ac_includes_default"
if test "x$ac_cv_header_jansson_h" = xyes; then :
JANSSON_HEADER_FOUND=1
else
JANSSON_HEADER_FOUND=0
fi
CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
fi
if test "x${JANSSON_HEADER_FOUND}" = "x0" ; then
JANSSON_LIB=""
JANSSON_INCLUDE=""
else
if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
JANSSON_LIB=""
fi
PBX_JANSSON=1
cat >>confdefs.h <<_ACEOF
#define HAVE_JANSSON 1
_ACEOF
fi
fi
fi
# BSD (and OS X) equivalent of inotify
if test "x${PBX_KQUEUE}" != "x1" -a "${USE_KQUEUE}" != "no"; then

View File

@ -406,6 +406,7 @@ AST_EXT_LIB_SETUP([INOTIFY], [inotify support], [inotify])
AST_EXT_LIB_SETUP([IODBC], [iODBC], [iodbc])
AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux], [isdnnet])
AST_EXT_LIB_SETUP([JACK], [Jack Audio Connection Kit], [jack])
AST_EXT_LIB_SETUP([JANSSON], [Jansson JSON library], [jansson])
AST_EXT_LIB_SETUP([KQUEUE], [kqueue support], [kqueue])
AST_EXT_LIB_SETUP([LDAP], [OpenLDAP], [ldap])
AST_LIBCURL_CHECK_CONFIG([], [7.10.1])
@ -1846,6 +1847,8 @@ AST_EXT_LIB_CHECK([INOTIFY], [c], [inotify_init], [sys/inotify.h])
AST_EXT_LIB_CHECK([JACK], [jack], [jack_activate], [jack/jack.h])
AST_EXT_LIB_CHECK([JANSSON], [jansson], [json_dumps], [jansson.h])
# BSD (and OS X) equivalent of inotify
AST_EXT_LIB_CHECK([KQUEUE], [c], [kqueue], [sys/event.h])

View File

@ -27,7 +27,7 @@ PACKAGES_DEBIAN="$PACKAGES_DEBIAN libcurl-dev libspeex-dev libspeexdsp-dev libog
PACKAGES_DEBIAN="$PACKAGES_DEBIAN libpq-dev unixodbc-dev libsqlite0-dev libmysqlclient15-dev libneon27-dev libgmime-dev libusb-dev liblua5.1-0-dev lua5.1"
PACKAGES_DEBIAN="$PACKAGES_DEBIAN libopenh323-dev libvpb-dev libgtk2.0-dev libmysqlclient-dev libbluetooth-dev libradiusclient-ng-dev freetds-dev"
PACKAGES_DEBIAN="$PACKAGES_DEBIAN libsnmp-dev libiksemel-dev libcorosync-dev libnewt-dev libpopt-dev libical-dev libspandsp-dev libjack-dev"
PACKAGES_DEBIAN="$PACKAGES_DEBIAN libresample-dev libc-client-dev binutils-dev libsrtp-dev libgsm1-dev libedit-dev doxygen"
PACKAGES_DEBIAN="$PACKAGES_DEBIAN libresample-dev libc-client-dev binutils-dev libsrtp-dev libgsm1-dev libedit-dev doxygen libjansson-dev"
PACKAGES_RH="automake gcc gcc-c++ ncurses-devel openssl-devel libxml2-devel unixODBC-devel libcurl-devel libogg-devel libvorbis-devel speex-devel"
PACKAGES_RH="$PACKAGES_RH spandsp-devel freetds-devel net-snmp-devel iksemel-devel corosynclib-devel newt-devel popt-devel libtool-ltdl-devel lua-devel"
PACKAGES_RH="$PACKAGES_RH libsqlite3x-devel radiusclient-ng-devel portaudio-devel postgresql-devel libresample-devel neon-devel libical-devel"

View File

@ -364,6 +364,9 @@
/* Define to 1 if you have the Jack Audio Connection Kit library. */
#undef HAVE_JACK
/* Define to 1 if you have the Jansson JSON library library. */
#undef HAVE_JANSSON
/* Define to 1 if you have the `kevent64' function. */
#undef HAVE_KEVENT64

762
include/asterisk/json.h Normal file
View File

@ -0,0 +1,762 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2012 - 2013, Digium, Inc.
*
* David M. Lee, II <dlee@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef _ASTERISK_JSON_H
#define _ASTERISK_JSON_H
/*! \file
*
* \brief Asterisk JSON abstraction layer.
* \since 12.0.0
*
* This is a very thin wrapper around the Jansson API. For more details on it, see its
* docs at http://www.digip.org/jansson/doc/2.4/apiref.html.
* \author David M. Lee, II <dlee@digium.com>
*/
/*!@{*/
/*!
* \brief Set custom allocators instead of the standard ast_malloc() and ast_free().
* \since 12.0.0
*
* This is used by the unit tests to do JSON specific memory leak detection. Since it
* affects all users of the JSON library, shouldn't normally be used.
*
* \param malloc_fn Custom allocation function.
* \param free_fn Matching free function.
*/
void ast_json_set_alloc_funcs(void *(*malloc_fn)(size_t), void (*free_fn)(void*));
/*!
* \brief Change alloc funcs back to the resource module defaults.
* \since 12.0.0
*
* If you use ast_json_set_alloc_funcs() to temporarily change the allocator functions
* (i.e., from in a unit test), this function sets them back to ast_malloc() and
* ast_free().
*/
void ast_json_reset_alloc_funcs(void);
/*!
* \struct ast_json
* \brief Abstract JSON element (object, array, string, int, ...).
* \since 12.0.0
*/
struct ast_json;
/*!
* \brief Increase refcount on \a value.
* \since 12.0.0
*
* \param value JSON value to reference.
* \return The given \a value.
*/
struct ast_json *ast_json_ref(struct ast_json *value);
/*!
* \brief Decrease refcount on \a value. If refcount reaches zero, \a value is freed.
* \since 12.0.0
*/
void ast_json_unref(struct ast_json *value);
/*!@}*/
/*!@{*/
/*!
* \brief Valid types of a JSON element.
* \since 12.0.0
*/
enum ast_json_type
{
AST_JSON_OBJECT,
AST_JSON_ARRAY,
AST_JSON_STRING,
AST_JSON_INTEGER,
AST_JSON_REAL,
AST_JSON_TRUE,
AST_JSON_FALSE,
AST_JSON_NULL,
};
/*!
* \brief Get the type of \a value.
* \since 12.0.0
* \param value Value to query.
* \return Type of \a value.
*/
enum ast_json_type ast_json_typeof(const struct ast_json *value);
/*!@}*/
/*!@{*/
/*!
* \brief Get the JSON true value.
* \since 12.0.0
*
* The returned value is a singleton, and does not need to be
* ast_json_unref()'ed.
*
* \return JSON true.
*/
struct ast_json *ast_json_true(void);
/*!
* \brief Get the JSON false value.
* \since 12.0.0
*
* The returned value is a singleton, and does not need to be
* ast_json_unref()'ed.
*
* \return JSON false.
*/
struct ast_json *ast_json_false(void);
/*!
* \brief Get the JSON boolean corresponding to \a value.
* \since 12.0.0
* \return JSON true if value is true (non-zero).
* \return JSON false if value is false (zero).
*/
struct ast_json *ast_json_boolean(int value);
/*!
* \brief Get the JSON null value.
* \since 12.0.0
*
* The returned value is a singleton, and does not need to be
* ast_json_unref()'ed.
*
* \return JSON null.
*/
struct ast_json *ast_json_null(void);
/*!
* \brief Check if \a value is JSON true.
* \since 12.0.0
* \return True (non-zero) if \a value == \ref ast_json_true().
* \return False (zero) otherwise..
*/
int ast_json_is_true(const struct ast_json *value);
/*!
* \brief Check if \a value is JSON false.
* \since 12.0.0
* \return True (non-zero) if \a value == \ref ast_json_false().
* \return False (zero) otherwise.
*/
int ast_json_is_false(const struct ast_json *value);
/*!
* \brief Check if \a value is JSON null.
* \since 12.0.0
* \return True (non-zero) if \a value == \ref ast_json_false().
* \return False (zero) otherwise.
*/
int ast_json_is_null(const struct ast_json *value);
/*!@}*/
/*!@{*/
/*!
* \brief Construct a JSON string from \a value.
* \since 12.0.0
*
* The given \a value must be a valid ASCII or UTF-8 encoded string.
*
* \param value Value of new JSON string.
* \return Newly constructed string element.
* \return \c NULL on error.
*/
struct ast_json *ast_json_string_create(const char *value);
/*!
* \brief Get the value of a JSON string.
* \since 12.0.0
* \param string JSON string.
* \return Value of the string.
* \return \c NULL on error.
*/
const char *ast_json_string_get(const struct ast_json *string);
/*!
* \brief Change the value of a JSON string.
* \since 12.0.0
*
* The given \a value must be a valid ASCII or UTF-8 encoded string.
*
* \param string JSON string to modify.
* \param value New value to store in \a string.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_string_set(struct ast_json *string, const char *value);
/*!
* \brief Create a JSON string, printf style.
* \since 12.0.0
*
* The formatted value must be a valid ASCII or UTF-8 encoded string.
*
* \param format \c printf style format string.
* \return Newly allocated string.
* \return \c NULL on error.
*/
struct ast_json *ast_json_stringf(const char *format, ...) __attribute__((format(printf, 1, 2)));
/*!
* \brief Create a JSON string, vprintf style.
* \since 12.0.0
*
* The formatted value must be a valid ASCII or UTF-8 encoded string.
*
* \param format \c printf style format string.
* \return Newly allocated string.
* \return \c NULL on error.
*/
struct ast_json *ast_json_vstringf(const char *format, va_list args) __attribute__((format(printf, 1, 0)));
/*!@}*/
/*!@{*/
/*!
* \brief Create a JSON integer.
* \since 12.0.0
* \param value Value of the new JSON integer.
* \return Newly allocated integer.
* \return \c NULL on error.
*/
struct ast_json *ast_json_integer_create(intmax_t value);
/*!
* \brief Get the value from a JSON integer.
* \since 12.0.0
* \param integer JSON integer.
* \return Value of a JSON integer.
* \return 0 if \a integer is not a JSON integer.
*/
intmax_t ast_json_integer_get(const struct ast_json *integer);
/*!
* \brief Set the value of a JSON integer.
* \since 12.0.0
* \param integer JSON integer to modify.
* \param value New value for \a integer.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_integer_set(struct ast_json *integer, intmax_t value);
/*!@}*/
/*!@{*/
/*!
* \brief Create a empty JSON array.
* \since 12.0.0
* \return Newly allocated array.
* \return \c NULL on error.
*/
struct ast_json *ast_json_array_create(void);
/*!
* \brief Get the size of a JSON array.
* \since 12.0.0
* \param array JSON array.
* \return Size of \a array.
* \return 0 if array is not a JSON array.
*/
size_t ast_json_array_size(const struct ast_json *array);
/*!
* \brief Get an element from an array.
* \since 12.0.0
*
* The returned element is a borrowed reference; use ast_json_ref() to safely keep a
* pointer to it.
*
* \param array JSON array.
* \param index Zero-based index into \a array.
* \return The specified element.
* \return \c NULL if \a array not an array.
* \return \c NULL if \a index is out of bounds.
*/
struct ast_json *ast_json_array_get(const struct ast_json *array, size_t index);
/*!
* \brief Change an element in an array.
* \since 12.0.0
*
* The \a array steals the \a value reference; use ast_json_ref() to safely keep a pointer
* to it.
*
* \param array JSON array to modify.
* \param index Zero-based index into array.
* \param value New JSON value to store in \a array at \a index.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_array_set(struct ast_json *array, size_t index, struct ast_json *value);
/*!
* \brief Append to an array.
* \since 12.0.0
*
* The array steals the \a value reference; use ast_json_ref() to safely keep a pointer
* to it.
*
* \param array JSON array to modify.
* \param value New JSON value to store at the end of \a array.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_array_append(struct ast_json *array, struct ast_json *value);
/*!
* \brief Insert into an array.
* \since 12.0.0
*
* The array steals the \a value reference; use ast_json_ref() to safely keep a pointer
* to it.
*
* \param array JSON array to modify.
* \param index Zero-based index into array.
* \param value New JSON value to store in \a array at \a index.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_array_insert(struct ast_json *array, size_t index, struct ast_json *value);
/*!
* \brief Remove an element from an array.
* \since 12.0.0
* \param array JSON array to modify.
* \param index Zero-based index into array.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_array_remove(struct ast_json *array, size_t index);
/*!
* \brief Remove all elements from an array.
* \since 12.0.0
* \param array JSON array to clear.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_array_clear(struct ast_json *array);
/*!
* \brief Append all elements from \a tail to \a array.
* \since 12.0.0
*
* The \a tail argument is not changed, so ast_json_unref() it when you are done with it.
*
* \param array JSON array to modify.
* \param tail JSON array with contents to append to \a array.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_array_extend(struct ast_json *array, struct ast_json *tail);
/*!@}*/
/*!@{*/
/*!
* \brief Create a new JSON object.
* \since 12.0.0
* \return Newly allocated object.
* \return \c NULL on error.
*/
struct ast_json *ast_json_object_create(void);
/*!
* \brief Get size of JSON object.
* \since 12.0.0
* \param object JSON object.
* \return Size of \a object.
* \return Zero of \a object is not a JSON object.
*/
size_t ast_json_object_size(struct ast_json *object);
/*!
* \brief Get a field from a JSON object.
* \since 12.0.0
*
* The returned element is a borrowed reference; use ast_json_ref() to safely keep a
* pointer to it.
*
* \param object JSON object.
* \param key Key of field to look up.
* \return Value with given \a key.
* \return \c NULL on error.
*/
struct ast_json *ast_json_object_get(struct ast_json *object, const char *key);
/*!
* \brief Set a field in a JSON object.
* \since 12.0.0
*
* The object steals the \a value reference; use ast_json_ref() to safely keep a pointer
* to it.
*
* \param object JSON object to modify.
* \param key Key of field to set.
* \param value JSON value to set for field.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value);
/*!
* \brief Delete a field from a JSON object.
* \since 12.0.0
*
* \param object JSON object to modify.
* \param key Key of field to delete.
* \return 0 on success, or -1 if key does not exist.
*/
int ast_json_object_del(struct ast_json *object, const char *key);
/*!
* \brief Delete all elements from a JSON object.
* \since 12.0.0
* \param object JSON object to clear.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_object_clear(struct ast_json *object);
/*!
* \brief Update \a object with all of the fields of \a other.
* \since 12.0.0
*
* All of the fields of \a other are copied into \a object, overwriting existing keys.
* The \a other object is not changed, so ast_json_unref() it when you are done with it.
*
* \param object JSON object to modify.
* \param other JSON object to copy into \a object.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_object_update(struct ast_json *object, struct ast_json *other);
/*!
* \brief Update existing fields in \a object with the fields of \a other.
* \since 12.0.0
*
* Like ast_json_object_update(), but only existing fields are updated. No new fields
* will get added. The \a other object is not changed, so ast_json_unref() it when you
* are done with it.
*
* \param object JSON object to modify.
* \param other JSON object to copy into \a object.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_object_update_existing(struct ast_json *object, struct ast_json *other);
/*!
* \brief Add new fields to \a object with the fields of \a other.
* \since 12.0.0
*
* Like ast_json_object_update(), but only missing fields are added. No existing fields
* will be modified. The \a other object is not changed, so ast_json_unref() it when you
* are done with it.
*
* \param object JSON object to modify.
* \param other JSON object to copy into \a object.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_object_update_missing(struct ast_json *object, struct ast_json *other);
/*!
* \struct ast_json_iter
* \brief Iterator for JSON object key/values.
* \since 12.0.0
*
* Note that iteration order is not specified, and may change as fields are added to
* and removed from the object.
*/
struct ast_json_iter;
/*!
* \brief Get an iterator pointing to the first field in a JSON object.
* \since 12.0.0
*
* The order of the fields in an object are not specified. However, iterating forward
* from this iterator will cover all fields in \a object. Adding or removing fields from
* \a object may invalidate its iterators.
*
* \param object JSON object.
* \return Iterator to the first field in \a object.
* \return \c NULL \a object is empty.
* \return \c NULL on error.
*/
struct ast_json_iter *ast_json_object_iter(struct ast_json *object);
/*!
* \brief Get an iterator pointing to a specified \a key in \a object.
* \since 12.0.0
*
* Iterating forward from this iterator may not to cover all elements in \a object.
*
* \param object JSON object to iterate.
* \param key Key of field to lookup.
* \return Iterator pointing to the field with the given \a key.
* \return \c NULL if \a key does not exist.
* \return \c NULL on error.
*/
struct ast_json_iter *ast_json_object_iter_at(struct ast_json *object, const char *key);
/*!
* \brief Get the next iterator.
* \since 12.0.0
* \param object JSON object \a iter was obtained from.
* \param iter JSON object iterator.
* \return Iterator to next field in \a object.
* \return \c NULL if \a iter was the last field.
*/
struct ast_json_iter *ast_json_object_iter_next(struct ast_json *object, struct ast_json_iter *iter);
/*!
* \brief Get the key from an iterator.
* \since 12.0.0
* \param iter JSON object iterator.
* \return Key of the field \a iter points to.
*/
const char *ast_json_object_iter_key(struct ast_json_iter *iter);
/*!
* \brief Get the value from an iterator.
* \since 12.0.0
*
* The returned element is a borrowed reference; use ast_json_ref() to safely
* keep a pointer to it.
*
* \param iter JSON object iterator.
* \return Value of the field \a iter points to.
*/
struct ast_json *ast_json_object_iter_value(struct ast_json_iter *iter);
/*!
* \brief Set the value of the field pointed to by an iterator.
* \since 12.0.0
*
* The array steals the value reference; use ast_json_ref() to safely keep a
* pointer to it.
*
* \param object JSON object \a iter was obtained from.
* \param iter JSON object iterator.
* \param value JSON value to store in \iter's field.
* \return 0 on success.
* \return -1 on error.
*/
int ast_json_object_iter_set(struct ast_json *object, struct ast_json_iter *iter, struct ast_json *value);
/*!@}*/
/*!@{*/
/*!
* \brief Encode a JSON value to a string.
* \since 12.0.0
*
* Returned string must be freed by calling ast_free().
*
* \param JSON value.
* \return String encoding of \a root.
* \return \c NULL on error.
*/
char *ast_json_dump_string(struct ast_json *root);
/*!
* \brief Encode a JSON value to an \ref ast_str.
* \since 12.0.0
*
* If \a dst is too small, it will be grown as needed.
*
* \param root JSON value.
* \param dst \ref ast_str to store JSON encoding.
* \return 0 on success.
* \return -1 on error. The contents of \a dst are undefined.
*/
int ast_json_dump_str(struct ast_json *root, struct ast_str **dst);
/*!
* \brief Encode a JSON value to a \c FILE.
* \since 12.0.0
*
* \param root JSON value.
* \param output File to write JSON encoding to.
* \return 0 on success.
* \return -1 on error. The contents of \a output are undefined.
*/
int ast_json_dump_file(struct ast_json *root, FILE *output);
/*!
* \brief Encode a JSON value to a file at the given location.
* \since 12.0.0
*
* \param root JSON value.
* \param path Path to file to write JSON encoding to.
* \return 0 on success.
* \return -1 on error. The contents of \a output are undefined.
*/
int ast_json_dump_new_file(struct ast_json *root, const char *path);
#define AST_JSON_ERROR_TEXT_LENGTH 160
#define AST_JSON_ERROR_SOURCE_LENGTH 80
/*!
* \brief JSON parsing error information.
* \since 12.0.0
*/
struct ast_json_error {
/*! Line number error occured on */
int line;
/*! Character (not byte, can be different for UTF-8) column on which the error occurred. */
int column;
/*! Position in bytes from start of input */
int position;
/*! Error message */
char text[AST_JSON_ERROR_TEXT_LENGTH];
/*! Source of the error (filename or <string>) */
char source[AST_JSON_ERROR_TEXT_LENGTH];
};
/*!
* \brief Parse null terminated string into a JSON object or array.
* \since 12.0.0
* \param input String to parse.
* \param[out] error Filled with information on error.
* \return Parsed JSON element.
* \return \c NULL on error.
*/
struct ast_json *ast_json_load_string(const char *input, struct ast_json_error *error);
/*!
* \brief Parse \ref ast_str into a JSON object or array.
* \since 12.0.0
* \param input \ref ast_str to parse.
* \param[out] error Filled with information on error.
* \return Parsed JSON element.
* \return \c NULL on error.
*/
struct ast_json *ast_json_load_str(const struct ast_str *input, struct ast_json_error *error);
/*!
* \brief Parse buffer with known length into a JSON object or array.
* \since 12.0.0
* \param buffer Buffer to parse.
* \param buflen Length of \a buffer.
* \param[out] error Filled with information on error.
* \return Parsed JSON element.
* \return \c NULL on error.
*/
struct ast_json *ast_json_load_buf(const char *buffer, size_t buflen, struct ast_json_error *error);
/*!
* \brief Parse a \c FILE into JSON object or array.
* \since 12.0.0
* \param input \c FILE to parse.
* \param[out] error Filled with information on error.
* \return Parsed JSON element.
* \return \c NULL on error.
*/
struct ast_json *ast_json_load_file(FILE *input, struct ast_json_error *error);
/*!
* \brief Parse file at \a path into JSON object or array.
* \since 12.0.0
* \param path Path of file to parse.
* \param[out] error Filled with information on error.
* \return Parsed JSON element.
* \return \c NULL on error.
*/
struct ast_json *ast_json_load_new_file(const char *path, struct ast_json_error *error);
/*!
* \brief Helper for creating complex JSON values.
* \since 12.0.0
*
* See original Jansson docs at http://www.digip.org/jansson/doc/2.4/apiref.html#apiref-pack
* for more details.
*/
struct ast_json *ast_json_pack(char const *format, ...);
/*!
* \brief Helper for creating complex JSON values simply.
* \since 12.0.0
*
* See original Jansson docs at http://www.digip.org/jansson/doc/2.4/apiref.html#apiref-pack
* for more details.
*/
struct ast_json *ast_json_vpack(char const *format, va_list ap);
/*!@}*/
/*!@{*/
/*!
* \brief Compare two JSON objects.
* \since 12.0.0
*
* Two JSON objects are equal if they are of the same type, and their contents are equal.
*
* \param lhs Value to compare.
* \param rhs Other value to compare.
* \return True (non-zero) if \a lhs and \a rhs are equal.
* \return False (zero) if they are not.
*/
int ast_json_equal(const struct ast_json *lhs, const struct ast_json *rhs);
/*!
* \brief Copy a JSON value, but not its children.
* \since 12.0.0
*
* If \a value is a JSON object or array, its children are shared with the returned copy.
*
* \param value JSON value to copy.
* \return Shallow copy of \a value.
* \return \c NULL on error.
*/
struct ast_json *ast_json_copy(const struct ast_json *value);
/*!
* \brief Copy a JSON value, and its children.
* \since 12.0.0
*
* If \a value is a JSON object or array, they are also copied.
*
* \param value JSON value to copy.
* \return Deep copy of \a value.
* \return \c NULL on error.
*/
struct ast_json *ast_json_deep_copy(const struct ast_json *value);
/*!@}*/
#endif /* _ASTERISK_JSON_H */

View File

@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2009-2010, Digium, Inc.
* Copyright (C) 2009-2013, Digium, Inc.
*
* David Vossel <dvossel@digium.com>
* Russell Bryant <russell@digium.com>
@ -283,5 +283,31 @@ int __ast_test_status_update(const char *file, const char *func, int line, struc
*/
#define ast_test_status_update(t, f, ...) __ast_test_status_update(__FILE__, __PRETTY_FUNCTION__, __LINE__, (t), (f), ## __VA_ARGS__)
/*!
* \brief Check a test condition, failing the test if it's not true.
*
* \since 12.0.0
*
* This macro evaluates \a condition. If the condition evaluates to true (non-zero),
* nothing happens. If it evaluates to false (zero), then the failure is printed
* using \ref ast_test_status_update, and the current test is ended with AST_TEST_FAIL.
*
* Sadly, the name 'ast_test_assert' was already taken.
*
* Note that since this macro returns from the current test, there must not be any
* cleanup work to be done before returning. Use \ref RAII_VAR for test cleanup.
*
* \param \a test Currently executing test
* \param \a condition Boolean condition to check.
*/
#define ast_test_validate(test, condition) \
do { \
if (!(condition)) { \
__ast_test_status_update(__FILE__, __PRETTY_FUNCTION__, __LINE__, (test), "Condition failed: %s\n", #condition); \
return AST_TEST_FAIL; \
} \
} while(0)
#endif /* TEST_FRAMEWORK */
#endif /* _AST_TEST_H */

View File

@ -35,6 +35,7 @@ AST_LIBS+=$(BKTR_LIB)
AST_LIBS+=$(LIBXML2_LIB)
AST_LIBS+=$(SQLITE3_LIB)
AST_LIBS+=$(ASTSSL_LIBS)
AST_LIBS+=$(JANSSON_LIB)
ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc linux-gnueabi kfreebsd-gnu linux-gnueabihf),)
ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)

View File

@ -169,6 +169,9 @@ IODBC_LIB=@IODBC_LIB@
JACK_INCLUDE=@JACK_INCLUDE@
JACK_LIB=@JACK_LIB@
JANSSON_INCLUDE=@JANSSON_INCLUDE@
JANSSON_LIB=@JANSSON_LIB@
LDAP_INCLUDE=@LDAP_INCLUDE@
LDAP_LIB=@LDAP_LIB@

517
res/res_json.c Normal file
View File

@ -0,0 +1,517 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2012 - 2013, Digium, Inc.
*
* David M. Lee, II <dlee@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief JSON abstraction layer.
*
* This is a very thin wrapper around the Jansson API. For more details on it, see its
* docs at http://www.digip.org/jansson/doc/2.4/apiref.html.
*
* \author David M. Lee, II <dlee@digium.com>
*/
/*** MODULEINFO
<depend>jansson</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/json.h"
#include "asterisk/module.h"
#include "asterisk/utils.h"
#include <jansson.h>
/*!
* \brief Function wrapper around ast_malloc macro.
*/
static void *json_malloc(size_t size)
{
return ast_malloc(size);
}
/*!
* \brief Function wrapper around ast_free macro.
*/
static void json_free(void *p)
{
ast_free(p);
}
void ast_json_set_alloc_funcs(void *(*malloc_fn)(size_t), void (*free_fn)(void*))
{
json_set_alloc_funcs(malloc_fn, free_fn);
}
void ast_json_reset_alloc_funcs(void)
{
json_set_alloc_funcs(json_malloc, json_free);
}
struct ast_json *ast_json_ref(struct ast_json *json)
{
json_incref((json_t *)json);
return json;
}
void ast_json_unref(struct ast_json *json)
{
json_decref((json_t *)json);
}
enum ast_json_type ast_json_typeof(const struct ast_json *json)
{
int r = json_typeof((json_t*)json);
switch(r) {
case JSON_OBJECT: return AST_JSON_OBJECT;
case JSON_ARRAY: return AST_JSON_ARRAY;
case JSON_STRING: return AST_JSON_STRING;
case JSON_INTEGER: return AST_JSON_INTEGER;
case JSON_REAL: return AST_JSON_REAL;
case JSON_TRUE: return AST_JSON_TRUE;
case JSON_FALSE: return AST_JSON_FALSE;
case JSON_NULL: return AST_JSON_NULL;
}
ast_assert(0); /* Unexpect return from json_typeof */
return r;
}
struct ast_json *ast_json_true(void)
{
return (struct ast_json *)json_true();
}
struct ast_json *ast_json_false(void)
{
return (struct ast_json *)json_false();
}
struct ast_json *ast_json_boolean(int value)
{
#if JANSSON_VERSION_HEX >= 0x020400
return (struct ast_json *)json_boolean(value);
#else
return value ? ast_json_true() : ast_json_false();
#endif
}
struct ast_json *ast_json_null(void)
{
return (struct ast_json *)json_null();
}
int ast_json_is_true(const struct ast_json *json)
{
return json_is_true((const json_t *)json);
}
int ast_json_is_false(const struct ast_json *json)
{
return json_is_false((const json_t *)json);
}
int ast_json_is_null(const struct ast_json *json)
{
return json_is_null((const json_t *)json);
}
struct ast_json *ast_json_string_create(const char *value)
{
return (struct ast_json *)json_string(value);
}
const char *ast_json_string_get(const struct ast_json *string)
{
return json_string_value((json_t *)string);
}
int ast_json_string_set(struct ast_json *string, const char *value)
{
return json_string_set((json_t *)string, value);
}
struct ast_json *ast_json_stringf(const char *format, ...)
{
struct ast_json *ret;
va_list args;
va_start(args, format);
ret = ast_json_vstringf(format, args);
va_end(args);
return ret;
}
struct ast_json *ast_json_vstringf(const char *format, va_list args)
{
char *str = NULL;
json_t *ret = NULL;
if (format) {
int err = vasprintf(&str, format, args);
if (err > 0) {
ret = json_string(str);
free(str);
}
}
return (struct ast_json *)ret;
}
struct ast_json *ast_json_integer_create(intmax_t value)
{
return (struct ast_json *)json_integer(value);
}
intmax_t ast_json_integer_get(const struct ast_json *integer)
{
return json_integer_value((json_t *)integer);
}
int ast_json_integer_set(struct ast_json *integer, intmax_t value)
{
return json_integer_set((json_t *)integer, value);
}
int ast_json_equal(const struct ast_json *lhs, const struct ast_json *rhs)
{
return json_equal((json_t *)lhs, (json_t *)rhs);
}
struct ast_json *ast_json_array_create(void)
{
return (struct ast_json *)json_array();
}
size_t ast_json_array_size(const struct ast_json *array)
{
return json_array_size((json_t *)array);
}
struct ast_json *ast_json_array_get(const struct ast_json *array, size_t index)
{
return (struct ast_json *)json_array_get((json_t *)array, index);
}
int ast_json_array_set(struct ast_json *array, size_t index, struct ast_json *value)
{
return json_array_set_new((json_t *)array, index, (json_t *)value);
}
int ast_json_array_append(struct ast_json *array, struct ast_json *value)
{
return json_array_append_new((json_t *)array, (json_t *)value);
}
int ast_json_array_insert(struct ast_json *array, size_t index, struct ast_json *value)
{
return json_array_insert_new((json_t *)array, index, (json_t *)value);
}
int ast_json_array_remove(struct ast_json *array, size_t index)
{
return json_array_remove((json_t *)array, index);
}
int ast_json_array_clear(struct ast_json *array)
{
return json_array_clear((json_t *)array);
}
int ast_json_array_extend(struct ast_json *array, struct ast_json *tail)
{
return json_array_extend((json_t *)array, (json_t *)tail);
}
struct ast_json *ast_json_object_create(void)
{
return (struct ast_json *)json_object();
}
size_t ast_json_object_size(struct ast_json *object)
{
return json_object_size((json_t *)object);
}
struct ast_json *ast_json_object_get(struct ast_json *object, const char *key)
{
if (key) {
return (struct ast_json *)json_object_get((json_t *)object, key);
}
return NULL;
}
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
{
return json_object_set_new((json_t *)object, key, (json_t *)value);
}
int ast_json_object_del(struct ast_json *object, const char *key)
{
return json_object_del((json_t *)object, key);
}
int ast_json_object_clear(struct ast_json *object)
{
return json_object_clear((json_t *)object);
}
int ast_json_object_update(struct ast_json *object, struct ast_json *other)
{
return json_object_update((json_t *)object, (json_t *)other);
}
int ast_json_object_update_existing(struct ast_json *object, struct ast_json *other)
{
#if JANSSON_VERSION_HEX >= 0x020300
return json_object_update_existing((json_t *)object, (json_t *)other);
#else
struct ast_json_iter *iter = ast_json_object_iter(other);
int ret = 0;
if (object == NULL || other == NULL) {
return -1;
}
while (iter != NULL && ret == 0) {
const char *key = ast_json_object_iter_key(iter);
if (ast_json_object_get(object, key) != NULL) {
ret = ast_json_object_set(object, key, ast_json_object_iter_value(iter));
}
iter = ast_json_object_iter_next(other, iter);
}
return ret;
#endif
}
int ast_json_object_update_missing(struct ast_json *object, struct ast_json *other)
{
#if JANSSON_VERSION_HEX >= 0x020300
return json_object_update_missing((json_t *)object, (json_t *)other);
#else
struct ast_json_iter *iter = ast_json_object_iter(other);
int ret = 0;
if (object == NULL || other == NULL) {
return -1;
}
while (iter != NULL && ret == 0) {
const char *key = ast_json_object_iter_key(iter);
if (ast_json_object_get(object, key) == NULL) {
ret = ast_json_object_set(object, key, ast_json_object_iter_value(iter));
}
iter = ast_json_object_iter_next(other, iter);
}
return ret;
#endif
}
struct ast_json_iter *ast_json_object_iter(struct ast_json *object)
{
return json_object_iter((json_t *)object);
}
struct ast_json_iter *ast_json_object_iter_at(struct ast_json *object, const char *key)
{
return json_object_iter_at((json_t *)object, key);
}
struct ast_json_iter *ast_json_object_iter_next(struct ast_json *object, struct ast_json_iter *iter)
{
return json_object_iter_next((json_t *)object, iter);
}
const char *ast_json_object_iter_key(struct ast_json_iter *iter)
{
return json_object_iter_key(iter);
}
struct ast_json *ast_json_object_iter_value(struct ast_json_iter *iter)
{
return (struct ast_json *)json_object_iter_value(iter);
}
int ast_json_object_iter_set(struct ast_json *object, struct ast_json_iter *iter, struct ast_json *value)
{
return json_object_iter_set_new((json_t *)object, iter, (json_t *)value);
}
/*!
* \brief Default flags for JSON encoding.
*/
static size_t dump_flags(void)
{
/* There's a chance this could become a runtime flag */
int flags = JSON_COMPACT;
#ifdef AST_DEVMODE
/* In dev mode, write readable JSON */
flags = JSON_INDENT(2) | JSON_PRESERVE_ORDER;
#endif
return flags;
}
char *ast_json_dump_string(struct ast_json *root)
{
return json_dumps((json_t *)root, dump_flags());
}
static int write_to_ast_str(const char *buffer, size_t size, void *data)
{
struct ast_str **dst = data;
size_t str_size = ast_str_size(*dst);
size_t remaining = str_size - ast_str_strlen(*dst);
int ret;
/* While ast_str_append will grow the ast_str, it won't report
* allocation errors. Fortunately, it's not that hard.
*/
/* Remaining needs to be big enough for buffer, plus null char */
while (remaining < size + 1) {
/* doubling the size of the buffer gives us 'amortized
* constant' time.
* See http://stackoverflow.com/a/249695/115478 for info.
*/
str_size *= 2;
remaining = str_size - ast_str_strlen(*dst);
}
ret = ast_str_make_space(dst, str_size);
if (ret == -1) {
/* Could not alloc; fail */
return -1;
}
ast_str_append_substr(dst, -1, buffer, size);
return 0;
}
int ast_json_dump_str(struct ast_json *root, struct ast_str **dst)
{
return json_dump_callback((json_t *)root, write_to_ast_str, dst, dump_flags());
}
int ast_json_dump_file(struct ast_json *root, FILE *output)
{
if (root && output) {
return json_dumpf((json_t *)root, output, dump_flags());
}
return -1;
}
int ast_json_dump_new_file(struct ast_json *root, const char *path)
{
return json_dump_file((json_t *)root, path, dump_flags());
}
/*!
* \brief Copy Jansson error struct to ours.
*/
static void copy_error(struct ast_json_error *error, const json_error_t *jansson_error)
{
if (error && jansson_error) {
error->line = jansson_error->line;
error->column = jansson_error->column;
error->position = jansson_error->position;
ast_copy_string(error->text, jansson_error->text, sizeof(error->text));
ast_copy_string(error->source, jansson_error->source, sizeof(error->source));
}
}
static void parse_error(struct ast_json_error *error, const char *text, const char *source)
{
if (error != NULL) {
error->line = 0;
error->column = 0;
error->position = 0;
strncpy(error->text, text, sizeof(error->text));
strncpy(error->source, source, sizeof(error->text));
}
}
struct ast_json *ast_json_load_string(const char *input, struct ast_json_error *error)
{
json_error_t jansson_error = {};
struct ast_json *r = NULL;
if (input != NULL) {
r = (struct ast_json *)json_loads(input, 0, &jansson_error);
copy_error(error, &jansson_error);
} else {
parse_error(error, "NULL input string", "<null>");
}
return r;
}
struct ast_json *ast_json_load_str(const struct ast_str *input, struct ast_json_error *error)
{
return ast_json_load_string(ast_str_buffer(input), error);
}
struct ast_json *ast_json_load_buf(const char *buffer, size_t buflen, struct ast_json_error *error)
{
json_error_t jansson_error = {};
struct ast_json *r = (struct ast_json *)json_loadb(buffer, buflen, 0, &jansson_error);
copy_error(error, &jansson_error);
return r;
}
struct ast_json *ast_json_load_file(FILE *input, struct ast_json_error *error)
{
json_error_t jansson_error = {};
struct ast_json *r = NULL;
if (input != NULL) {
r = (struct ast_json *)json_loadf(input, 0, &jansson_error);
copy_error(error, &jansson_error);
} else {
parse_error(error, "NULL input file", "<null>");
}
return r;
}
struct ast_json *ast_json_load_new_file(const char *path, struct ast_json_error *error)
{
json_error_t jansson_error = {};
struct ast_json *r = (struct ast_json *)json_load_file(path, 0, &jansson_error);
copy_error(error, &jansson_error);
return r;
}
struct ast_json *ast_json_pack(char const *format, ...)
{
struct ast_json *ret;
va_list args;
va_start(args, format);
ret = ast_json_vpack(format, args);
va_end(args);
return ret;
}
struct ast_json *ast_json_vpack(char const *format, va_list ap)
{
struct ast_json *r = NULL;
if (format) {
r = (struct ast_json *)json_vpack_ex(NULL, 0, format, ap);
}
return r;
}
struct ast_json *ast_json_copy(const struct ast_json *value)
{
return (struct ast_json *)json_copy((json_t *)value);
}
struct ast_json *ast_json_deep_copy(const struct ast_json *value)
{
return (struct ast_json *)json_deep_copy((json_t *)value);
}
static int unload_module(void)
{
/* Nothing to do */
return 0;
}
static int load_module(void)
{
/* Setup to use Asterisk custom allocators */
ast_json_reset_alloc_funcs();
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "JSON library",
.load = load_module,
.unload = unload_module);

6
res/res_json.exports.in Normal file
View File

@ -0,0 +1,6 @@
{
global:
LINKER_SYMBOL_PREFIXast_json_*;
local:
*;
};

1701
tests/test_json.c Normal file

File diff suppressed because it is too large Load Diff