diff options
-rw-r--r-- | include/apr_json.h | 29 | ||||
-rw-r--r-- | json/apr_json.c | 56 | ||||
-rw-r--r-- | test/testjson.c | 30 |
3 files changed, 115 insertions, 0 deletions
diff --git a/include/apr_json.h b/include/apr_json.h index ea87a2793..3fc127e23 100644 --- a/include/apr_json.h +++ b/include/apr_json.h @@ -78,6 +78,11 @@ extern "C" { #define APR_JSON_FLAGS_WHITESPACE 1 /** + * Flag indicating strict overlay. + */ +#define APR_JSON_FLAGS_STRICT 2 + +/** * A structure to hold a JSON object. */ typedef struct apr_json_object_t apr_json_object_t; @@ -310,6 +315,30 @@ APR_DECLARE(apr_status_t) apr_json_encode(apr_bucket_brigade * brigade, apr_brigade_flush flush, void *ctx, const apr_json_value_t * json, int flags, apr_pool_t * pool) __attribute__((nonnull(1, 4, 6))); +/** + * Overlay one JSON object over a second JSON object. + * + * If the values are objects, a new object will be returned containing + * all keys from the overlay superimposed on the base. + * + * Keys that appear in the overlay will replace keys in the base, unless + * APR_JSON_FLAGS_STRICT is specified, in which case NULL will be returned. + * + * If the base is not an object, overlay will be returned. + * @param p pool to use + * @param overlay the JSON object to overlay on top of base. If NULL, the + * base will be returned. + * @param base the base JSON object. If NULL, the overlay will be returned. + * @param flags set to APR_JSON_FLAGS_STRICT to fail if object keys are not + * unique, or APR_JSON_FLAGS_NONE to replace keys in base with overlay. + * @return A new object containing the result. If APR_JSON_FLAGS_STRICT was + * specified and a key was present in overlay that was also present in base, + * NULL will be returned. + */ +APR_DECLARE(apr_json_value_t *) apr_json_overlay(apr_pool_t *p, + apr_json_value_t *overlay, apr_json_value_t *base, + int flags) __attribute__((nonnull(1)));; + #ifdef __cplusplus } #endif diff --git a/json/apr_json.c b/json/apr_json.c index b035c435f..4d671d1cd 100644 --- a/json/apr_json.c +++ b/json/apr_json.c @@ -167,3 +167,59 @@ apr_json_kv_t *apr_json_object_get(apr_json_value_t *object, const char *key) return apr_hash_get(object->value.object->hash, key, APR_HASH_KEY_STRING); } + +apr_json_value_t *apr_json_overlay(apr_pool_t *p, + apr_json_value_t *overlay, apr_json_value_t *base, + int flags) +{ + apr_json_value_t *res; + apr_json_kv_t *kv; + int oc, bc; + + if (!base || base->type != APR_JSON_OBJECT) { + return overlay; + } + if (!overlay) { + return base; + } + if (overlay->type != APR_JSON_OBJECT) { + return overlay; + } + + oc = apr_hash_count(overlay->value.object->hash); + if (!oc) { + return base; + } + bc = apr_hash_count(base->value.object->hash); + if (!bc) { + return overlay; + } + + res = apr_json_object_create(p); + + for (kv = APR_RING_FIRST(&(base->value.object)->list); + kv != APR_RING_SENTINEL(&(base->value.object)->list, apr_json_kv_t, link); + kv = APR_RING_NEXT((kv), link)) { + + if (!apr_hash_get(overlay->value.object->hash, kv->k->value.string.p, + kv->k->value.string.len)) { + + apr_json_object_set(res, kv->k, kv->v, p); + + } + else if (APR_JSON_FLAGS_STRICT & flags) { + return NULL; + } + + } + + for (kv = APR_RING_FIRST(&(overlay->value.object)->list); + kv != APR_RING_SENTINEL(&(overlay->value.object)->list, apr_json_kv_t, link); + kv = APR_RING_NEXT((kv), link)) { + + apr_json_object_set(res, kv->k, kv->v, p); + + } + + return res; +} diff --git a/test/testjson.c b/test/testjson.c index 16bb5dce3..0777eb14a 100644 --- a/test/testjson.c +++ b/test/testjson.c @@ -149,6 +149,35 @@ static void test_json_string(abts_case * tc, void *data) (memcmp(expected, json->value.string.p, json->value.string.len) == 0)); } +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\"}"; + + apr_json_value_t *res; + apr_json_value_t *base; + apr_json_value_t *overlay; + + 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); + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + status = apr_json_decode(&overlay, o, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + res = apr_json_overlay(p, overlay, base, APR_JSON_FLAGS_NONE); + ABTS_INT_EQUAL(tc, 5, apr_hash_count(res->value.object->hash)); + + res = apr_json_overlay(p, overlay, base, APR_JSON_FLAGS_STRICT); + ABTS_ASSERT(tc, "overlay strict should return NULL", + (res == NULL)); + +} + abts_suite *testjson(abts_suite * suite) { suite = ADD_SUITE(suite); @@ -157,6 +186,7 @@ abts_suite *testjson(abts_suite * suite) abts_run_test(suite, test_json_level, NULL); abts_run_test(suite, test_json_eof, NULL); abts_run_test(suite, test_json_string, NULL); + abts_run_test(suite, test_json_overlay, NULL); return suite; } |