diff options
author | minfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68> | 2018-08-31 09:24:04 +0000 |
---|---|---|
committer | minfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68> | 2018-08-31 09:24:04 +0000 |
commit | 88b8fa42f150c4357efd8431ce5cdabac85f613b (patch) | |
tree | 6dab73d3d4304233d9073577306add95d3888959 | |
parent | 8029762a0ac265a3bd9a9bd47158756cd14bba1d (diff) | |
download | libapr-88b8fa42f150c4357efd8431ce5cdabac85f613b.tar.gz |
Make it possible to iterate through JSON arrays as well as JSON objects.
As a side effect, this removes the need for the temporary pool
during JSON decoding.
git-svn-id: http://svn.apache.org/repos/asf/apr/apr/trunk@1839735 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | include/apr_json.h | 70 | ||||
-rw-r--r-- | json/apr_json.c | 81 | ||||
-rw-r--r-- | json/apr_json_decode.c | 57 | ||||
-rw-r--r-- | json/apr_json_encode.c | 17 | ||||
-rw-r--r-- | test/testjson.c | 68 |
5 files changed, 242 insertions, 51 deletions
diff --git a/include/apr_json.h b/include/apr_json.h index 3b2575399..06ec58ebf 100644 --- a/include/apr_json.h +++ b/include/apr_json.h @@ -88,6 +88,11 @@ extern "C" { typedef struct apr_json_object_t apr_json_object_t; /** + * A structure to hold a JSON array. + */ +typedef struct apr_json_array_t apr_json_array_t; + +/** * Enum that represents the type of the given JSON value. */ typedef enum apr_json_type_e { @@ -116,6 +121,8 @@ typedef struct apr_json_string_t { * Use apr_json_value_create() to allocate. */ typedef struct apr_json_value_t { + /** Links to the rest of the values if in an array */ + APR_RING_ENTRY(apr_json_value_t) link; /** preceding whitespace, if any */ const char *pre; /** trailing whitespace, if any */ @@ -127,7 +134,7 @@ typedef struct apr_json_value_t { /** JSON object */ apr_json_object_t *object; /** JSON array */ - apr_array_header_t *array; + apr_json_array_t *array; /** JSON floating point value */ double dnumber; /** JSON long integer value */ @@ -166,6 +173,18 @@ struct apr_json_object_t { }; /** + * A structure to hold a JSON array. + * + * Use apr_json_array_create() to allocate. + */ +struct apr_json_array_t { + /** The key value pairs in the object are in this list */ + APR_RING_HEAD(apr_json_array_list_t, apr_json_value_t) list; + /** Array of JSON objects */ + apr_array_header_t *array; +}; + +/** * Allocate and return a apr_json_value_t structure. * * @param pool The pool to allocate from. @@ -274,7 +293,7 @@ APR_DECLARE(apr_status_t) apr_json_object_set(apr_json_value_t *obj, */ APR_DECLARE(apr_json_kv_t *) apr_json_object_get(apr_json_value_t *obj, const char *key, - apr_ssize_t klen) + apr_ssize_t klen) __attribute__((nonnull(1, 2))); /** @@ -303,6 +322,53 @@ APR_DECLARE(apr_json_kv_t *) apr_json_object_next(apr_json_value_t *obj, __attribute__((nonnull(1, 2)));; /** + * Add the value to the end of this array. + * @param arr The JSON array. + * @param val Value to add to the array. + * @param pool Pool to use. + * @return APR_SUCCESS on success, APR_EINVAL if the array value is not + * an APR_JSON_ARRAY. + */ +APR_DECLARE(apr_status_t) apr_json_array_add(apr_json_value_t *arr, + apr_json_value_t *val, apr_pool_t *pool) + __attribute__((nonnull(1, 2, 3))); + +/** + * Look up the value associated with a key in a JSON object. + * @param arr The JSON array. + * @param index The index of the element in the array. + * @return Returns NULL if the element is out of bounds. + */ +APR_DECLARE(apr_json_value_t *) + apr_json_array_get(apr_json_value_t *arr, int index) + __attribute__((nonnull(1))); + +/** + * Get the first value associated with an array. + * + * If the value is an array, this function returns the first value. + * @param arr The JSON array. + * @return Returns the first value, or NULL if not an array, or the array is + * empty. + */ +APR_DECLARE(apr_json_value_t *) apr_json_array_first(const apr_json_value_t *obj) + __attribute__((nonnull(1)));; + +/** + * Get the next value associated with an array. + * + * This function returns the next value in the array, or NULL if no more + * values are present. + * @param arr The JSON array. + * @param val The previous element of the array. + * @return Returns the next value in the array, or NULL if not an array, or + * we have reached the end of the array. + */ +APR_DECLARE(apr_json_value_t *) apr_json_array_next(const apr_json_value_t *arr, + const apr_json_value_t *val) + __attribute__((nonnull(1, 2)));; + +/** * Decode utf8-encoded JSON string into apr_json_value_t. * @param retval the result * @param injson utf8-encoded JSON string. diff --git a/json/apr_json.c b/json/apr_json.c index 8a4c91d60..b8b55fc9a 100644 --- a/json/apr_json.c +++ b/json/apr_json.c @@ -25,6 +25,12 @@ APR_RING_CHECK_CONSISTENCY(&(o)->list, apr_json_kv_t, link); \ } while (0) +#define APR_JSON_ARRAY_INSERT_TAIL(o, e) do { \ + apr_json_value_t *ap__b = (e); \ + APR_RING_INSERT_TAIL(&(o)->list, ap__b, apr_json_value_t, link); \ + APR_RING_CHECK_CONSISTENCY(&(o)->list, apr_json_value_t, link); \ + } while (0) + apr_json_value_t *apr_json_value_create(apr_pool_t *pool) { return apr_pcalloc(pool, sizeof(apr_json_value_t)); @@ -67,7 +73,9 @@ apr_json_value_t *apr_json_array_create(apr_pool_t *pool, int nelts) if (json) { json->type = APR_JSON_ARRAY; - json->value.array = apr_array_make(pool, nelts, + json->value.array = apr_pcalloc(pool, sizeof(apr_json_array_t)); + APR_RING_INIT(&json->value.array->list, apr_json_value_t, link); + json->value.array->array = apr_array_make(pool, nelts, sizeof(apr_json_value_t *)); } @@ -159,8 +167,7 @@ apr_status_t apr_json_object_set(apr_json_value_t *object, apr_json_value_t *key return APR_SUCCESS; } -apr_json_kv_t *apr_json_object_get(apr_json_value_t *object, const char *key, - apr_ssize_t klen) +apr_json_kv_t *apr_json_object_get(apr_json_value_t *object, const char *key, apr_ssize_t klen) { if (object->type != APR_JSON_OBJECT) { return NULL; @@ -205,6 +212,74 @@ apr_json_kv_t *apr_json_object_next(apr_json_value_t *obj, apr_json_kv_t *kv) } } +apr_status_t apr_json_array_add(apr_json_value_t *arr, + apr_json_value_t *val, apr_pool_t *pool) +{ + apr_array_header_t *array; + + if (arr->type != APR_JSON_ARRAY) { + return APR_EINVAL; + } + + APR_RING_ELEM_INIT(val, link); + APR_JSON_ARRAY_INSERT_TAIL(arr->value.array, val); + + array = arr->value.array->array; + if (array) { + *((apr_json_value_t **) (apr_array_push(array))) = val; + } + + return APR_SUCCESS; +} + +apr_json_value_t *apr_json_array_get(apr_json_value_t *arr, int index) +{ + if (arr->type != APR_JSON_ARRAY) { + return NULL; + } + + return APR_ARRAY_IDX(arr->value.array->array, index, apr_json_value_t *); +} + +apr_json_value_t *apr_json_array_first(const apr_json_value_t *arr) +{ + apr_json_value_t *val; + + if (arr->type != APR_JSON_ARRAY) { + return NULL; + } + + val = APR_RING_FIRST(&(arr->value.array)->list); + + if (val + != APR_RING_SENTINEL(&(arr->value.object)->list, apr_json_value_t, + link)) { + return val; + } else { + return NULL; + } +} + +apr_json_value_t *apr_json_array_next(const apr_json_value_t *arr, + const apr_json_value_t *val) +{ + apr_json_value_t *next; + + if (arr->type != APR_JSON_ARRAY) { + return NULL; + } + + next = APR_RING_NEXT((val), link); + + if (next + != APR_RING_SENTINEL(&(arr->value.array)->list, apr_json_value_t, + link)) { + return next; + } else { + return NULL; + } +} + apr_json_value_t *apr_json_overlay(apr_pool_t *p, apr_json_value_t *overlay, apr_json_value_t *base, int flags) diff --git a/json/apr_json_decode.c b/json/apr_json_decode.c index c5ce25f4b..e8d44013b 100644 --- a/json/apr_json_decode.c +++ b/json/apr_json_decode.c @@ -350,16 +350,13 @@ out: } static apr_status_t apr_json_decode_array(apr_json_scanner_t * self, - apr_array_header_t ** retval) + apr_json_value_t * array) { apr_status_t status = APR_SUCCESS; - apr_pool_t *link_pool = NULL; - json_link_t *head = NULL, *tail = NULL; apr_size_t count = 0; if (self->p >= self->e) { - status = APR_EOF; - goto out; + return APR_EOF; } if (self->level <= 0) { @@ -367,19 +364,21 @@ static apr_status_t apr_json_decode_array(apr_json_scanner_t * self, } self->level--; - if ((status = apr_pool_create(&link_pool, self->pool))) { - return status; + array->value.array = apr_pcalloc(self->pool, + sizeof(apr_json_array_t)); + if (!array) { + return APR_ENOMEM; } + APR_RING_INIT(&array->value.array->list, apr_json_value_t, link); + array->value.array->array = NULL; self->p++; /* toss of the leading [ */ for (;;) { apr_json_value_t *element; - json_link_t *new_node; if (self->p == self->e) { - status = APR_EOF; - goto out; + return APR_EOF; } if (*self->p == ']') { @@ -388,49 +387,41 @@ static apr_status_t apr_json_decode_array(apr_json_scanner_t * self, } if (APR_SUCCESS != (status = apr_json_decode_value(self, &element))) { - goto out; + return status; } - new_node = apr_pcalloc(link_pool, sizeof(json_link_t)); - new_node->value = element; - if (tail) { - tail->next = new_node; - } - else { - head = new_node; + if (APR_SUCCESS + != (status = apr_json_array_add(array, element, self->pool))) { + return status; } - tail = new_node; + count++; if (self->p == self->e) { - status = APR_EOF; - goto out; + return APR_EOF; } if (*self->p == ',') { self->p++; } else if (*self->p != ']') { - status = APR_BADCH; - goto out; + return APR_BADCH; } } { - json_link_t *node; - apr_array_header_t *array = apr_array_make(self->pool, count, sizeof(apr_json_value_t *)); - for (node = head; node; node = node->next) { - *((apr_json_value_t **) (apr_array_push(array))) = node->value; + apr_json_value_t *element = apr_json_array_first(array); + array->value.array->array = apr_array_make(self->pool, count, + sizeof(apr_json_value_t *)); + while (element) { + *((apr_json_value_t **) (apr_array_push(array->value.array->array))) = + element; + element = apr_json_array_next(array, element); } - *retval = array; } self->level++; -out: - if (link_pool) { - apr_pool_destroy(link_pool); - } return status; } @@ -740,7 +731,7 @@ static apr_status_t apr_json_decode_value(apr_json_scanner_t * self, apr_json_va break; case '[': value.type = APR_JSON_ARRAY; - status = apr_json_decode_array(self, &value.value.array); + status = apr_json_decode_array(self, &value); break; case '{': value.type = APR_JSON_OBJECT; diff --git a/json/apr_json_encode.c b/json/apr_json_encode.c index 8b4830c9d..13b1e67a8 100644 --- a/json/apr_json_encode.c +++ b/json/apr_json_encode.c @@ -164,30 +164,35 @@ static apr_status_t apr_json_encode_string(apr_json_serializer_t * self, } -static apr_status_t apr_json_encode_array(apr_json_serializer_t * self, apr_array_header_t * array) +static apr_status_t apr_json_encode_array(apr_json_serializer_t * self, + const apr_json_value_t * array) { apr_status_t status; - apr_size_t i; + apr_json_value_t *val; + apr_size_t count = 0; status = apr_brigade_putc(self->brigade, self->flush, self->ctx, '['); if (APR_SUCCESS != status) { return status; } - for (i = 0; i < array->nelts; i++) { + val = apr_json_array_first(array); + while (val) { - if (i > 0) { + if (count > 0) { status = apr_brigade_putc(self->brigade, self->flush, self->ctx, ','); if (APR_SUCCESS != status) { return status; } } - status = apr_json_encode_value(self, ((apr_json_value_t **) array->elts)[i]); + status = apr_json_encode_value(self, val); if (APR_SUCCESS != status) { return status; } + val = apr_json_array_next(array, val); + count++; } return apr_brigade_putc(self->brigade, self->flush, self->ctx, ']'); @@ -269,7 +274,7 @@ static apr_status_t apr_json_encode_value(apr_json_serializer_t * self, const ap status = apr_json_encode_object(self, value->value.object); break; case APR_JSON_ARRAY: - status = apr_json_encode_array(self, value->value.array); + status = apr_json_encode_array(self, value); break; default: return APR_EINVAL; diff --git a/test/testjson.c b/test/testjson.c index 0777eb14a..f1c3e9f3c 100644 --- a/test/testjson.c +++ b/test/testjson.c @@ -151,15 +151,15 @@ static void test_json_string(abts_case * tc, void *data) static void test_json_overlay(abts_case * tc, void *data) { - const char *o = "{\"o1\":\"foo\",\"common\":\"bar\",\"o2\":\"baz\"}"; - const char *b = "{\"b1\":\"foo\",\"common\":\"bar\",\"b2\":\"baz\"}"; + const char *o = "{\"o1\":\"foo\",\"common\":\"bar\",\"o2\":\"baz\"}"; + const char *b = "{\"b1\":\"foo\",\"common\":\"bar\",\"b2\":\"baz\"}"; - apr_json_value_t *res; - apr_json_value_t *base; - apr_json_value_t *overlay; + apr_json_value_t *res; + apr_json_value_t *base; + apr_json_value_t *overlay; - apr_off_t offset; - apr_status_t status; + apr_off_t offset; + apr_status_t status; status = apr_json_decode(&base, b, APR_JSON_VALUE_STRING, &offset, APR_JSON_FLAGS_WHITESPACE, 10, p); @@ -178,6 +178,58 @@ static void test_json_overlay(abts_case * tc, void *data) } +static void test_json_object_iterate(abts_case * tc, void *data) +{ + const char *o = "{\"o1\":\"foo\",\"o2\":\"bar\"}"; + + apr_json_value_t *val; + apr_json_kv_t *kv; + + apr_off_t offset; + apr_status_t status; + + status = apr_json_decode(&val, o, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + kv = apr_json_object_first(val); + ABTS_PTR_NOTNULL(tc, kv); + + kv = apr_json_object_next(val, kv); + ABTS_PTR_NOTNULL(tc, kv); + + kv = apr_json_object_next(val, kv); + ABTS_ASSERT(tc, "object next should return NULL", + (kv == NULL)); + +} + +static void test_json_array_iterate(abts_case * tc, void *data) +{ + const char *o = "[\"a1\",\"a2\"]"; + + apr_json_value_t *arr; + apr_json_value_t *val; + + apr_off_t offset; + apr_status_t status; + + status = apr_json_decode(&arr, o, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + val = apr_json_array_first(arr); + ABTS_PTR_NOTNULL(tc, val); + + val = apr_json_array_next(arr, val); + ABTS_PTR_NOTNULL(tc, val); + + val = apr_json_array_next(arr, val); + ABTS_ASSERT(tc, "array next should return NULL", + (val == NULL)); + +} + abts_suite *testjson(abts_suite * suite) { suite = ADD_SUITE(suite); @@ -187,6 +239,8 @@ abts_suite *testjson(abts_suite * suite) abts_run_test(suite, test_json_eof, NULL); abts_run_test(suite, test_json_string, NULL); abts_run_test(suite, test_json_overlay, NULL); + abts_run_test(suite, test_json_object_iterate, NULL); + abts_run_test(suite, test_json_array_iterate, NULL); return suite; } |