diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/go/pkg/zbxlib/globals_windows.go | 2 | ||||
-rw-r--r-- | src/libs/zbxjson/Makefile.am | 4 | ||||
-rw-r--r-- | src/libs/zbxjson/json.c | 14 | ||||
-rw-r--r-- | src/libs/zbxjson/json.h | 2 | ||||
-rw-r--r-- | src/libs/zbxjson/json_parser.c | 127 | ||||
-rw-r--r-- | src/libs/zbxjson/json_parser.h | 8 | ||||
-rw-r--r-- | src/libs/zbxjson/jsonobj.c | 366 | ||||
-rw-r--r-- | src/libs/zbxjson/jsonobj.h | 56 | ||||
-rw-r--r-- | src/libs/zbxjson/jsonpath.c | 1219 | ||||
-rw-r--r-- | src/libs/zbxjson/jsonpath.h | 17 | ||||
-rw-r--r-- | src/zabbix_agent/Makefile.am | 2 | ||||
-rw-r--r-- | src/zabbix_server/Makefile.am | 4 | ||||
-rw-r--r-- | src/zabbix_server/preprocessor/item_preproc.c | 73 | ||||
-rw-r--r-- | src/zabbix_server/preprocessor/preproc_cache.c | 5 |
14 files changed, 1419 insertions, 480 deletions
diff --git a/src/go/pkg/zbxlib/globals_windows.go b/src/go/pkg/zbxlib/globals_windows.go index 17cbe67a30b..c3138979244 100644 --- a/src/go/pkg/zbxlib/globals_windows.go +++ b/src/go/pkg/zbxlib/globals_windows.go @@ -39,6 +39,7 @@ package zbxlib #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/md5.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/vector.o +#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/hashset.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/zbxregexp.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/algodefs.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/persistent_state.o @@ -46,6 +47,7 @@ package zbxlib #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json_parser.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonpath.o +#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonobj.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sha256crypt.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/variant.o #cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo_system.o diff --git a/src/libs/zbxjson/Makefile.am b/src/libs/zbxjson/Makefile.am index ad27f80c98e..f7981730a30 100644 --- a/src/libs/zbxjson/Makefile.am +++ b/src/libs/zbxjson/Makefile.am @@ -8,4 +8,6 @@ libzbxjson_a_SOURCES = \ json_parser.c \ json_parser.h \ jsonpath.c \ - jsonpath.h + jsonpath.h \ + jsonobj.c \ + jsonobj.h diff --git a/src/libs/zbxjson/json.c b/src/libs/zbxjson/json.c index 4960eca4466..412dcc098cf 100644 --- a/src/libs/zbxjson/json.c +++ b/src/libs/zbxjson/json.c @@ -840,7 +840,7 @@ static unsigned int zbx_json_decode_character(const char **p, unsigned char *byt * string copying failed. * * * ******************************************************************************/ -static const char *zbx_json_copy_string(const char *p, char *out, size_t size) +const char *json_copy_string(const char *p, char *out, size_t size) { char *start = out; @@ -919,7 +919,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j /* only primitive values are decoded */ return NULL; default: - if (0 == (len = json_parse_value(p, NULL))) + if (0 == (len = json_parse_value(p, NULL, NULL))) return NULL; } @@ -929,7 +929,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j switch (type_local) { case ZBX_JSON_TYPE_STRING: - return zbx_json_copy_string(p, string, size); + return json_copy_string(p, string, size); case ZBX_JSON_TYPE_NULL: if (0 == size) return NULL; @@ -953,7 +953,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin /* only primitive values are decoded */ return NULL; default: - if (0 == (len = json_parse_value(p, NULL))) + if (0 == (len = json_parse_value(p, NULL, NULL))) return NULL; } @@ -969,7 +969,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin switch (type_local) { case ZBX_JSON_TYPE_STRING: - return zbx_json_copy_string(p, *string, *string_alloc); + return json_copy_string(p, *string, *string_alloc); case ZBX_JSON_TYPE_NULL: **string = '\0'; return p + len; @@ -986,7 +986,7 @@ const char *zbx_json_pair_next(const struct zbx_json_parse *jp, const char *p, c if (ZBX_JSON_TYPE_STRING != __zbx_json_type(p)) return NULL; - if (NULL == (p = zbx_json_copy_string(p, name, len))) + if (NULL == (p = json_copy_string(p, name, len))) return NULL; SKIP_WHITESPACE(p); @@ -1214,7 +1214,7 @@ int json_open_path(const struct zbx_json_parse *jp, const zbx_jsonpath_t *jsonpa object.start = p; if (NULL == (object.end = __zbx_json_rbracket(p))) - object.end = p + json_parse_value(p, NULL) - 1; + object.end = p + json_parse_value(p, NULL, NULL) - 1; } *out = object; diff --git a/src/libs/zbxjson/json.h b/src/libs/zbxjson/json.h index a9b7a4f5f29..2a53f0ec687 100644 --- a/src/libs/zbxjson/json.h +++ b/src/libs/zbxjson/json.h @@ -35,4 +35,6 @@ void zbx_set_json_strerror(const char *fmt, ...) __zbx_attr_format_printf(1, 2); int json_open_path(const struct zbx_json_parse *jp, const zbx_jsonpath_t *jsonpath, struct zbx_json_parse *out); +const char *json_copy_string(const char *p, char *out, size_t size); + #endif diff --git a/src/libs/zbxjson/json_parser.c b/src/libs/zbxjson/json_parser.c index 20ce1098437..7f64f4164f2 100644 --- a/src/libs/zbxjson/json_parser.c +++ b/src/libs/zbxjson/json_parser.c @@ -21,27 +21,31 @@ #include "common.h" #include "json.h" - -static zbx_int64_t json_parse_object(const char *start, char **error); +#include "jsonobj.h" /****************************************************************************** * * * Purpose: Prepares JSON parsing error message * * * - * Parameters: message - [IN] the error message * - * json_buffer - [IN] the failing data fragment * - * error - [OUT] the parsing error message (can be NULL) * + * Parameters: message - [IN] the error message * + * ptr - [IN] the failing data fragment * + * error - [OUT] the parsing error message (can be NULL) * * * * Return value: 0 - the json_error() function always returns 0 value * * so it can be used to return from failed parses * * * ******************************************************************************/ -static zbx_int64_t json_error(const char *message, const char *json_buffer, char **error) +zbx_int64_t json_error(const char *message, const char *ptr, char **error) { if (NULL != error) { - if (NULL != json_buffer) - *error = zbx_dsprintf(*error, "%s at: '%s'", message, json_buffer); + if (NULL != ptr) + { + if (128 < strlen(ptr)) + *error = zbx_dsprintf(*error, "%s at: '%128s...'", message, ptr); + else + *error = zbx_dsprintf(*error, "%s at: '%s'", message, ptr); + } else *error = zbx_strdup(*error, message); } @@ -54,6 +58,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char * Purpose: Parses JSON string value or object name * * * * Parameters: start - [IN] the JSON data without leading whitespace * + * str - [OUT] the parsed unquoted string (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -61,7 +66,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_string(const char *start, char **error) +static zbx_int64_t json_parse_string(const char *start, char **str, char **error) { const char *ptr = start; @@ -119,6 +124,12 @@ static zbx_int64_t json_parse_string(const char *start, char **error) ptr++; } + if (NULL != str) + { + *str = (char *)zbx_malloc(NULL, (size_t)(ptr - start)); + json_copy_string(start, *str, (size_t)(ptr - start)); + } + return ptr - start + 1; } @@ -127,6 +138,7 @@ static zbx_int64_t json_parse_string(const char *start, char **error) * Purpose: Parses JSON array value * * * * Parameters: start - [IN] the JSON data without leading whitespace * + * obj - [IN/OUT] the JSON object (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -134,11 +146,14 @@ static zbx_int64_t json_parse_string(const char *start, char **error) * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_array(const char *start, char **error) +zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error) { const char *ptr = start; zbx_int64_t len; + if (NULL != obj) + jsonobj_init(obj, ZBX_JSON_TYPE_ARRAY); + ptr++; SKIP_WHITESPACE(ptr); @@ -146,9 +161,29 @@ static zbx_int64_t json_parse_array(const char *start, char **error) { while (1) { + zbx_jsonobj_t *value; + + if (NULL != obj) + { + value = zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + jsonobj_init(value, ZBX_JSON_TYPE_UNKNOWN); + } + else + value = NULL; + /* json_parse_value strips leading whitespace, so we don't have to do it here */ - if (0 == (len = json_parse_value(ptr, error))) + if (0 == (len = json_parse_value(ptr, value, error))) + { + if (NULL != obj) + { + zbx_jsonobj_clear(value); + zbx_free(value); + } return 0; + } + + if (NULL != obj) + zbx_vector_jsonobj_ptr_append(&obj->data.array, value); ptr += len; SKIP_WHITESPACE(ptr); @@ -171,15 +206,16 @@ static zbx_int64_t json_parse_array(const char *start, char **error) * * * Purpose: Parses JSON number value * * * - * Parameters: start - [IN] the JSON data without leading whitespace * - * error - [OUT] the parsing error message (can be NULL) * + * Parameters: start - [IN] the JSON data without leading whitespace * + * number - [OUT] the parsed number (can be NULL) * + * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * * error parameter (if not NULL) contains allocated error * * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_number(const char *start, char **error) +static zbx_int64_t json_parse_number(const char *start, double *number, char **error) { const char *ptr = start; char first_digit; @@ -235,6 +271,9 @@ static zbx_int64_t json_parse_number(const char *start, char **error) } } + if (NULL != number) + *number = atof(start); + return ptr - start; } @@ -281,10 +320,12 @@ static zbx_int64_t json_parse_literal(const char *start, const char *text, char * message. * * * ******************************************************************************/ -zbx_int64_t json_parse_value(const char *start, char **error) +zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error) { const char *ptr = start; zbx_int64_t len; + char *str = NULL; + double number; SKIP_WHITESPACE(ptr); @@ -293,28 +334,40 @@ zbx_int64_t json_parse_value(const char *start, char **error) case '\0': return json_error("unexpected end of object value", NULL, error); case '"': - if (0 == (len = json_parse_string(ptr, error))) + if (0 == (len = json_parse_string(ptr, (NULL != obj ? &str : NULL), error))) return 0; + + if (NULL != obj) + jsonobj_set_string(obj, str); break; case '{': - if (0 == (len = json_parse_object(ptr, error))) + if (0 == (len = json_parse_object(ptr, obj, error))) return 0; break; case '[': - if (0 == (len = json_parse_array(ptr, error))) + if (0 == (len = json_parse_array(ptr, obj, error))) return 0; break; case 't': if (0 == (len = json_parse_literal(ptr, "true", error))) return 0; + + if (NULL != obj) + jsonobj_set_true(obj); break; case 'f': if (0 == (len = json_parse_literal(ptr, "false", error))) return 0; + + if (NULL != obj) + jsonobj_set_false(obj); break; case 'n': if (0 == (len = json_parse_literal(ptr, "null", error))) return 0; + + if (NULL != obj) + jsonobj_set_null(obj); break; case '0': case '1': @@ -327,8 +380,12 @@ zbx_int64_t json_parse_value(const char *start, char **error) case '8': case '9': case '-': - if (0 == (len = json_parse_number(ptr, error))) + if (0 == (len = json_parse_number(ptr, (NULL != obj ? &number : NULL), error))) return 0; + + if (NULL != obj) + jsonobj_set_number(obj, number); + break; default: return json_error("invalid JSON object value starting character", ptr, error); @@ -342,6 +399,7 @@ zbx_int64_t json_parse_value(const char *start, char **error) * Purpose: Parses JSON object * * * * Parameters: start - [IN] the JSON data * + * obj - [IN/OUT] the JSON object (can be NULL) * * error - [OUT] the parsing error message (can be NULL) * * * * Return value: The number of characters parsed. On error 0 is returned and * @@ -349,10 +407,13 @@ zbx_int64_t json_parse_value(const char *start, char **error) * message. * * * ******************************************************************************/ -static zbx_int64_t json_parse_object(const char *start, char **error) +zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error) { - const char *ptr = start; - zbx_int64_t len; + const char *ptr = start; + zbx_int64_t len; + + if (NULL != obj) + jsonobj_init(obj, ZBX_JSON_TYPE_OBJECT); /* parse object name */ SKIP_WHITESPACE(ptr); @@ -364,11 +425,15 @@ static zbx_int64_t json_parse_object(const char *start, char **error) { while (1) { + zbx_jsonobj_el_t el; + if ('"' != *ptr) return json_error("invalid object name", ptr, error); + jsonobj_el_init(&el); + /* cannot parse object name, failing */ - if (0 == (len = json_parse_string(ptr, error))) + if (0 == (len = json_parse_string(ptr, (NULL != obj ? &el.name : NULL), error))) return 0; ptr += len; @@ -377,11 +442,21 @@ static zbx_int64_t json_parse_object(const char *start, char **error) SKIP_WHITESPACE(ptr); if (':' != *ptr) + { + jsonobj_el_clear(&el); return json_error("invalid object name/value separator", ptr, error); + } + ptr++; - if (0 == (len = json_parse_value(ptr, error))) + if (0 == (len = json_parse_value(ptr, (NULL != obj ? &el.value : NULL), error))) + { + jsonobj_el_clear(&el); return 0; + } + + if (NULL != obj) + zbx_hashset_insert(&obj->data.object, &el, sizeof(el)); ptr += len; @@ -426,11 +501,11 @@ zbx_int64_t zbx_json_validate(const char *start, char **error) switch (*start) { case '{': - if (0 == (len = json_parse_object(start, error))) + if (0 == (len = json_parse_object(start, NULL, error))) return 0; break; case '[': - if (0 == (len = json_parse_array(start, error))) + if (0 == (len = json_parse_array(start, NULL, error))) return 0; break; default: diff --git a/src/libs/zbxjson/json_parser.h b/src/libs/zbxjson/json_parser.h index f9fb3bb8cf5..cd54939eb7d 100644 --- a/src/libs/zbxjson/json_parser.h +++ b/src/libs/zbxjson/json_parser.h @@ -21,9 +21,15 @@ #define ZABBIX_JSON_PARSER_H #include "zbxtypes.h" +#include "jsonobj.h" zbx_int64_t zbx_json_validate(const char *start, char **error); -zbx_int64_t json_parse_value(const char *start, char **error); +zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error); + +zbx_int64_t json_error(const char *message, const char *ptr, char **error); + +zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error); +zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error); #endif diff --git a/src/libs/zbxjson/jsonobj.c b/src/libs/zbxjson/jsonobj.c new file mode 100644 index 00000000000..c343e4e896a --- /dev/null +++ b/src/libs/zbxjson/jsonobj.c @@ -0,0 +1,366 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "jsonobj.h" + +#include "json_parser.h" +#include "json.h" +#include "../zbxalgo/vectorimpl.h" + +ZBX_PTR_VECTOR_IMPL(jsonobj_ptr, zbx_jsonobj_t *) +ZBX_VECTOR_IMPL(jsonobj_ref, zbx_jsonobj_ref_t) + +/* jsonobject index hashset support */ + +static zbx_hash_t jsonobj_index_el_hash(const void *v) +{ + const zbx_jsonobj_index_el_t *el = (const zbx_jsonobj_index_el_t *)v; + + return ZBX_DEFAULT_STRING_HASH_FUNC(el->value); +} + +static int jsonobj_index_el_compare(const void *v1, const void *v2) +{ + const zbx_jsonobj_index_el_t *el1 = (const zbx_jsonobj_index_el_t *)v1; + const zbx_jsonobj_index_el_t *el2 = (const zbx_jsonobj_index_el_t *)v2; + + return strcmp(el1->value, el2->value); +} + +/* jsonobject values hashset support */ + +static zbx_hash_t jsonobj_el_hash(const void *v) +{ + const zbx_jsonobj_el_t *el = (const zbx_jsonobj_el_t *)v; + + return ZBX_DEFAULT_STRING_HASH_FUNC(el->name); +} + +static int jsonobj_el_compare(const void *v1, const void *v2) +{ + const zbx_jsonobj_el_t *el1 = (const zbx_jsonobj_el_t *)v1; + const zbx_jsonobj_el_t *el2 = (const zbx_jsonobj_el_t *)v2; + + return strcmp(el1->name, el2->name); +} + +/****************************************************************************** + * * + * Purpose: initialize json object structure * + * * + * Parameters: obj - [IN/OUT] the json object to initialize * + * type - [IN] the json object type * + * * + ******************************************************************************/ +void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type) +{ + obj->type = type; + + switch (type) + { + case ZBX_JSON_TYPE_ARRAY: + zbx_vector_jsonobj_ptr_create(&obj->data.array); + break; + case ZBX_JSON_TYPE_OBJECT: + zbx_hashset_create(&obj->data.object, 0, jsonobj_el_hash, jsonobj_el_compare); + break; + default: + memset(&obj->data, 0, sizeof(obj->data)); + break; + } + + obj->index = NULL; +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object index element * + * * + * Parameters: v - [IN] the json index element * + * * + ******************************************************************************/ +static void jsonobj_index_el_clear(void *v) +{ + zbx_jsonobj_index_el_t *el = (zbx_jsonobj_index_el_t *)v; + int i; + + zbx_free(el->value); + for (i = 0; i < el->objects.values_num; i++) + { + zbx_free(el->objects.values[i].name); + + if (0 != el->objects.values[i].external) + { + zbx_jsonobj_clear(el->objects.values[i].value); + zbx_free(el->objects.values[i].value); + } + } + + zbx_vector_jsonobj_ref_destroy(&el->objects); +} + +/****************************************************************************** + * * + * Purpose: initialize json object index * + * * + * Parameters: obj - [IN/OUT] the json object * + * path - [IN] the indexed relative path * + * * + ******************************************************************************/ +void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path) +{ + obj->index = (zbx_jsonobj_index_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_index_t)); + obj->index->path = zbx_strdup(NULL, path); + zbx_hashset_create_ext(&obj->index->objects, 0, jsonobj_index_el_hash, jsonobj_index_el_compare, + jsonobj_index_el_clear, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, + ZBX_DEFAULT_MEM_FREE_FUNC); +} + +/****************************************************************************** + * * + * Purpose: set string value to json object * + * * + ******************************************************************************/ +void jsonobj_set_string(zbx_jsonobj_t *obj, char *str) +{ + obj->type = ZBX_JSON_TYPE_STRING; + obj->data.string = str; +} + +/****************************************************************************** + * * + * Purpose: set numeric value to json object * + * * + ******************************************************************************/ +void jsonobj_set_number(zbx_jsonobj_t *obj, double number) +{ + obj->type = ZBX_JSON_TYPE_NUMBER; + obj->data.number = number; +} + +/****************************************************************************** + * * + * Purpose: set true value to json object * + * * + ******************************************************************************/ +void jsonobj_set_true(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_TRUE; +} + +/****************************************************************************** + * * + * Purpose: set false value to json object * + * * + ******************************************************************************/ +void jsonobj_set_false(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_FALSE; +} + +/****************************************************************************** + * * + * Purpose: set null value to json object * + * * + ******************************************************************************/ +void jsonobj_set_null(zbx_jsonobj_t *obj) +{ + obj->type = ZBX_JSON_TYPE_NULL; +} + +/****************************************************************************** + * * + * Purpose: initialize json object element * + * * + ******************************************************************************/ +void jsonobj_el_init(zbx_jsonobj_el_t *el) +{ + el->name = NULL; + jsonobj_init(&el->value, ZBX_JSON_TYPE_UNKNOWN); +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object element * + * * + ******************************************************************************/ +void jsonobj_el_clear(zbx_jsonobj_el_t *el) +{ + zbx_free(el->name); + zbx_jsonobj_clear(&el->value); +} + +/****************************************************************************** + * * + * Purpose: free json object * + * * + ******************************************************************************/ +static void jsonobj_free(zbx_jsonobj_t *obj) +{ + zbx_jsonobj_clear(obj); + zbx_free(obj); +} + +/****************************************************************************** + * * + * Purpose: free resources allocated by json object * + * * + ******************************************************************************/ +void zbx_jsonobj_clear(zbx_jsonobj_t *obj) +{ + zbx_jsonobj_el_t *el; + zbx_hashset_iter_t iter; + + switch (obj->type) + { + case ZBX_JSON_TYPE_STRING: + zbx_free(obj->data.string); + break; + case ZBX_JSON_TYPE_ARRAY: + zbx_vector_jsonobj_ptr_clear_ext(&obj->data.array, jsonobj_free); + zbx_vector_jsonobj_ptr_destroy(&obj->data.array); + break; + case ZBX_JSON_TYPE_OBJECT: + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + zbx_free(el->name); + zbx_jsonobj_clear(&el->value); + } + zbx_hashset_destroy(&obj->data.object); + break; + default: + break; + } + + if (NULL != obj->index) + { + zbx_free(obj->index->path); + zbx_hashset_destroy(&obj->index->objects); + zbx_free(obj->index); + } +} + +/****************************************************************************** + * * + * Purpose: convert json object to text format * + * * + ******************************************************************************/ +int zbx_jsonobj_to_string(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj) +{ + char *tmp, buf[32], delim; + int i; + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; + + switch (obj->type) + { + case ZBX_JSON_TYPE_TRUE: + zbx_strcpy_alloc(str, str_alloc, str_offset, "true"); + break; + case ZBX_JSON_TYPE_FALSE: + zbx_strcpy_alloc(str, str_alloc, str_offset, "false"); + break; + case ZBX_JSON_TYPE_NULL: + zbx_strcpy_alloc(str, str_alloc, str_offset, "null"); + break; + case ZBX_JSON_TYPE_STRING: + tmp = zbx_strdup(NULL, obj->data.string); + zbx_json_escape(&tmp); + zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp); + zbx_free(tmp); + break; + case ZBX_JSON_TYPE_NUMBER: + zbx_print_double(buf, sizeof(buf), obj->data.number); + zbx_strcpy_alloc(str, str_alloc, str_offset, buf); + break; + case ZBX_JSON_TYPE_ARRAY: + delim = '['; + for (i = 0; i < obj->data.array.values_num; i++) + { + zbx_chrcpy_alloc(str, str_alloc, str_offset, delim); + delim = ','; + zbx_jsonobj_to_string(str, str_alloc, str_offset, obj->data.array.values[i]); + } + zbx_chrcpy_alloc(str, str_alloc, str_offset, ']'); + break; + case ZBX_JSON_TYPE_OBJECT: + delim = '{'; + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + zbx_chrcpy_alloc(str, str_alloc, str_offset, delim); + delim = ','; + + tmp = zbx_strdup(NULL, el->name); + zbx_json_escape(&tmp); + zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp); + zbx_chrcpy_alloc(str, str_alloc, str_offset, ':'); + zbx_free(tmp); + + zbx_jsonobj_to_string(str, str_alloc, str_offset, &el->value); + } + zbx_chrcpy_alloc(str, str_alloc, str_offset, '}'); + break; + default: + zbx_set_json_strerror("unknown json object with type: %u", obj->type); + return FAIL; + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Purpose: parses json formatted data into json object structure * + * * + ******************************************************************************/ +int zbx_jsonobj_open(const char *data, zbx_jsonobj_t *obj) +{ + int ret = FAIL; + char *error = NULL; + + switch (*data) + { + case '{': + if (0 == json_parse_object(data, obj, &error)) + goto out; + break; + case '[': + if (0 == json_parse_array(data, obj, &error)) + goto out; + break; + default: + /* not json data, failing */ + jsonobj_init(obj, ZBX_JSON_TYPE_UNKNOWN); + (void)json_error("invalid object format, expected opening character '{' or '['", data, &error); + goto out; + } + + ret = SUCCEED; +out: + if (FAIL == ret) + { + zbx_jsonobj_clear(obj); + zbx_set_json_strerror("%s", error); + zbx_free(error); + } + + return ret; +} diff --git a/src/libs/zbxjson/jsonobj.h b/src/libs/zbxjson/jsonobj.h new file mode 100644 index 00000000000..dfd12df6b9b --- /dev/null +++ b/src/libs/zbxjson/jsonobj.h @@ -0,0 +1,56 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#ifndef ZABBIX_JSONOBJ_H +#define ZABBIX_JSONOBJ_H + +#include "zbxjson.h" + +typedef struct +{ + char *name; + zbx_jsonobj_t *value; + unsigned char external; /* 1 - the reference is to an external object. */ + /* 0 - the reference is to a local object which must be freed */ + /* when reference is destroyed */ +} +zbx_jsonobj_ref_t; + +ZBX_VECTOR_DECL(jsonobj_ref, zbx_jsonobj_ref_t) + +typedef struct +{ + char *value; /* the value found at indexed path */ + zbx_vector_jsonobj_ref_t objects; /* the objects matching value at indexed path */ +} +zbx_jsonobj_index_el_t; + +void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type); + +void jsonobj_el_init(zbx_jsonobj_el_t *el); +void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path); +void jsonobj_el_clear(zbx_jsonobj_el_t *el); + +void jsonobj_set_string(zbx_jsonobj_t *obj, char *str); +void jsonobj_set_number(zbx_jsonobj_t *obj, double number); +void jsonobj_set_true(zbx_jsonobj_t *obj); +void jsonobj_set_false(zbx_jsonobj_t *obj); +void jsonobj_set_null(zbx_jsonobj_t *obj); + +#endif diff --git a/src/libs/zbxjson/jsonpath.c b/src/libs/zbxjson/jsonpath.c index 7944e2b88e0..0cce389e9b2 100644 --- a/src/libs/zbxjson/jsonpath.c +++ b/src/libs/zbxjson/jsonpath.c @@ -19,28 +19,22 @@ #include "jsonpath.h" -#include "common.h" #include "zbxregexp.h" -#include "zbxjson.h" +#include "zbxvariant.h" #include "json.h" #include "json_parser.h" -#include "zbxvariant.h" +#include "jsonobj.h" #include "../zbxalgo/vectorimpl.h" typedef struct { - char *name; - const char *value; + zbx_jsonobj_t *root; /* the root object */ + zbx_jsonpath_t *path; + unsigned char found; /* set to 1 when one object was matched and */ + /* no more matches are required */ + zbx_vector_jsonobj_ref_t objects; /* the matched objects */ } -zbx_json_element_t; - -ZBX_VECTOR_DECL(json, zbx_json_element_t) -ZBX_VECTOR_IMPL(json, zbx_json_element_t) - -static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects); -static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects); +zbx_jsonpath_context_t; typedef struct { @@ -49,6 +43,10 @@ typedef struct } zbx_jsonpath_token_def_t; +static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth); +static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth); +static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj); + /* define token groups and precedence */ static zbx_jsonpath_token_def_t jsonpath_tokens[] = { {0, 0}, @@ -74,6 +72,7 @@ static zbx_jsonpath_token_def_t jsonpath_tokens[] = { {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7} /* ZBX_JSONPATH_TOKEN_OP_REGEXP */ }; + static int jsonpath_token_precedence(int type) { return jsonpath_tokens[type].precedence; @@ -84,31 +83,118 @@ static int jsonpath_token_group(int type) return jsonpath_tokens[type].group; } -/* json element vector support */ -static void zbx_vector_json_add_element(zbx_vector_json_t *elements, const char *name, const char *value) +/****************************************************************************** + * * + * Purpose: add external json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * name - [IN] the json object name or array index * + * value - [IN] the json object * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add_object(zbx_vector_jsonobj_ref_t *refs, const char *name, + zbx_jsonobj_t *value) +{ + zbx_jsonobj_ref_t ref; + + ref.name = zbx_strdup(NULL, name); + ref.external = 1; + + ref.value = value; + zbx_vector_jsonobj_ref_append(refs, ref); +} + +/****************************************************************************** + * * + * Purpose: add internal json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * name - [IN] the json object name or array index * + * str - [IN] the string value of the object * + * * + * Comments: This function will create json object and add internal reference,* + * meaning the object will be destroyed together with its reference.* + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add_string(zbx_vector_jsonobj_ref_t *refs, const char *name, + const char *str) { - zbx_json_element_t el; + zbx_jsonobj_ref_t ref; + + ref.name = zbx_strdup(NULL, name); + ref.external = 0; + + ref.value = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + jsonobj_init(ref.value, ZBX_JSON_TYPE_STRING); + jsonobj_set_string(ref.value, zbx_strdup(NULL, str)); - el.name = zbx_strdup(NULL, name); - el.value = value; - zbx_vector_json_append(elements, el); + zbx_vector_jsonobj_ref_append(refs, ref); } -static void zbx_vector_json_copy(zbx_vector_json_t *dst, const zbx_vector_json_t *src) +/****************************************************************************** + * * + * Purpose: add a copy of json object reference to a vector * + * * + * Parameters: refs - [IN/OUT] the json object reference vector * + * ref - [IN] the json object reference * + * * + * Comments: For internal references a new internal json object will be * + * created. * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_add(zbx_vector_jsonobj_ref_t *refs, zbx_jsonobj_ref_t *ref) +{ + if (0 != ref->external) + zbx_vector_jsonobj_ref_add_object(refs, ref->name, ref->value); + else + zbx_vector_jsonobj_ref_add_string(refs, ref->name, ref->value->data.string); +} + +/****************************************************************************** + * * + * Purpose: copy json object references from one vector to other * + * * + * Parameters: dst - [IN/OUT] the destination json object reference vector * + * src - [IN] the source json object reference vector * + * * + * Comments: For internal references a new internal json object will be * + * created. * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_copy(zbx_vector_jsonobj_ref_t *dst, const zbx_vector_jsonobj_ref_t *src) { int i; for (i = 0; i < src->values_num; i++) - zbx_vector_json_add_element(dst, src->values[i].name, src->values[i].value); + { + if (0 != src->values[i].external) + zbx_vector_jsonobj_ref_add_object(dst, src->values[i].name, src->values[i].value); + else + zbx_vector_jsonobj_ref_add_string(dst, src->values[i].name, src->values[i].value->data.string); + } } -static void zbx_vector_json_clear_ext(zbx_vector_json_t *elements) +/****************************************************************************** + * * + * Purpose: free resources allocated by json object reference * + * * + ******************************************************************************/ +static void zbx_vector_jsonobj_ref_clear_ext(zbx_vector_jsonobj_ref_t *refs) { int i; - for (i = 0; i < elements->values_num; i++) - zbx_free(elements->values[i].name); - zbx_vector_json_clear(elements); + for (i = 0; i < refs->values_num; i++) + { + zbx_jsonobj_ref_t *ref = &refs->values[i]; + + zbx_free(ref->name); + if (0 == ref->external) + { + zbx_jsonobj_clear(ref->value); + zbx_free(ref->value); + } + } + zbx_vector_jsonobj_ref_clear(refs); } /****************************************************************************** @@ -215,7 +301,62 @@ static void jsonpath_list_free(zbx_jsonpath_list_node_t *list) /****************************************************************************** * * - * Purpose: create jsonpath and compile json path * + * Purpose: append array index to list * + * * + ******************************************************************************/ +static zbx_jsonpath_list_node_t *jsonpath_list_append_index(zbx_jsonpath_list_node_t *head, int index, + int check_duplicate) +{ + zbx_jsonpath_list_node_t *node; + + if (0 != check_duplicate) + { + for (node = head; NULL != node; node = node->next) + { + int query_index; + + memcpy(&query_index, node->data, sizeof(query_index)); + if (query_index == index) + return head; + } + } + + node = jsonpath_list_create_node(sizeof(int)); + node->next = head; + memcpy(node->data, &index, sizeof(int)); + + return node; +} + +/****************************************************************************** + * * + * Purpose: append name to list * + * * + ******************************************************************************/ +static zbx_jsonpath_list_node_t *jsonpath_list_append_name(zbx_jsonpath_list_node_t *head, const char *name, size_t len) +{ + zbx_jsonpath_list_node_t *node, *new_node; + + new_node = jsonpath_list_create_node(len + 1); + jsonpath_unquote(new_node->data, name, len + 1); + + for (node = head; NULL != node; node = node->next) + { + if (0 == strcmp((char *)new_node->data, (char *)node->data)) + { + zbx_free(new_node); + return head; + } + } + + new_node->next = head; + + return new_node; +} + +/****************************************************************************** + * * + * Purpose: create jsonpath structure and compile json path * * * ******************************************************************************/ static zbx_jsonpath_t *jsonpath_create_token_jsonpath(const char *text, size_t len) @@ -258,18 +399,18 @@ out: /****************************************************************************** * * - * Function: jsonpath_create_token * - * * * Purpose: create jsonpath expression token * * * * Parameters: type - [IN] the token type * * expression - [IN] the expression * * loc - [IN] the token location in the expression * * * - * Return value: The created token (must be freed by the caller). * + * Return value: The created token (must be freed by the caller) or * + * NULL in the case of error. * * * ******************************************************************************/ -static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *expression, const zbx_strloc_t *loc) +static zbx_jsonpath_token_t *jsonpath_create_token(unsigned char type, const char *expression, + const zbx_strloc_t *loc) { zbx_jsonpath_token_t *token; @@ -304,6 +445,11 @@ static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *express return token; } +/****************************************************************************** + * * + * Purpose: free jsonpath expression token * + * * + ******************************************************************************/ static void jsonpath_token_free(zbx_jsonpath_token_t *token) { zbx_free(token->text); @@ -337,16 +483,21 @@ static void jsonpath_reserve(zbx_jsonpath_t *jsonpath, int num) jsonpath->segments_alloc *= 2; jsonpath->segments = (zbx_jsonpath_segment_t *)zbx_realloc(jsonpath->segments, - sizeof(zbx_jsonpath_segment_t) * jsonpath->segments_alloc); + sizeof(zbx_jsonpath_segment_t) * (size_t)jsonpath->segments_alloc); /* Initialize the memory allocated for new segments, as parser can set */ /* detached flag for the next segment, so the memory cannot be initialized */ /* when creating a segment. */ memset(jsonpath->segments + old_alloc, 0, - (jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t)); + (size_t)(jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t)); } } +/****************************************************************************** + * * + * Purpose: free resource allocated by jsonpath segment * + * * + ******************************************************************************/ static void jsonpath_segment_clear(zbx_jsonpath_segment_t *segment) { switch (segment->type) @@ -454,7 +605,7 @@ static int jsonpath_next(const char **pnext) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_parse_substring(const char *start, int *len) +static int jsonpath_parse_substring(const char *start, size_t *len) { const char *ptr; char quotes; @@ -463,7 +614,7 @@ static int jsonpath_parse_substring(const char *start, int *len) { if (*ptr == quotes) { - *len = ptr - start + 1; + *len = (size_t)(ptr - start + 1); return SUCCEED; } @@ -492,7 +643,7 @@ static int jsonpath_parse_substring(const char *start, int *len) * jsonpath filter expressions. * * * ******************************************************************************/ -static int jsonpath_parse_path(const char *start, int *len) +static int jsonpath_parse_path(const char *start, size_t *len) { const char *ptr = start + 1; @@ -502,7 +653,7 @@ static int jsonpath_parse_path(const char *start, int *len) return FAIL; } - *len = ptr - start; + *len = (size_t)(ptr - start); return SUCCEED; } @@ -517,7 +668,7 @@ static int jsonpath_parse_path(const char *start, int *len) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_parse_number(const char *start, int *len) +static int jsonpath_parse_number(const char *start, size_t *len) { const char *ptr = start; char *end; @@ -537,7 +688,7 @@ static int jsonpath_parse_number(const char *start, int *len) if (ptr != end || HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno) return FAIL; - *len = (int)(ptr - start); + *len = (size_t)(ptr - start); return SUCCEED; } @@ -557,14 +708,14 @@ static int jsonpath_parse_number(const char *start, int *len) * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_expression_next_token(const char *expression, int pos, int prev_group, +static int jsonpath_expression_next_token(const char *expression, size_t pos, int prev_group, zbx_jsonpath_token_type_t *type, zbx_strloc_t *loc) { - int len; + size_t len; const char *ptr = expression + pos; SKIP_WHITESPACE(ptr); - loc->l = ptr - expression; + loc->l = (size_t)(ptr - expression); switch (*ptr) { @@ -696,6 +847,139 @@ out: return zbx_jsonpath_error(ptr); } +/* value types on index stack */ +typedef enum +{ + ZBX_JSONPATH_CONST = 1, /* constant value - string or number */ + ZBX_JSONPATH_VALUE, /* result of an operation after which cannot be used in index */ + ZBX_JSONPATH_PATH, /* relative jsonpath - @.a.b.c */ + ZBX_JSONPATH_PATH_OP /* result of an operation with jsonpath which still can be used in index */ +} +zbx_jsonpath_index_value_type_t; + +typedef struct +{ + zbx_jsonpath_index_value_type_t type; + zbx_jsonpath_token_t *index_token; + zbx_jsonpath_token_t *value_token; +} +zbx_jsonpath_index_value_t; + +ZBX_VECTOR_DECL(jpi_value, zbx_jsonpath_index_value_t) +ZBX_VECTOR_IMPL(jpi_value, zbx_jsonpath_index_value_t) + +/****************************************************************************** + * * + * Purpose: analyze expression and set indexing fields if possible * + * * + * Comments: Expression can be indexed if it contains relative json path * + * comparison with constant that is used in and operations. * + * This is tested by doing a pseudo evaluation by operand types * + * and checking the result type. * + * * + * So expressions like ?(@.a.b == 1), ?(@.a == "A" and @.b == "B") * + * can be indexed (by @.a.b and by @.a) while expressions like * + * ?(@.a == @.b), ?(@.a == "A" or @.b == "B") cannot. * + * * + ******************************************************************************/ +static void jsonpath_expression_prepare_index(zbx_jsonpath_expression_t *exp) +{ + int i; + zbx_vector_jpi_value_t stack; + zbx_jsonpath_index_value_t *left, *right; + + zbx_vector_jpi_value_create(&stack); + + for (i = 0; i < exp->tokens.values_num; i++) + { + zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)exp->tokens.values[i]; + zbx_jsonpath_index_value_t jpi = {0}; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_OP_NOT: + if (1 > stack.values_num) + goto out; + stack.values[stack.values_num - 1].type = ZBX_JSONPATH_VALUE; + stack.values[stack.values_num - 1].index_token = NULL; + stack.values[stack.values_num - 1].value_token = NULL; + continue; + case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + jpi.index_token = token; + jpi.type = ZBX_JSONPATH_PATH; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: + jpi.type = ZBX_JSONPATH_VALUE; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + case ZBX_JSONPATH_TOKEN_CONST_STR: + case ZBX_JSONPATH_TOKEN_CONST_NUM: + jpi.value_token = token; + jpi.type = ZBX_JSONPATH_CONST; + zbx_vector_jpi_value_append(&stack, jpi); + continue; + } + + if (2 > stack.values_num) + goto out; + + left = &stack.values[stack.values_num - 2]; + right = &stack.values[stack.values_num - 1]; + stack.values_num--; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_OP_EQ: + if ((ZBX_JSONPATH_PATH == left->type || ZBX_JSONPATH_PATH == right->type) && + (ZBX_JSONPATH_CONST == left->type || ZBX_JSONPATH_CONST == right->type)) + { + left->type = ZBX_JSONPATH_PATH_OP; + + if (ZBX_JSONPATH_CONST == right->type) + left->value_token = right->value_token; + else + left->index_token = right->index_token; + } + else + left->type = ZBX_JSONPATH_VALUE; + continue; + case ZBX_JSONPATH_TOKEN_OP_AND: + if (ZBX_JSONPATH_PATH == left->type) + left->type = ZBX_JSONPATH_VALUE; + + if (ZBX_JSONPATH_PATH == right->type) + right->type = ZBX_JSONPATH_VALUE; + + if (ZBX_JSONPATH_PATH_OP == left->type && ZBX_JSONPATH_PATH_OP == right->type) + continue; + + if ((ZBX_JSONPATH_PATH_OP == left->type || ZBX_JSONPATH_PATH_OP == right->type) && + (ZBX_JSONPATH_VALUE == left->type || ZBX_JSONPATH_VALUE == right->type)) + { + if (ZBX_JSONPATH_PATH_OP != left->type) + *left = *right; + } + else + left->type = ZBX_JSONPATH_VALUE; + continue; + default: + left->type = ZBX_JSONPATH_VALUE; + left->index_token = NULL; + left->value_token = NULL; + break; + } + } + + if (1 == stack.values_num && ZBX_JSONPATH_PATH_OP == stack.values[0].type) + { + exp->index_token = stack.values[0].index_token; + exp->value_token = stack.values[0].value_token; + } +out: + zbx_vector_jpi_value_destroy(&stack); +} + /****************************************************************************** * * * Purpose: parse jsonpath filter expression in format * @@ -888,6 +1172,10 @@ out: zbx_vector_ptr_create(&segment->data.expression.tokens); zbx_vector_ptr_append_array(&segment->data.expression.tokens, output.values, output.values_num); + /* index only json path that has been definite until this point */ + if (0 != jsonpath->definite) + jsonpath_expression_prepare_index(&segment->data.expression); + jsonpath->definite = 0; } cleanup: @@ -941,18 +1229,13 @@ static int jsonpath_parse_names(const char *list, zbx_jsonpath_t *jsonpath, cons } else if (*start == *end) { - zbx_jsonpath_list_node_t *node; - if (start + 1 == end) { ret = zbx_jsonpath_error(start); goto out; } - node = jsonpath_list_create_node(end - start + 1); - jsonpath_unquote(node->data, start, end - start + 1); - node->next = head; - head = node; + head = jsonpath_list_append_name(head, start, (size_t)(end - start)); parsed_name = 1; start = NULL; } @@ -1059,19 +1342,13 @@ static int jsonpath_parse_indexes(const char *list, zbx_jsonpath_t *jsonpath, co if (NULL != start) { - int value; - if ('-' == *start && end == start + 1) { ret = zbx_jsonpath_error(start); goto out; } - node = jsonpath_list_create_node(sizeof(int)); - node->next = head; - head = node; - value = atoi(start); - memcpy(node->data, &value, sizeof(int)); + head = jsonpath_list_append_index(head, atoi(start), type == ZBX_JSONPATH_SEGMENT_MATCH_LIST); start = NULL; parsed_index = 1; } @@ -1235,7 +1512,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat { zbx_jsonpath_segment_t *segment; const char *ptr; - int len; + size_t len; segment = &jsonpath->segments[jsonpath->segments_num]; jsonpath->segments_num++; @@ -1251,6 +1528,8 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat for (ptr = start; 0 != isalnum((unsigned char)*ptr) || '_' == *ptr;) ptr++; + len = (size_t)(ptr - start); + if ('(' == *ptr) { const char *end = ptr + 1; @@ -1258,28 +1537,28 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat SKIP_WHITESPACE(end); if (')' == *end) { - if (ZBX_CONST_STRLEN("min") == ptr - start && 0 == strncmp(start, "min", ptr - start)) + if (ZBX_CONST_STRLEN("min") == len && 0 == strncmp(start, "min", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_MIN; } - else if (ZBX_CONST_STRLEN("max") == ptr - start && 0 == strncmp(start, "max", ptr - start)) + else if (ZBX_CONST_STRLEN("max") == len && 0 == strncmp(start, "max", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_MAX; } - else if (ZBX_CONST_STRLEN("avg") == ptr - start && 0 == strncmp(start, "avg", ptr - start)) + else if (ZBX_CONST_STRLEN("avg") == len && 0 == strncmp(start, "avg", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_AVG; } - else if (ZBX_CONST_STRLEN("length") == ptr - start && 0 == strncmp(start, "length", ptr - start)) + else if (ZBX_CONST_STRLEN("length") == len && 0 == strncmp(start, "length", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_LENGTH; } - else if (ZBX_CONST_STRLEN("first") == ptr - start && 0 == strncmp(start, "first", ptr - start)) + else if (ZBX_CONST_STRLEN("first") == len && 0 == strncmp(start, "first", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_FIRST; jsonpath->first_match = 1; } - else if (ZBX_CONST_STRLEN("sum") == ptr - start && 0 == strncmp(start, "sum", ptr - start)) + else if (ZBX_CONST_STRLEN("sum") == len && 0 == strncmp(start, "sum", len)) { segment->data.function.type = ZBX_JSONPATH_FUNCTION_SUM; } @@ -1292,7 +1571,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat } } - if (0 < (len = ptr - start)) + if (0 < len) { segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST; segment->data.list.type = ZBX_JSONPATH_LIST_NAME; @@ -1332,123 +1611,84 @@ static int jsonpath_parse_name_reference(const char *start, zbx_jsonpath_t *json /****************************************************************************** * * - * Purpose: convert a pointer to an object/array/value in json data to * - * json parse structure * - * * - * Parameters: pnext - [IN] a pointer to object/array/value data * - * jp - [OUT] json parse data with start/end set * - * * - * Return value: SUCCEED - pointer was converted successfully * - * FAIL - otherwise * - * * - ******************************************************************************/ -static int jsonpath_pointer_to_jp(const char *pnext, struct zbx_json_parse *jp) -{ - if ('[' == *pnext || '{' == *pnext) - { - return zbx_json_brackets_open(pnext, jp); - } - else - { - jp->start = pnext; - jp->end = pnext + json_parse_value(pnext, NULL) - 1; - return SUCCEED; - } -} - -/****************************************************************************** - * * * Purpose: perform the rest of jsonpath query on json data * * * - * Parameters: jp_root - [IN] the document root * - * pnext - [IN] a pointer to object/array/value in json data * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the data were queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_contents(const struct zbx_json_parse *jp_root, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_contents(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) { - struct zbx_json_parse jp_child; + int ret; - switch (*pnext) + switch (obj->type) { - case '{': - if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) - return FAIL; - - return jsonpath_query_object(jp_root, &jp_child, jsonpath, path_depth, done, objects); - case '[': - if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) - return FAIL; - - return jsonpath_query_array(jp_root, &jp_child, jsonpath, path_depth, done, objects); + case ZBX_JSON_TYPE_OBJECT: + ret = jsonpath_query_object(ctx, obj, path_depth); + break; + case ZBX_JSON_TYPE_ARRAY: + ret = jsonpath_query_array(ctx, obj, path_depth); + break; + default: + ret = SUCCEED; } - return SUCCEED; + + return ret; } /****************************************************************************** * * * Purpose: query next segment * * * - * Parameters: jp_root - [IN] the document root * + * Parameters: ctx - [IN] the jsonpath query context * * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to object/array/value in json data * - * jsonpath - [IN] the jsonpath * + * obj - [IN] the current json object * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the segment was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_next_segment(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_next_segment(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj, + int path_depth) { /* check if jsonpath end has been reached, so we have found matching data */ /* (functions are processed afterwards) */ - if (++path_depth == jsonpath->segments_num || - ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath->segments[path_depth].type) + if (++path_depth == ctx->path->segments_num || + ZBX_JSONPATH_SEGMENT_FUNCTION == ctx->path->segments[path_depth].type) { - if (1 == jsonpath->first_match) - *done = 1; + if (1 == ctx->path->first_match) + ctx->found = 1; - zbx_vector_json_add_element(objects, name, pnext); + zbx_vector_jsonobj_ref_add_object(&ctx->objects, name, obj); return SUCCEED; } - /* continue by matching found data against the rest of jsonpath segments */ - return jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, done, objects); + /* continue by matching found object against the rest of jsonpath segments */ + return jsonpath_query_contents(ctx, obj, path_depth); } /****************************************************************************** * * - * Purpose: match object value name against jsonpath segment name list * + * Purpose: match object contents against jsonpath segment name list * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to object value with the specified * - * name * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] parent json object * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_match_name(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; const zbx_jsonpath_list_node_t *node; + zbx_jsonobj_el_t el_local, *el; /* object contents can match only name list */ if (ZBX_JSONPATH_LIST_NAME != segment->data.list.type) @@ -1456,14 +1696,11 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char for (node = segment->data.list.values; NULL != node; node = node->next) { - if (0 == strcmp(name, node->data)) + el_local.name = (char *)node->data; + if (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_search(&parent->data.object, &el_local))) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects)) - { + if (FAIL == jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth)) return FAIL; - } - break; } } @@ -1474,8 +1711,8 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char * * * Purpose: extract value from json data by the specified path * * * - * Parameters: jp - [IN] the parent object * - * path - [IN] the jsonpath (definite) * + * Parameters: obj - [IN] the parent object * + * path - [IN] the jsonpath * * value - [OUT] the extracted value * * * * Return value: SUCCEED - the value was extracted successfully * @@ -1483,28 +1720,29 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char * extract * * * ******************************************************************************/ -static int jsonpath_extract_value(const struct zbx_json_parse *jp, const zbx_jsonpath_t *path, - zbx_variant_t *value) +static int jsonpath_extract_value(zbx_jsonobj_t *obj, zbx_jsonpath_t *path, zbx_variant_t *value) { - struct zbx_json_parse jp_child; - char *data = NULL; - size_t data_alloc = 0; - int ret = FAIL; + int ret = FAIL; + zbx_jsonpath_context_t ctx; - if (FAIL == json_open_path(jp, path, &jp_child)) - goto out; + ctx.path = path; + ctx.found = 0; + ctx.root = obj; + zbx_vector_jsonobj_ref_create(&ctx.objects); - if (NULL == zbx_json_decodevalue_dyn(jp_child.start, &data, &data_alloc, NULL)) + if (SUCCEED == jsonpath_query_contents(&ctx, obj, 0) && 0 != ctx.objects.values_num) { - size_t len = jp_child.end - jp_child.start + 2; + char *str = NULL; + size_t str_alloc = 0, str_offset = 0; - data = (char *)zbx_malloc(NULL, len); - zbx_strlcpy(data, jp_child.start, len); + jsonpath_str_copy_value(&str, &str_alloc, &str_offset, ctx.objects.values[0].value); + zbx_variant_set_str(value, str); + ret = SUCCEED; } - zbx_variant_set_str(value, data); - ret = SUCCEED; -out: + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + zbx_vector_jsonobj_ref_destroy(&ctx.objects); + return ret; } @@ -1521,15 +1759,9 @@ out: ******************************************************************************/ static char *jsonpath_expression_to_str(zbx_jsonpath_expression_t *expression) { - int i; - char *str = NULL; - size_t str_alloc = 0, str_offset = 0; - - if (0 == expression->tokens.values_num) - { - THIS_SHOULD_NEVER_HAPPEN; - return zbx_strdup(NULL, "?"); - } + int i; + char *str = NULL; + size_t str_alloc = 0, str_offset = 0; for (i = 0; i < expression->tokens.values_num; i++) { @@ -1696,36 +1928,123 @@ static int jsonpath_regexp_match(const char *text, const char *pattern, double * /****************************************************************************** * * + * Purpose: add matched object to the index * + * * + * Parameters: index - [IN] the parent object index * + * name - [IN] the name of objec to add to index * + * obj - [IN] the object to add to index * + * value - [IN] the object matched by index path * + * * + ******************************************************************************/ +static void jsonpath_index_append_result(zbx_hashset_t *index, const char *name, zbx_jsonobj_t *obj, + zbx_jsonobj_t *value) +{ + zbx_jsonobj_index_el_t el_local = {.value = NULL}, *el; + size_t value_alloc = 0, value_offset = 0; + zbx_jsonobj_ref_t ref; + + jsonpath_str_copy_value(&el_local.value, &value_alloc, &value_offset, value); + + if (NULL == (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(index, &el_local))) + { + el = (zbx_jsonobj_index_el_t *)zbx_hashset_insert(index, &el_local, sizeof(el_local)); + zbx_vector_jsonobj_ref_create(&el->objects); + } + else + zbx_free(el_local.value); + + ref.name = zbx_strdup(NULL, name); + ref.value = obj; + ref.external = 0; + zbx_vector_jsonobj_ref_append(&el->objects, ref); +} + +/****************************************************************************** + * * + * Purpose: index json object using the expression token index path * + * * + * Parameters: obj - [IN] the object to index * + * index_token - [IN] the expression index token (relative path) * + * * + ******************************************************************************/ +static void jsonpath_create_index(zbx_jsonobj_t *obj, zbx_jsonpath_token_t *index_token) +{ + zbx_jsonpath_context_t ctx; + + jsonobj_init_index(obj, index_token->text); + + ctx.root = obj; + ctx.path = index_token->path; + zbx_vector_jsonobj_ref_create(&ctx.objects); + + if (ZBX_JSON_TYPE_OBJECT == obj->type) + { + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; + + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter))) + { + ctx.found = 0; + if (SUCCEED == jsonpath_query_contents(&ctx, &el->value, 0) && 1 == ctx.objects.values_num) + { + jsonpath_index_append_result(&obj->index->objects, el->name, &el->value, + ctx.objects.values[0].value); + } + + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + } + } + else + { + int i; + + for (i = 0; i < obj->data.array.values_num; i++) + { + char name[MAX_ID_LEN + 1]; + zbx_jsonobj_t *value = obj->data.array.values[i]; + + zbx_snprintf(name, sizeof(name), "%d", i); + + ctx.found = 0; + if (SUCCEED == jsonpath_query_contents(&ctx, value, 0) && 1 == ctx.objects.values_num) + { + jsonpath_index_append_result(&obj->index->objects, name, value, + ctx.objects.values[0].value); + } + + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + } + } + + zbx_vector_jsonobj_ref_destroy(&ctx.objects); +} + +/****************************************************************************** + * * * Purpose: match json array element/object value against jsonpath expression * * * - * Parameters: jp_root - [IN] the document root * + * Parameters: ctx - [IN] the jsonpath query context * * name - [IN] name or index of the next json element * - * pnext - [IN] a pointer to array element/object value * - * jsonpath - [IN] the jsonpath * + * obj - [IN] the jsonobject to match * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_match_expression(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj, + int path_depth) { - struct zbx_json_parse jp; zbx_vector_var_t stack; int i, ret = SUCCEED; zbx_jsonpath_segment_t *segment; zbx_variant_t value, *right; double res; - if (SUCCEED != jsonpath_pointer_to_jp(pnext, &jp)) - return FAIL; - zbx_vector_var_create(&stack); - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; for (i = 0; i < segment->data.expression.tokens.values_num; i++) { @@ -1872,16 +2191,16 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const switch (token->type) { case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: - if (FAIL == jsonpath_extract_value(jp_root, token->path, &value)) + if (FAIL == jsonpath_extract_value(ctx->root, token->path, &value)) zbx_variant_set_none(&value); zbx_vector_var_append_ptr(&stack, &value); break; case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: /* relative path can be applied only to array or object */ - if ('[' != *jp.start && '{' != *jp.start) + if (ZBX_JSON_TYPE_ARRAY != obj->type && ZBX_JSON_TYPE_OBJECT != obj->type) goto out; - if (FAIL == jsonpath_extract_value(&jp, token->path, &value)) + if (FAIL == jsonpath_extract_value(obj, token->path, &value)) zbx_variant_set_none(&value); zbx_vector_var_append_ptr(&stack, &value); break; @@ -1917,7 +2236,7 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const jsonpath_variant_to_boolean(&stack.values[0]); if (SUCCEED != zbx_double_compare(stack.values[0].data.dbl, 0.0)) - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, objects); + ret = jsonpath_query_next_segment(ctx, name, obj, path_depth); out: for (i = 0; i < stack.values_num; i++) zbx_variant_clear(&stack.values[i]); @@ -1928,50 +2247,91 @@ out: /****************************************************************************** * * + * Purpose: query indexed object fields for jsonpath segment match * + * * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object to query * + * path_depth - [IN] the jsonpath segment to match * + * * + * Return value: SUCCEED - the object was queried successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_match_indexed_expression(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) +{ + zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; + zbx_jsonobj_index_el_t index_local, *el; + + index_local.value = segment->data.expression.value_token->text; + + if (NULL != (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(&obj->index->objects, &index_local))) + { + int i; + + for (i = 0; i < el->objects.values_num; i++) + { + jsonpath_match_expression(ctx, el->objects.values[i].name, el->objects.values[i].value, + path_depth); + } + } + + return SUCCEED; +} + +/****************************************************************************** + * * * Purpose: query object fields for jsonpath segment match * * * - * Parameters: jp_root - [IN] the document root * - * jp - [IN] the json object to query * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * obj - [IN] the json object to query * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the object was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth) { - const char *pnext = NULL; - char name[MAX_STRING_LEN]; const zbx_jsonpath_segment_t *segment; int ret = SUCCEED; + zbx_hashset_iter_t iter; + zbx_jsonobj_el_t *el; - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; - while (NULL != (pnext = zbx_json_pair_next(jp, pnext, name, sizeof(name))) && SUCCEED == ret && 0 == *done) + if (ZBX_JSONPATH_SEGMENT_MATCH_LIST == segment->type) + { + ret = jsonpath_match_name(ctx, obj, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + } + else if (ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION == segment->type && NULL != segment->data.expression.index_token) + { + if (NULL == obj->index) + jsonpath_create_index(obj, segment->data.expression.index_token); + + if (0 == strcmp(obj->index->path, segment->data.expression.index_token->text)) + return jsonpath_match_indexed_expression(ctx, obj, path_depth); + } + + zbx_hashset_iter_reset(&obj->data.object, &iter); + while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter)) && SUCCEED == ret && + 0 == ctx->found) { switch (segment->type) { case ZBX_JSONPATH_SEGMENT_MATCH_ALL: - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_LIST: - ret = jsonpath_match_name(jp_root, name, pnext, jsonpath, path_depth, done, objects); + ret = jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth); break; case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: - ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, done, - objects); + ret = jsonpath_match_expression(ctx, el->name, &el->value, path_depth); break; default: break; } if (1 == segment->detached) - ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, done, objects); + ret = jsonpath_query_contents(ctx, &el->value, path_depth); } return ret; @@ -1979,27 +2339,19 @@ static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const str /****************************************************************************** * * - * Purpose: match array element against segment index list * + * Purpose: array elements against segment index list * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] the json element name (index) * - * pnext - [IN] a pointer to an array element * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] the json array * * path_depth - [IN] the jsonpath segment to match * - * index - [IN] the array element index * - * elements_num - [IN] the total number of elements in array * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, unsigned char *done, - zbx_vector_json_t *objects) +static int jsonpath_match_index(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; const zbx_jsonpath_list_node_t *node; /* array contents can match only index list */ @@ -2012,14 +2364,20 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char memcpy(&query_index, node->data, sizeof(query_index)); - if ((query_index >= 0 && index == query_index) || index == elements_num + query_index) + if (0 > query_index) + query_index += parent->data.array.values_num; + + if (query_index >= 0 && query_index < parent->data.array.values_num) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects)) + char name[MAX_ID_LEN + 1]; + + zbx_snprintf(name, sizeof(name), "%d", query_index); + + if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[query_index], + path_depth)) { return FAIL; } - break; } } @@ -2028,40 +2386,37 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char /****************************************************************************** * * - * Purpose: match array element against segment index range * + * Purpose: match array elements against segment index range * * * - * Parameters: jp_root - [IN] the document root * - * name - [IN] the json element name (index) * - * pnext - [IN] a pointer to an array element * - * jsonpath - [IN] the jsonpath * - * path_depth - [IN] the jsonpath segment to match * - * index - [IN] the array element index * - * elements_num - [IN] the total number of elements in array * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * + * Parameters: ctx - [IN] the jsonpath query context * + * parent - [IN] the json array * + * path_depth - [IN] the jsonpath segment to match * * * * Return value: SUCCEED - no errors, failed match is not an error * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char *name, const char *pnext, - const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, unsigned char *done, - zbx_vector_json_t *objects) +static int jsonpath_match_range(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth) { - int start_index, end_index; - const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth]; + int i, start_index, end_index, values_num; + const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth]; + values_num = parent->data.array.values_num; start_index = (0 != (segment->data.range.flags & 0x01) ? segment->data.range.start : 0); - end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : elements_num); + end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : values_num); if (0 > start_index) - start_index += elements_num; + start_index += values_num; if (0 > end_index) - end_index += elements_num; + end_index += values_num; - if (start_index <= index && end_index > index) + for (i = start_index; i < end_index; i++) { - if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, objects)) + char name[MAX_ID_LEN + 1]; + + zbx_snprintf(name, sizeof(name), "%d", i); + + if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[i], path_depth)) return FAIL; } @@ -2070,62 +2425,69 @@ static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char /****************************************************************************** * * - * Purpose: query array elements for jsonpath segment match * + * Purpose: query json array for jsonpath segment match * * * - * Parameters: jp_root - [IN] the document root * - * jp - [IN] the json array to query * - * jsonpath - [IN] the jsonpath * + * Parameters: ctx - [IN] the jsonpath query context * + * array - [IN] the json array to query * * path_depth - [IN] the jsonpath segment to match * - * done - [OUT] set to 1 when the query is finished * - * objects - [OUT] the matched json elements (name, value) * * * * Return value: SUCCEED - the array was queried successfully * * FAIL - otherwise * * * ******************************************************************************/ -static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, - const zbx_jsonpath_t *jsonpath, int path_depth, unsigned char *done, zbx_vector_json_t *objects) +static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth) { - const char *pnext = NULL; - int index = 0, elements_num = 0, ret = SUCCEED; + int ret = SUCCEED, i; zbx_jsonpath_segment_t *segment; - segment = &jsonpath->segments[path_depth]; + segment = &ctx->path->segments[path_depth]; - while (NULL != (pnext = zbx_json_next(jp, pnext))) - elements_num++; + switch (segment->type) + { + case ZBX_JSONPATH_SEGMENT_MATCH_LIST: + ret = jsonpath_match_index(ctx, array, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + break; + case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: + ret = jsonpath_match_range(ctx, array, path_depth); + if (FAIL == ret || 1 != segment->detached) + return ret; + break; + case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: + if (NULL != segment->data.expression.index_token) + { + if (NULL == array->index) + jsonpath_create_index(array, segment->data.expression.index_token); - while (NULL != (pnext = zbx_json_next(jp, pnext)) && SUCCEED == ret && 0 == *done) + if (0 == strcmp(array->index->path, segment->data.expression.index_token->text)) + return jsonpath_match_indexed_expression(ctx, array, path_depth); + } + break; + default: + break; + } + + for (i = 0; i < array->data.array.values_num && SUCCEED == ret && 0 == ctx->found; i++) { char name[MAX_ID_LEN + 1]; - zbx_snprintf(name, sizeof(name), "%d", index); + zbx_snprintf(name, sizeof(name), "%d", i); + switch (segment->type) { case ZBX_JSONPATH_SEGMENT_MATCH_ALL: - ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, done, - objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_LIST: - ret = jsonpath_match_index(jp_root, name, pnext, jsonpath, path_depth, index, - elements_num, done, objects); - break; - case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: - ret = jsonpath_match_range(jp_root, name, pnext, jsonpath, path_depth, index, - elements_num, done, objects); + ret = jsonpath_query_next_segment(ctx, name, array->data.array.values[i], path_depth); break; case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: - ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, done, - objects); + ret = jsonpath_match_expression(ctx, name, array->data.array.values[i], path_depth); break; default: break; } if (1 == segment->detached) - ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, done, objects); - - index++; + ret = jsonpath_query_contents(ctx, array->data.array.values[i], path_depth); } return ret; @@ -2133,123 +2495,99 @@ static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const stru /****************************************************************************** * * - * Purpose: extract JSON element value from data * - * * - * Parameters: ptr - [IN] pointer to the element to extract * - * element - [OUT] the extracted element * + * Purpose: get numeric value from json data * * * - * Return value: SUCCEED - the element was extracted successfully * - * FAIL - the pointer was not pointing to a JSON element * + * Parameters: obj - [IN] json object * + * value - [OUT] the extracted value * * * - * Comments: String value element is unquoted, other elements are copied as * - * is. * + * Return value: SUCCEED - the value was extracted successfully * + * FAIL - the pointer was not pointing at numeric value * * * ******************************************************************************/ -static int jsonpath_extract_element(const char *ptr, char **element) +static int jsonpath_get_numeric_value(const zbx_jsonobj_t *obj, double *value) { - size_t element_size = 0; - - if (NULL == zbx_json_decodevalue_dyn(ptr, element, &element_size, NULL)) + switch (obj->type) { - struct zbx_json_parse jp; - - if (SUCCEED != zbx_json_brackets_open(ptr, &jp)) + case ZBX_JSON_TYPE_NUMBER: + *value = obj->data.number; + return SUCCEED; + case ZBX_JSON_TYPE_STRING: + if (SUCCEED == is_double(obj->data.string, value)) + return SUCCEED; + zbx_set_json_strerror("array value is not a number or out of range: %s", obj->data.string); + return FAIL; + default: + zbx_set_json_strerror("array value type is not a number or string"); return FAIL; - - *element = jsonpath_strndup(jp.start, jp.end - jp.start + 1); } - - return SUCCEED; } /****************************************************************************** * * - * Purpose: extract numeric value from json data * - * * - * Parameters: ptr - [IN] pointer to the value to extract * - * value - [OUT] the extracted value * + * Purpose: get value from json data * * * * Return value: SUCCEED - the value was extracted successfully * * FAIL - the pointer was not pointing at numeric value * * * ******************************************************************************/ -static int jsonpath_extract_numeric_value(const char *ptr, double *value) +static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj) { - char buffer[MAX_STRING_LEN]; - - if (NULL == zbx_json_decodevalue(ptr, buffer, sizeof(buffer), NULL) || - SUCCEED != is_double(buffer, value)) + switch (obj->type) { - zbx_set_json_strerror("array value is not a number or out of range starting with: %s", ptr); - return FAIL; + case ZBX_JSON_TYPE_STRING: + zbx_strcpy_alloc(str, str_alloc, str_offset, obj->data.string); + return SUCCEED; + break; + default: + return zbx_jsonobj_to_string(str, str_alloc, str_offset, obj); } - - return SUCCEED; } /****************************************************************************** * * * Purpose: apply jsonpath function to the extracted object list * * * - * Parameters: objects - [IN] the matched json elements (name, value) * + * Parameters: in - [IN] the matched objects * * type - [IN] the function type * - * definite_path - [IN] 1 - if the path is definite (pointing at * - * single object) * - * 0 - otherwise * - * output - [OUT] the output value * + * definite_path - [IN/OUT] 1 - if the path is definite (pointing * + * at single object) * + * 0 - otherwise * + * out - [OUT] the result objects * * * * Return value: SUCCEED - the function was applied successfully * * FAIL - invalid input data for the function or internal * * json error * * * ******************************************************************************/ -static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpath_function_type_t type, - int definite_path, char **output) +static int jsonpath_apply_function(const zbx_vector_jsonobj_ref_t *in, zbx_jsonpath_function_type_t type, + int *definite_path, zbx_vector_jsonobj_ref_t *out) { - int i, ret = FAIL; - zbx_vector_json_t objects_tmp; - double result; + int i, ret = FAIL; + double result; + zbx_vector_jsonobj_ref_t tmp; + char buffer[64]; - zbx_vector_json_create(&objects_tmp); + zbx_vector_jsonobj_ref_create(&tmp); if (ZBX_JSONPATH_FUNCTION_NAME == type) { - if (0 == objects->values_num) + if (0 == in->values_num) { zbx_set_json_strerror("cannot extract name from empty result"); goto out; } - /* For definite paths we have single output value, so return its name. */ - /* Otherwise return array of all output element names. */ - if (0 == definite_path) - { - struct zbx_json j; - - /* reserve some space for output json, 1k being large enough to satisfy most queries */ - zbx_json_initarray(&j, 1024); - for (i = 0; i < objects->values_num; i++) - zbx_json_addstring(&j, NULL, objects->values[i].name, ZBX_JSON_TYPE_STRING); - - zbx_json_close(&j); - *output = zbx_strdup(NULL, j.buffer); - zbx_json_clean(&j); - } - else - *output = zbx_strdup(NULL, objects->values[0].name); + for (i = 0; i < in->values_num; i++) + zbx_vector_jsonobj_ref_add_string(out, "", in->values[i].name); ret = SUCCEED; goto out; } /* convert definite path result to object array if possible */ - if (0 != definite_path) + if (0 != *definite_path) { - const char *pnext; - struct zbx_json_parse jp; - int index = 0; - - if (0 == objects->values_num || '[' != *objects->values[0].value) + if (0 == in->values_num || ZBX_JSON_TYPE_ARRAY != in->values[0].value->type) { /* all functions can be applied only to arrays */ /* attempt to apply a function to non-array will fail */ @@ -2257,51 +2595,51 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat goto out; } - if (FAIL == zbx_json_brackets_open(objects->values[0].value, &jp)) - goto out; - - for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp, pnext));) + for (i = 0; i < in->values[0].value->data.array.values_num; i++) { char name[MAX_ID_LEN + 1]; - zbx_snprintf(name, sizeof(name), "%d", index++); - zbx_vector_json_add_element(&objects_tmp, name, pnext); + zbx_snprintf(name, sizeof(name), "%d", i); + zbx_vector_jsonobj_ref_add_object(&tmp, name, in->values[0].value->data.array.values[i]); } - objects = &objects_tmp; + in = &tmp; + *definite_path = 0; } if (ZBX_JSONPATH_FUNCTION_LENGTH == type) { - *output = zbx_dsprintf(NULL, "%d", objects->values_num); + zbx_snprintf(buffer, sizeof(buffer), "%d", in->values_num); + zbx_vector_jsonobj_ref_add_string(out, "", buffer); + *definite_path = 1; ret = SUCCEED; goto out; } if (ZBX_JSONPATH_FUNCTION_FIRST == type) { - if (0 < objects->values_num) - ret = jsonpath_extract_element(objects->values[0].value, output); - else - ret = SUCCEED; + if (0 < in->values_num) + zbx_vector_jsonobj_ref_add(out, &in->values[0]); + *definite_path = 1; + ret = SUCCEED; goto out; } - if (0 == objects->values_num) + if (0 == in->values_num) { zbx_set_json_strerror("cannot apply aggregation function to empty array"); goto out; } - if (FAIL == jsonpath_extract_numeric_value(objects->values[0].value, &result)) + if (FAIL == jsonpath_get_numeric_value(in->values[0].value, &result)) goto out; - for (i = 1; i < objects->values_num; i++) + for (i = 1; i < in->values_num; i++) { double value; - if (FAIL == jsonpath_extract_numeric_value(objects->values[i].value, &value)) + if (FAIL == jsonpath_get_numeric_value(in->values[i].value, &value)) goto out; switch (type) @@ -2324,19 +2662,22 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat } if (ZBX_JSONPATH_FUNCTION_AVG == type) - result /= objects->values_num; + result /= in->values_num; - *output = zbx_dsprintf(NULL, ZBX_FS_DBL, result); - if (SUCCEED != is_double(*output, NULL)) + zbx_print_double(buffer, sizeof(buffer), result); + if (SUCCEED != is_double(buffer, NULL)) { - zbx_set_json_strerror("invalid function result: %s", *output); + zbx_set_json_strerror("invalid function result: %s", buffer); goto out; } - del_zeros(*output); + + del_zeros(buffer); + zbx_vector_jsonobj_ref_add_string(out, "", buffer); + *definite_path = 1; ret = SUCCEED; out: - zbx_vector_json_clear_ext(&objects_tmp); - zbx_vector_json_destroy(&objects_tmp); + zbx_vector_jsonobj_ref_clear_ext(&tmp); + zbx_vector_jsonobj_ref_destroy(&tmp); return ret; } @@ -2345,58 +2686,47 @@ out: * * * Purpose: apply jsonpath function to the extracted object list * * * - * Parameters: jp_root - [IN] the document root * - * objects - [IN] the matched json elements (name, value) * - * jsonpath - [IN] the jsonpath * - * path_depth - [IN] the jsonpath segment to match * - * output - [OUT] the output value * + * Parameters: ctx - [IN] the jsonpath query context * + * path_depth - [IN] the jsonpath segment to match * + * definite_path - [IN/OUT] * + * out - [OUT] the result object * * * * Return value: SUCCEED - the function was applied successfully * * FAIL - invalid input data for the function or internal * * json error * * * ******************************************************************************/ -static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const zbx_vector_json_t *objects, - const zbx_jsonpath_t *jsonpath, int path_depth, char **output) +static int jsonpath_apply_functions(zbx_jsonpath_context_t *ctx, int path_depth, int *definite_path, + zbx_vector_jsonobj_ref_t *out) { - int ret, definite_path; - zbx_vector_json_t input; - char *input_json = NULL; + int ret; + zbx_vector_jsonobj_ref_t in; - zbx_vector_json_create(&input); + zbx_vector_jsonobj_ref_create(&in); /* when functions are applied directly to the json document (at the start of the jsonpath ) */ /* it makes all document as input object */ if (0 == path_depth) - zbx_vector_json_add_element(&input, "", jp_root->start); + zbx_vector_jsonobj_ref_add_object(&in, "", ctx->root); else - zbx_vector_json_copy(&input, objects); - - definite_path = jsonpath->definite; + zbx_vector_jsonobj_ref_copy(&in, &ctx->objects); for (;;) { - ret = jsonpath_apply_function(&input, jsonpath->segments[path_depth++].data.function.type, - definite_path, output); + ret = jsonpath_apply_function(&in, ctx->path->segments[path_depth++].data.function.type, + definite_path, out); - zbx_vector_json_clear_ext(&input); - zbx_free(input_json); + zbx_vector_jsonobj_ref_clear_ext(&in); - if (SUCCEED != ret || path_depth == jsonpath->segments_num) + if (SUCCEED != ret || path_depth == ctx->path->segments_num) break; - if (NULL != *output) - { - zbx_vector_json_add_element(&input, "", *output); - input_json = *output; - *output = NULL; - } - - /* functions return single value, so for the next functions path becomes definite */ - definite_path = 1; + zbx_vector_jsonobj_ref_copy(&in, out); + zbx_vector_jsonobj_ref_clear_ext(out); } - zbx_vector_json_destroy(&input); + zbx_vector_jsonobj_ref_clear_ext(&in); + zbx_vector_jsonobj_ref_destroy(&in); return ret; } @@ -2405,49 +2735,37 @@ static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const * * * Purpose: format query result, depending on jsonpath type * * * - * Parameters: objects - [IN] the matched json elements (name, value) * - * jsonpath - [IN] the jsonpath used to acquire result * - * output - [OUT] the output value * + * Parameters: objects - [IN] the matched json refs (name, value) * + * definite_path - [IN] the jsonpath definite flag * + * output - [OUT] the output value * * * * Return value: SUCCEED - the result was formatted successfully * * FAIL - invalid result data (internal json error) * * * ******************************************************************************/ -static int jsonpath_format_query_result(const zbx_vector_json_t *objects, zbx_jsonpath_t *jsonpath, char **output) +static int jsonpath_format_query_result(const zbx_vector_jsonobj_ref_t *objects, int definite_path, char **output) { size_t output_offset = 0, output_alloc; int i; + char delim; if (0 == objects->values_num) return SUCCEED; - if (1 == jsonpath->definite) - { - return jsonpath_extract_element(objects->values[0].value, output); - } + if (1 == definite_path) + return jsonpath_str_copy_value(output, &output_alloc, &output_offset, objects->values[0].value); /* reserve 32 bytes per returned object plus array start/end [] and terminating zero */ - output_alloc = objects->values_num * 32 + 3; + output_alloc = (size_t)objects->values_num * 32 + 3; *output = (char *)zbx_malloc(NULL, output_alloc); - zbx_chrcpy_alloc(output, &output_alloc, &output_offset, '['); + delim = '['; for (i = 0; i < objects->values_num; i++) { - struct zbx_json_parse jp; - - if (FAIL == jsonpath_pointer_to_jp(objects->values[i].value, &jp)) - { - zbx_set_json_strerror("cannot format query result, unrecognized json part starting with: %s", - objects->values[i].value); - zbx_free(*output); - return FAIL; - } - - if (0 != i) - zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ','); - - zbx_strncpy_alloc(output, &output_alloc, &output_offset, jp.start, jp.end - jp.start + 1); + zbx_chrcpy_alloc(output, &output_alloc, &output_offset, delim); + zbx_jsonobj_to_string(output, &output_alloc, &output_offset, objects->values[i].value); + delim = ','; } zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ']'); @@ -2470,7 +2788,7 @@ void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath) * Purpose: compile jsonpath to be used in queries * * * * Parameters: path - [IN] the path to parse * - * jsonpath - [IN/OUT] the compiled jsonpath * + * jsonpath - [IN/OUT] the compiled jsonpath * * * * Return value: SUCCEED - the segment was parsed successfully * * FAIL - otherwise * @@ -2582,38 +2900,89 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath) * being counted as successful query) * * FAIL - otherwise * * * + * Comments: This function is for compatibility purposes. Where possible the * + * zbx_jsonobj_query() function must be used. * + * * ******************************************************************************/ int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output) { + int ret; + zbx_jsonobj_t obj; + + if (SUCCEED != zbx_jsonobj_open(jp->start, &obj)) + return FAIL; + + ret = zbx_jsonobj_query(&obj, path, output); + + zbx_jsonobj_clear(&obj); + + return ret; +} + +/****************************************************************************** + * * + * Purpose: perform jsonpath query on the specified json object * + * * + * Parameters: obj - [IN] the json object * + * path - [IN] the jsonpath * + * output - [OUT] the output value * + * * + * Return value: SUCCEED - the query was performed successfully (empty result * + * being counted as successful query) * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_jsonobj_query(zbx_jsonobj_t *obj, const char *path, char **output) +{ + zbx_jsonpath_context_t ctx; zbx_jsonpath_t jsonpath; - int path_depth = 0, ret = SUCCEED; - zbx_vector_json_t objects; - unsigned char done = 0; + int ret = SUCCEED; if (FAIL == zbx_jsonpath_compile(path, &jsonpath)) return FAIL; - zbx_vector_json_create(&objects); + ctx.found = 0; + ctx.root = obj; + ctx.path = &jsonpath; + zbx_vector_jsonobj_ref_create(&ctx.objects); - if ('{' == *jp->start) - ret = jsonpath_query_object(jp, jp, &jsonpath, path_depth, &done, &objects); - else if ('[' == *jp->start) - ret = jsonpath_query_array(jp, jp, &jsonpath, path_depth, &done, &objects); + switch (obj->type) + { + case ZBX_JSON_TYPE_OBJECT: + ret = jsonpath_query_object(&ctx, obj, 0); + break; + case ZBX_JSON_TYPE_ARRAY: + ret = jsonpath_query_array(&ctx, obj, 0); + break; + default: + break; + } if (SUCCEED == ret) { + zbx_vector_jsonobj_ref_t out; + int definite_path = jsonpath.definite, path_depth; + + zbx_vector_jsonobj_ref_create(&out); + path_depth = jsonpath.segments_num; while (0 < path_depth && ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath.segments[path_depth - 1].type) path_depth--; if (path_depth < jsonpath.segments_num) - ret = jsonpath_apply_functions(jp, &objects, &jsonpath, path_depth, output); + { + if (SUCCEED == (ret = jsonpath_apply_functions(&ctx, path_depth, &definite_path, &out))) + ret = jsonpath_format_query_result(&out, definite_path, output); + } else - ret = jsonpath_format_query_result(&objects, &jsonpath, output); + ret = jsonpath_format_query_result(&ctx.objects, definite_path, output); + + zbx_vector_jsonobj_ref_clear_ext(&out); + zbx_vector_jsonobj_ref_destroy(&out); } - zbx_vector_json_clear_ext(&objects); - zbx_vector_json_destroy(&objects); + zbx_vector_jsonobj_ref_clear_ext(&ctx.objects); + zbx_vector_jsonobj_ref_destroy(&ctx.objects); zbx_jsonpath_clear(&jsonpath); return ret; diff --git a/src/libs/zbxjson/jsonpath.h b/src/libs/zbxjson/jsonpath.h index ae3262a1009..644aef6aa4a 100644 --- a/src/libs/zbxjson/jsonpath.h +++ b/src/libs/zbxjson/jsonpath.h @@ -20,9 +20,10 @@ #ifndef ZABBIX_JSONPATH_H #define ZABBIX_JSONPATH_H -#include "zbxalgo.h" #include "zbxjson.h" +typedef struct zbx_jsonpath_token zbx_jsonpath_token_t; + typedef enum { ZBX_JSONPATH_SEGMENT_UNKNOWN, @@ -79,10 +80,19 @@ typedef struct } zbx_jsonpath_range_t; +typedef enum +{ + ZBX_JSONPATH_EXPRESSION_INDEX_TRUE, + ZBX_JSONPATH_EXPRESSION_INDEX_FALSE, +} +zbx_json_path_expression_index_t; + /* expression tokens in postfix notation */ typedef struct { zbx_vector_ptr_t tokens; + zbx_jsonpath_token_t *index_token; /* relative path token that is used to index parent object */ + zbx_jsonpath_token_t *value_token; /* the index value token */ } zbx_jsonpath_expression_t; @@ -151,12 +161,11 @@ typedef enum } zbx_jsonpath_token_type_t; -typedef struct +struct zbx_jsonpath_token { unsigned char type; char *text; zbx_jsonpath_t *path; -} -zbx_jsonpath_token_t; +}; #endif diff --git a/src/zabbix_agent/Makefile.am b/src/zabbix_agent/Makefile.am index d2006c2399f..740d9cd1a3b 100644 --- a/src/zabbix_agent/Makefile.am +++ b/src/zabbix_agent/Makefile.am @@ -52,13 +52,13 @@ zabbix_agentd_LDADD = \ $(top_builddir)/src/libs/zbxsysinfo/simple/libsimplesysinfo.a \ $(top_builddir)/src/libs/zbxlog/libzbxlog.a \ $(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \ - $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxsys/libzbxsys.a \ $(top_builddir)/src/libs/zbxnix/libzbxnix.a \ $(top_builddir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_builddir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ $(top_builddir)/src/libs/zbxconf/libzbxconf.a \ $(top_builddir)/src/libs/zbxjson/libzbxjson.a \ + $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_builddir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_builddir)/src/libs/zbxexec/libzbxexec.a \ diff --git a/src/zabbix_server/Makefile.am b/src/zabbix_server/Makefile.am index 23d1430c2db..1c03570c1b0 100644 --- a/src/zabbix_server/Makefile.am +++ b/src/zabbix_server/Makefile.am @@ -93,8 +93,8 @@ zabbix_server_LDADD = \ $(top_builddir)/src/libs/zbxself/libzbxself.a \ $(top_builddir)/src/libs/zbxself/libzbxself_server.a \ $(top_builddir)/src/libs/zbxnix/libzbxnix.a \ - $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_builddir)/src/libs/zbxsys/libzbxsys.a \ + $(top_builddir)/src/libs/zbxipcservice/libzbxipcservice.a \ $(top_builddir)/src/libs/zbxconf/libzbxconf.a \ $(top_builddir)/src/libs/zbxmedia/libzbxmedia.a \ $(top_builddir)/src/libs/zbxaudit/libzbxaudit.a \ @@ -104,6 +104,8 @@ zabbix_server_LDADD = \ $(top_builddir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_builddir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ $(top_builddir)/src/libs/zbxjson/libzbxjson.a \ + $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \ + $(top_builddir)/src/libs/zbxvault/libzbxvault.a \ $(top_builddir)/src/libs/zbxhttp/libzbxhttp.a \ $(top_builddir)/src/libs/zbxipcservice/libzbxipcservice.a \ $(top_builddir)/src/libs/zbxexec/libzbxexec.a \ diff --git a/src/zabbix_server/preprocessor/item_preproc.c b/src/zabbix_server/preprocessor/item_preproc.c index 2748cade102..12d81aa4bff 100644 --- a/src/zabbix_server/preprocessor/item_preproc.c +++ b/src/zabbix_server/preprocessor/item_preproc.c @@ -819,7 +819,8 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char ** * * * Purpose: execute jsonpath query * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -827,18 +828,59 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char ** * FAIL - otherwise * * * ******************************************************************************/ -static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, char **errmsg) +static int item_preproc_jsonpath_op(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params, + char **errmsg) { - struct zbx_json_parse jp; - char *data = NULL; + char *data = NULL; - if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) - return FAIL; + if (NULL == cache) + { + zbx_jsonobj_t obj; + + if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) + return FAIL; - if (FAIL == zbx_json_open(value->data.str, &jp) || FAIL == zbx_jsonpath_query(&jp, params, &data)) + if (FAIL == zbx_jsonobj_open(value->data.str, &obj)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } + + if (FAIL == zbx_jsonobj_query(&obj, params, &data)) + { + zbx_jsonobj_clear(&obj); + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } + + zbx_jsonobj_clear(&obj); + } + else { - *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); - return FAIL; + zbx_jsonobj_t *obj; + + if (NULL == (obj = (zbx_jsonobj_t *)zbx_preproc_cache_get(cache, ZBX_PREPROC_JSONPATH))) + { + if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) + return FAIL; + + obj = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t)); + + if (SUCCEED != zbx_jsonobj_open(value->data.str, obj)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + zbx_free(obj); + return FAIL; + } + + zbx_preproc_cache_put(cache, ZBX_PREPROC_JSONPATH, obj); + } + + if (FAIL == zbx_jsonobj_query(obj, params, &data)) + { + *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); + return FAIL; + } } if (NULL == data) @@ -857,7 +899,8 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch * * * Purpose: execute jsonpath query * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -865,11 +908,12 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch * FAIL - otherwise * * * ******************************************************************************/ -static int item_preproc_jsonpath(zbx_variant_t *value, const char *params, char **errmsg) +static int item_preproc_jsonpath(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params, + char **errmsg) { char *err = NULL; - if (SUCCEED == item_preproc_jsonpath_op(value, params, &err)) + if (SUCCEED == item_preproc_jsonpath_op(cache, value, params, &err)) return SUCCEED; *errmsg = zbx_dsprintf(*errmsg, "cannot extract value from json by path \"%s\": %s", params, err); @@ -1456,7 +1500,8 @@ fail: * * * Purpose: parse Prometheus format metrics * * * - * Parameters: value - [IN/OUT] the value to process * + * Parameters: cache - [IN] the preprocessing cache * + * value - [IN/OUT] the value to process * * params - [IN] the operation parameters * * errmsg - [OUT] error message * * * @@ -2033,7 +2078,7 @@ int zbx_item_preproc(zbx_preproc_cache_t *cache, unsigned char value_type, zbx_v ret = item_preproc_xpath(value, op->params, error); break; case ZBX_PREPROC_JSONPATH: - ret = item_preproc_jsonpath(value, op->params, error); + ret = item_preproc_jsonpath(cache, value, op->params, error); break; case ZBX_PREPROC_VALIDATE_RANGE: ret = item_preproc_validate_range(value_type, value, op->params, error); diff --git a/src/zabbix_server/preprocessor/preproc_cache.c b/src/zabbix_server/preprocessor/preproc_cache.c index 1ea66e5267a..58803324f06 100644 --- a/src/zabbix_server/preprocessor/preproc_cache.c +++ b/src/zabbix_server/preprocessor/preproc_cache.c @@ -96,6 +96,11 @@ void zbx_preproc_cache_clear(zbx_preproc_cache_t *cache) case ZBX_PREPROC_PROMETHEUS_PATTERN: zbx_prometheus_clear((zbx_prometheus_t *)cache->refs.values[i].impl); zbx_free(cache->refs.values[i].impl); + break; + case ZBX_PREPROC_JSONPATH: + zbx_jsonobj_clear((zbx_jsonobj_t *)cache->refs.values[i].impl); + zbx_free(cache->refs.values[i].impl); + break; } } |