summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/apr_json.h29
-rw-r--r--json/apr_json.c56
-rw-r--r--test/testjson.c30
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;
}