87 enum json_option_flags {
95 #define MAX_JSON_STACK 32
97 static int parse_node(
char **key,
char *currentkey,
char *nestchar,
int count,
struct ast_json *json,
char *buf,
size_t len,
int *depth)
99 const char *result = NULL;
105 if (*depth > MAX_JSON_STACK) {
106 ast_log(LOG_WARNING,
"Max JSON stack (%d) exceeded\n", MAX_JSON_STACK);
110 snprintf(buf, len,
"%s",
"");
112 ast_debug(1,
"Could not find key '%s' in parsed JSON\n", currentkey);
117 unsigned long int size;
121 case AST_JSON_STRING:
123 ast_debug(1,
"Got JSON string: %s\n", result);
126 case AST_JSON_INTEGER:
128 ast_debug(1,
"Got JSON integer: %d\n", r);
129 snprintf(buf, len,
"%d", r);
133 ast_debug(1,
"Got JSON real: %.17g\n", d);
134 snprintf(buf, len,
"%.17g", d);
138 previouskey = currentkey;
139 currentkey = strsep(key, nestchar);
141 ast_debug(1,
"Parsed JSON array of size %lu, key: %s\n", size, currentkey);
144 ast_debug(1,
"No key on which to index in the array, so returning count: %lu\n", size);
145 snprintf(buf, len,
"%lu", size);
149 ast_debug(1,
"No key on which to index in the array, so dumping '%s' array\n", previouskey);
154 ast_debug(1,
"Requested index '%s' is not numeric or is invalid\n", currentkey);
155 }
else if (r >= size) {
156 ast_debug(1,
"Requested index '%d' does not exist in parsed array\n", r);
158 ast_debug(1,
"Recursing on index %d in array\n", r);
159 if (parse_node(key, currentkey, nestchar, count,
ast_json_array_get(jsonval, r), buf, len, depth)) {
167 ast_debug(1,
"Got JSON %s for key %s\n", r ?
"true" :
"false", currentkey);
168 snprintf(buf, len,
"%d", r);
171 ast_debug(1,
"Got JSON null for key %s\n", currentkey);
173 case AST_JSON_OBJECT:
174 ast_debug(1,
"Got generic JSON object for key %s\n", currentkey);
175 previouskey = currentkey;
176 currentkey = strsep(key, nestchar);
182 ast_debug(1,
"Recursing on object (key was '%s' and is now '%s')\n", previouskey, currentkey);
183 if (parse_node(key, currentkey, nestchar, count,
ast_json_object_get(jsonval, currentkey), buf, len, depth)) {
189 ast_log(LOG_WARNING,
"Got unsuported type %d\n",
ast_json_typeof(jsonval));
195 static int json_decode_read(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
199 struct ast_json *json = NULL, *start = NULL;
200 char *nestchar =
".";
201 int index, res, depth = 0;
209 char *varsubst, *key, *currentkey, *nextkey, *firstkey, *tmp;
214 if (!ast_strlen_zero(args.options)) {
216 if (ast_test_flag(&flags, OPT_COUNT)) {
221 if (ast_strlen_zero(args.varname)) {
222 ast_log(LOG_WARNING,
"%s requires a variable name\n", cmd);
226 if (ast_strlen_zero(args.key)) {
227 ast_log(LOG_WARNING,
"%s requires a key\n", cmd);
232 if (!ast_strlen_zero(args.nestchar)) {
233 int seplen = strlen(args.nestchar);
235 ast_log(LOG_WARNING,
"Nesting separator '%s' has length %d and is invalid (must be a single character)\n", args.nestchar, seplen);
237 nestchar = args.nestchar;
241 varsubst =
ast_alloca(strlen(args.varname) + 4);
243 ast_log(LOG_ERROR,
"Failed to allocate string\n");
246 sprintf(varsubst,
"${%s}", args.varname);
249 ast_debug(1,
"Parsing JSON using nesting delimeter '%s'\n", nestchar);
252 ast_debug(1,
"Variable '%s' contains no data, nothing to search!\n", args.varname);
259 tmp = strstr(firstkey, nestchar);
266 if (ast_strlen_zero(currentkey)) {
271 ast_debug(1,
"JSON node '%s', contains no data, nothing to search!\n", currentkey);
277 ast_log(LOG_WARNING,
"Failed to parse as JSON: %s\n",
ast_str_buffer(str));
282 nextkey = strsep(&key, nestchar);
287 ast_debug(1,
"Requested index '%s' is not numeric or is invalid\n", currentkey);
293 res = parse_node(&key, nextkey, nestchar, count, start, buf, len, &depth);
299 .
name =
"JSON_DECODE",
300 .read = json_decode_read,
303 #ifdef TEST_FRAMEWORK
306 int i, res = AST_TEST_PASS;
310 const char *test_strings[][6] = {
311 {
"{\"myboolean\": true, \"state\": \"USA\"}",
"",
"myboolean",
"1"},
312 {
"{\"myboolean\": false, \"state\": \"USA\"}",
"",
"myboolean",
"0"},
313 {
"{\"myreal\": 1E+2, \"state\": \"USA\"}",
"",
"myreal",
"100"},
314 {
"{\"myreal\": 1.23, \"state\": \"USA\"}",
"",
"myreal",
"1.23"},
315 {
"{\"myarray\": [[1]], \"state\": \"USA\"}",
"",
"myarray.0.0",
"1"},
316 {
"{\"myarray\": [null], \"state\": \"USA\"}",
"",
"myarray.0",
""},
317 {
"{\"myarray\": [0, 1], \"state\": \"USA\"}",
"",
"myarray",
"[0,1]"},
318 {
"[0, 1]",
"",
"",
""},
319 {
"[0, 1]",
"",
"0",
"0"},
320 {
"[0, 1]",
"",
"foo",
""},
321 {
"{\"mynull\": null, \"state\": \"USA\"}",
"",
"mynull",
""},
322 {
"{\"city\": \"Anytown\", \"state\": \"USA\"}",
"",
"city",
"Anytown"},
323 {
"{\"city\": \"Anytown\", \"state\": \"USA\"}",
"",
"state",
"USA"},
324 {
"{\"city\": \"Anytown\", \"state\": \"USA\"}",
"",
"blah",
""},
325 {
"{\"key1\": \"123\", \"key2\": \"456\"}",
"",
"key1",
"123"},
326 {
"{\"key1\": 123, \"key2\": 456}",
"",
"key1",
"123"},
327 {
"{ \"path\": { \"to\": { \"elem\": \"someVar\" } } }",
"/",
"path/to/elem",
"someVar"},
328 {
"{ \"path\": { \"to\": { \"elem\": \"someVar\" } } }",
"",
"path.to.elem2",
""},
329 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
"/",
"path/to/arr/2",
""},
330 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
"/",
"path/to/arr/-1",
""},
331 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
"/",
"path/to/arr/test",
""},
332 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
"",
"path.to.arr.test.test2.subkey",
""},
333 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
",c",
"path.to.arr",
"2"},
334 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
"",
"path.to.arr",
"[\"item0\",\"item1\"]"},
335 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
".",
"path.to.arr.1",
"item1"},
336 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
"/",
"path/to/arr",
"[\"item0\",\"item1\"]"},
337 {
"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }",
"/",
"path/to/arr/1",
"item1"},
338 {
"{ \"path\": { \"to\": { \"arr\": [ {\"name\": \"John Smith\", \"phone\": \"123\"}, {\"name\": \"Jane Doe\", \"phone\": \"234\"} ] } } }",
",c",
"path.to.arr.0.name",
"John Smith"},
339 {
"{ \"path\": { \"to\": { \"arr\": [ {\"name\": 1, \"phone\": 123}, {\"name\": 2, \"phone\": 234} ] } } }",
",c",
"path.to.arr.0.name",
"1"},
340 {
"{ \"path\": { \"to\": { \"arr\": [ {\"name\": [ \"item11\", \"item12\" ], \"phone\": [ \"item13\", \"item14\" ]}, {\"name\": [ \"item15\", \"item16\" ], \"phone\": [ \"item17\", \"item18\" ]} ] } } }",
",c",
"path.to.arr.0.name.1",
"item12"},
341 {
"{ \"startId\": \"foobar\", \"abcd\": { \"id\": \"abcd\", \"type\": \"EXT\" }, \"bcde\": { \"id\": \"bcde\", \"type\": \"CONDITION\" }, \"defg\": { \"id\": \"defg\", \"type\": \"EXT\" }, \"efgh\": { \"id\": \"efgh\", \"type\": \"VOICEMAIL\" } }",
"",
"bcde",
"{\"id\":\"bcde\",\"type\":\"CONDITION\"}"},
346 info->name =
"func_JSON_DECODE";
347 info->category =
"/funcs/func_json/";
348 info->summary =
"Test JSON_DECODE function";
349 info->description =
"Verify JSON_DECODE behavior";
350 return AST_TEST_NOT_RUN;
356 ast_test_status_update(
test,
"Unable to allocate dummy channel\n");
357 return AST_TEST_FAIL;
361 ast_test_status_update(
test,
"Unable to allocate dynamic string buffer\n");
363 return AST_TEST_FAIL;
366 for (i = 0; i < ARRAY_LEN(test_strings); i++) {
369 struct ast_var_t *var = ast_var_assign(
"test_string", test_strings[i][0]);
371 ast_test_status_update(
test,
"Unable to allocate variable\n");
374 return AST_TEST_FAIL;
379 snprintf(tmp,
sizeof(tmp),
"${JSON_DECODE(%s,%s,%s)}",
"test_string", test_strings[i][2], test_strings[i][1]);
383 ast_test_status_update(
test,
"Format string '%s' substituted to '%s' (key: %s). Expected '%s'.\n", test_strings[i][0],
ast_str_buffer(str), test_strings[i][2], test_strings[i][3]);
395 static int unload_module(
void)
399 #ifdef TEST_FRAMEWORK
400 AST_TEST_UNREGISTER(test_JSON_DECODE);
407 static int load_module(
void)
411 #ifdef TEST_FRAMEWORK
412 AST_TEST_REGISTER(test_JSON_DECODE);
419 AST_MODULE_INFO_STANDARD_EXTENDED(
ASTERISK_GPL_KEY,
"JSON decoding function");
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_channel * ast_channel_release(struct ast_channel *chan)
Unlink and release reference to a channel.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
int ast_json_is_true(const struct ast_json *value)
Check if value is JSON true.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
struct ast_json * ast_json_load_str(const struct ast_str *input, struct ast_json_error *error)
Parse ast_str into a JSON object or array.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Data structure associated with a custom dialplan function.
Conversion utility functions.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
#define ast_debug(level,...)
Log a DEBUG message.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Core PBX routines and definitions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
int ast_str_to_int(const char *str, int *res)
Convert the given string to a signed integer.
Support for dynamic strings.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
double ast_json_real_get(const struct ast_json *real)
Get the value from a JSON real number.
int ast_json_is_object(const struct ast_json *value)
Check if value is JSON object.
enum ast_json_type ast_json_typeof(const struct ast_json *value)
Get the type of value.
Structure used to handle boolean flags.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
size_t ast_json_array_size(const struct ast_json *array)
Get the size of a JSON array.
#define AST_TEST_DEFINE(hdr)
Abstract JSON element (object, array, string, int, ...).
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
struct ast_json * ast_json_array_get(const struct ast_json *array, size_t index)
Get an element from an array.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
#define AST_APP_ARG(name)
Define an application argument.