summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorminfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68>2018-08-31 09:24:04 +0000
committerminfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68>2018-08-31 09:24:04 +0000
commit88b8fa42f150c4357efd8431ce5cdabac85f613b (patch)
tree6dab73d3d4304233d9073577306add95d3888959
parent8029762a0ac265a3bd9a9bd47158756cd14bba1d (diff)
downloadlibapr-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.h70
-rw-r--r--json/apr_json.c81
-rw-r--r--json/apr_json_decode.c57
-rw-r--r--json/apr_json_encode.c17
-rw-r--r--test/testjson.c68
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;
}