diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 87eee31..e6a8f9d 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -343,4 +343,20 @@ static cJSON *cJSONUtils_SortList(cJSON *list) void cJSONUtils_SortObject(cJSON *object) {object->child=cJSONUtils_SortList(object->child);} +cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch) { + if (!patch || patch->type != cJSON_Object) {cJSON_Delete(target);return cJSON_Duplicate(patch,1);} + if (!target || target->type != cJSON_Object) {cJSON_Delete(target);target=cJSON_CreateObject();} + patch=patch->child; + while (patch) + { + if (patch->type == cJSON_NULL) cJSON_DeleteItemFromObject(target,patch->string); + else + { + cJSON *replaceme=cJSON_DetachItemFromObject(target,patch->string); + cJSON_AddItemToObject(target,patch->string,cJSONUtils_MergePatch(replaceme,patch)); + } + patch=patch->next; + } + return target; +} diff --git a/cJSON_Utils.h b/cJSON_Utils.h index 0e9dfb4..c845f8e 100644 --- a/cJSON_Utils.h +++ b/cJSON_Utils.h @@ -21,6 +21,9 @@ int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches); /* Returns 0 for succ // Code not added to library since this strategy is a LOT slower. */ +/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ +cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); /* target will be modified by patch. return value is new ptr for target. */ + char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target); /* Given a root object and a target object, construct a pointer from one to the other. */ void cJSONUtils_SortObject(cJSON *object); /* Sorts the members of the object into alphabetical order. */ diff --git a/test_utils.c b/test_utils.c index 79664a5..929f570 100644 --- a/test_utils.c +++ b/test_utils.c @@ -40,6 +40,25 @@ int main() {"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": 10}]",""}, {"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": \"10\"}]",""}, {"{ \"foo\": [\"bar\"] }","[ { \"op\": \"add\", \"path\": \"/foo/-\", \"value\": [\"abc\", \"def\"] }]","{\"foo\": [\"bar\", [\"abc\", \"def\"]] }"}}; + + /* JSON Apply Merge tests: */ + const char *merges[15][3]={ + {"{\"a\":\"b\"}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"}, + {"{\"a\":\"b\"}", "{\"b\":\"c\"}", "{\"a\":\"b\",\"b\":\"c\"}"}, + {"{\"a\":\"b\"}", "{\"a\":null}", "{}"}, + {"{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":null}", "{\"b\":\"c\"}"}, + {"{\"a\":[\"b\"]}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"}, + {"{\"a\":\"c\"}", "{\"a\":[\"b\"]}", "{\"a\":[\"b\"]}"}, + {"{\"a\":{\"b\":\"c\"}}", "{\"a\":{\"b\":\"d\",\"c\":null}}", "{\"a\":{\"b\":\"d\"}}"}, + {"{\"a\":[{\"b\":\"c\"}]}", "{\"a\":[1]}", "{\"a\":[1]}"}, + {"[\"a\",\"b\"]", "[\"c\",\"d\"]", "[\"c\",\"d\"]"}, + {"{\"a\":\"b\"}", "[\"c\"]", "[\"c\"]"}, + {"{\"a\":\"foo\"}", "null", "null"}, + {"{\"a\":\"foo\"}", "\"bar\"", "\"bar\""}, + {"{\"e\":null}", "{\"a\":1}", "{\"e\":null,\"a\":1}"}, + {"[1,2]", "{\"a\":\"b\",\"c\":null}", "{\"a\":\"b\"}"}, + {"{}","{\"a\":{\"bb\":{\"ccc\":null}}}", "{\"a\":{\"bb\":{}}}"}}; + /* Misc tests */ int numbers[10]={0,1,2,3,4,5,6,7,8,9}; @@ -113,4 +132,21 @@ int main() after=cJSON_PrintUnformatted(sortme); printf("Before: [%s]\nAfter: [%s]\n\n",before,after); free(before);free(after);cJSON_Delete(sortme); + + /* Merge tests: */ + printf("JSON Merge Patch tests\n"); + for (i=0;i<15;i++) + { + cJSON *object=cJSON_Parse(merges[i][0]); + cJSON *patch=cJSON_Parse(merges[i][1]); + char *before=cJSON_PrintUnformatted(object); + char *patchtext=cJSON_PrintUnformatted(patch); + printf("Before: [%s] -> [%s] = ",before,patchtext); + object=cJSONUtils_MergePatch(object,patch); + char *after=cJSON_PrintUnformatted(object); + printf("[%s] vs [%s] (%s)\n",after,merges[i][2],strcmp(after,merges[i][2])?"FAIL":"OK"); + + free(before);free(patchtext);free(after);cJSON_Delete(object);cJSON_Delete(patch); + } + }