CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch) { if (!cJSON_IsObject(patch)) { /* scalar value, array or NULL, just duplicate */ cJSON_Delete(target); return cJSON_Duplicate(patch, 1); } if (!cJSON_IsObject(target)) { cJSON_Delete(target); target = cJSON_CreateObject(); } patch = patch->child; while (patch) { if (cJSON_IsNull(patch)) { /* NULL is the indicator to remove a value, see RFC7396 */ 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; }
static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive) { cJSON *patch_child = NULL; if (!cJSON_IsObject(patch)) { /* scalar value, array or NULL, just duplicate */ cJSON_Delete(target); return cJSON_Duplicate(patch, 1); } if (!cJSON_IsObject(target)) { cJSON_Delete(target); target = cJSON_CreateObject(); } patch_child = patch->child; while (patch_child != NULL) { if (cJSON_IsNull(patch_child)) { /* NULL is the indicator to remove a value, see RFC7396 */ if (case_sensitive) { cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string); } else { cJSON_DeleteItemFromObject(target, patch_child->string); } } else { cJSON *replace_me = NULL; cJSON *replacement = NULL; if (case_sensitive) { replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string); } else { replace_me = cJSON_DetachItemFromObject(target, patch_child->string); } replacement = merge_patch(replace_me, patch_child, case_sensitive); if (replacement == NULL) { return NULL; } cJSON_AddItemToObject(target, patch_child->string, replacement); } patch_child = patch_child->next; } return target; }
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to) { cJSON *patch = NULL; if (!to) { /* patch to delete everything */ return cJSON_CreateNull(); } if (!cJSON_IsObject(to) || !cJSON_IsObject(from)) { return cJSON_Duplicate(to, 1); } cJSONUtils_SortObject(from); cJSONUtils_SortObject(to); from = from->child; to = to->child; patch = cJSON_CreateObject(); while (from || to) { int compare = from ? (to ? strcmp(from->string, to->string) : -1) : 1; if (compare < 0) { /* from has a value that to doesn't have -> remove */ cJSON_AddItemToObject(patch, from->string, cJSON_CreateNull()); from = from->next; } else if (compare > 0) { /* to has a value that from doesn't have -> add to patch */ cJSON_AddItemToObject(patch, to->string, cJSON_Duplicate(to, 1)); to = to->next; } else { /* object key exists in both objects */ if (cJSONUtils_Compare(from, to)) { /* not identical --> generate a patch */ cJSON_AddItemToObject(patch, to->string, cJSONUtils_GenerateMergePatch(from, to)); } /* next key in the object */ from = from->next; to = to->next; } } if (!patch->child) { cJSON_Delete(patch); return NULL; } return patch; }
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target) { size_t child_index = 0; cJSON *current_child = 0; if (object == target) { /* found */ return (char*)cJSONUtils_strdup((const unsigned char*)""); } /* recursively search all children of the object or array */ for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++) { unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target); /* found the target? */ if (target_pointer != NULL) { if (cJSON_IsArray(object)) { /* reserve enough memory for a 64 bit integer + '/' and '\0' */ unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/")); /* check if conversion to unsigned long is valid * This should be eliminated at compile time by dead code elimination * if size_t is an alias of unsigned long, or if it is bigger */ if (child_index > ULONG_MAX) { cJSON_free(target_pointer); return NULL; } sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */ cJSON_free(target_pointer); return (char*)full_pointer; } if (cJSON_IsObject(object)) { unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2); full_pointer[0] = '/'; encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string); strcat((char*)full_pointer, (char*)target_pointer); cJSON_free(target_pointer); return (char*)full_pointer; } /* reached leaf of the tree, found nothing */ cJSON_free(target_pointer); return NULL; } } /* not found */ return NULL; }
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target) { size_t c = 0; cJSON *obj = 0; if (object == target) { /* found */ return (char*)cJSONUtils_strdup((const unsigned char*)""); } /* recursively search all children of the object */ for (obj = object->child; obj; (void)(obj = obj->next), c++) { unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target); if (found) { if (cJSON_IsArray(object)) { /* reserve enough memory for a 64 bit integer + '/' and '\0' */ unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + 23); /* check if conversion to unsigned long is valid * This should be eliminated at compile time by dead code elimination * if size_t is an alias of unsigned long, or if it is bigger */ if (c > ULONG_MAX) { free(found); return NULL; } sprintf((char*)ret, "/%lu%s", (unsigned long)c, found); /* /<array_index><path> */ free(found); return (char*)ret; } else if (cJSON_IsObject(object)) { unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2); *ret = '/'; cJSONUtils_PointerEncodedstrcpy(ret + 1, (unsigned char*)obj->string); strcat((char*)ret, (char*)found); free(found); return (char*)ret; } /* reached leaf of the tree, found nothing */ free(found); return NULL; } } /* not found */ return NULL; }
/* detach an item at the given path */ static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) { unsigned char *parent_pointer = NULL; unsigned char *child_pointer = NULL; cJSON *parent = NULL; cJSON *detached_item = NULL; /* copy path and split it in parent and child */ parent_pointer = cJSONUtils_strdup(path); if (parent_pointer == NULL) { goto cleanup; } child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */ if (child_pointer == NULL) { goto cleanup; } /* split strings */ child_pointer[0] = '\0'; child_pointer++; parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); decode_pointer_inplace(child_pointer); if (cJSON_IsArray(parent)) { size_t index = 0; if (!decode_array_index_from_pointer(child_pointer, &index)) { goto cleanup; } detached_item = detach_item_from_array(parent, index); } else if (cJSON_IsObject(parent)) { detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer); } else { /* Couldn't find object to remove child from. */ goto cleanup; } cleanup: if (parent_pointer != NULL) { cJSON_free(parent_pointer); } return detached_item; }
static void treasure_create_json_object_for_type_A_test(void) { struct rnd *rnd = rnd_alloc_fake_min(); struct treasure treasure; treasure_initialize(&treasure); struct treasure_type *treasure_type = treasure_type_by_letter('A'); treasure_type_generate(treasure_type, rnd, &treasure); struct cJSON *json_object = treasure_create_json_object(&treasure); assert(cJSON_IsObject(json_object)); char *json_string = cJSON_PrintUnformatted(json_object); char const *expected = "{" "\"struct\":\"treasure\"," "\"rev\":0," "\"type\":\"A\"," "\"coins\":{" "\"struct\":\"coins\"," "\"rev\":0," "\"pp\":100," "\"gp\":1000," "\"ep\":1000," "\"sp\":1000," "\"cp\":1000" "}," "\"gems\":[" "{\"struct\":\"gem\",\"rev\":0,\"size\":\"very small\",\"type\":\"ornamental\",\"kind\":\"azurite\",\"colors\":\"mottled deep blue\",\"value_percent_modifier\":0,\"value_rank_modifier\":7}," "{\"struct\":\"gem\",\"rev\":0,\"size\":\"very small\",\"type\":\"ornamental\",\"kind\":\"azurite\",\"colors\":\"mottled deep blue\",\"value_percent_modifier\":0,\"value_rank_modifier\":7}," "{\"struct\":\"gem\",\"rev\":0,\"size\":\"very small\",\"type\":\"ornamental\",\"kind\":\"azurite\",\"colors\":\"mottled deep blue\",\"value_percent_modifier\":0,\"value_rank_modifier\":7}," "{\"struct\":\"gem\",\"rev\":0,\"size\":\"very small\",\"type\":\"ornamental\",\"kind\":\"azurite\",\"colors\":\"mottled deep blue\",\"value_percent_modifier\":0,\"value_rank_modifier\":7}" "]," "\"jewelry\":[" "{\"struct\":\"jewelry\",\"rev\":0,\"has_gems\":false,\"form\":\"anklet\",\"material\":\"ivory\",\"workmanship_bonus\":12,\"exceptional_stone_bonus\":0,\"value_in_cp\":400000}," "{\"struct\":\"jewelry\",\"rev\":0,\"has_gems\":false,\"form\":\"anklet\",\"material\":\"ivory\",\"workmanship_bonus\":12,\"exceptional_stone_bonus\":0,\"value_in_cp\":400000}," "{\"struct\":\"jewelry\",\"rev\":0,\"has_gems\":false,\"form\":\"anklet\",\"material\":\"ivory\",\"workmanship_bonus\":12,\"exceptional_stone_bonus\":0,\"value_in_cp\":400000}" "]," "\"maps\":[" "{\"struct\":\"treasure_map\",\"rev\":0,\"is_false\":true,\"treasure\":{\"struct\":\"treasure\",\"rev\":0,\"type\":null,\"coins\":{\"struct\":\"coins\",\"rev\":0,\"pp\":0,\"gp\":0,\"ep\":0,\"sp\":0,\"cp\":0},\"gems\":[],\"jewelry\":[],\"maps\":[],\"magic_items\":[]},\"true_description\":\"false map to treasure of (no treasure) in nearby labyrinth to the north\"}," "{\"struct\":\"treasure_map\",\"rev\":0,\"is_false\":true,\"treasure\":{\"struct\":\"treasure\",\"rev\":0,\"type\":null,\"coins\":{\"struct\":\"coins\",\"rev\":0,\"pp\":0,\"gp\":0,\"ep\":0,\"sp\":0,\"cp\":0},\"gems\":[],\"jewelry\":[],\"maps\":[],\"magic_items\":[]},\"true_description\":\"false map to treasure of (no treasure) in nearby labyrinth to the north\"}," "{\"struct\":\"treasure_map\",\"rev\":0,\"is_false\":true,\"treasure\":{\"struct\":\"treasure\",\"rev\":0,\"type\":null,\"coins\":{\"struct\":\"coins\",\"rev\":0,\"pp\":0,\"gp\":0,\"ep\":0,\"sp\":0,\"cp\":0},\"gems\":[],\"jewelry\":[],\"maps\":[],\"magic_items\":[]},\"true_description\":\"false map to treasure of (no treasure) in nearby labyrinth to the north\"}" "]," "\"magic_items\":[]" "}"; assert(str_eq(expected, json_string)); free(json_string); cJSON_Delete(json_object); treasure_finalize(&treasure); rnd_free(rnd); }
static int GLua_JSON_IsObject( lua_State *L ) { // Check and make sure we're using a valid index if( !FJSON_ValidateNode( L, 1 ) ) { lua_pushnil(L); return 1; } cJSON *node = FJSON_RetrieveNode( L, 1 ); lua_pushboolean( L, cJSON_IsObject( node ) ); return 1; }
void treasure_map_initialize_from_json_object(struct treasure_map *treasure_map, struct cJSON *json_object) { treasure_map_initialize(treasure_map); if ( ! cJSON_IsObject(json_object)) return; if ( ! json_object_has_struct_member(json_object, "treasure_map")) return; treasure_map->is_false = json_object_get_bool_value(json_object, "is_false", false); struct cJSON *treasure = cJSON_GetObjectItemCaseSensitive(json_object, "treasure"); treasure_initialize_from_json_object(&treasure_map->treasure, treasure); treasure_map->true_description = json_object_alloc_string_value(json_object, "true_description", NULL); }
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer) { /* follow path of the pointer */ while ((*pointer++ == '/') && object) { if (cJSON_IsArray(object)) { size_t which = 0; /* parse array index */ while ((*pointer >= '0') && (*pointer <= '9')) { which = (10 * which) + (size_t)(*pointer++ - '0'); } if (*pointer && (*pointer != '/')) { /* not end of string or new path token */ return NULL; } if (which > INT_MAX) { return NULL; } object = cJSON_GetArrayItem(object, (int)which); } else if (cJSON_IsObject(object)) { object = object->child; /* GetObjectItem. */ while (object && cJSONUtils_Pstrcasecmp((unsigned char*)object->string, (const unsigned char*)pointer)) { object = object->next; } /* skip to the next path token or end of string */ while (*pointer && (*pointer != '/')) { pointer++; } } else { return NULL; } } return object; }
static cJSON *cJSONUtils_PatchDetach(cJSON *object, const unsigned char *path) { unsigned char *parentptr = NULL; unsigned char *childptr = NULL; cJSON *parent = NULL; cJSON *ret = NULL; /* copy path and split it in parent and child */ parentptr = cJSONUtils_strdup(path); if (parentptr == NULL) { return NULL; } childptr = (unsigned char*)strrchr((char*)parentptr, '/'); /* last '/' */ if (childptr == NULL) { free(parentptr); return NULL; } /* split strings */ *childptr++ = '\0'; parent = cJSONUtils_GetPointer(object, (char*)parentptr); cJSONUtils_InplaceDecodePointerString(childptr); if (!parent) { /* Couldn't find object to remove child from. */ ret = NULL; } else if (cJSON_IsArray(parent)) { ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr)); } else if (cJSON_IsObject(parent)) { ret = cJSON_DetachItemFromObject(parent, (char*)childptr); } free(parentptr); /* return the detachted item */ return ret; }
static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive) { cJSON *current_element = object; /* follow path of the pointer */ while ((pointer[0] == '/') && (current_element != NULL)) { pointer++; if (cJSON_IsArray(current_element)) { size_t index = 0; if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index)) { return NULL; } current_element = get_array_item(current_element, index); } else if (cJSON_IsObject(current_element)) { current_element = current_element->child; /* GetObjectItem. */ while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive)) { current_element = current_element->next; } /* skip to the next path token or end of string */ while ((pointer[0] != '\0') && (pointer[0] != '/')) { pointer++; } } else { return NULL; } } return current_element; }
static void BG_ParseDamage ( weaponFireModeStats_t *fireModeStats, cJSON *damageNode, qboolean secondary ) { if ( !damageNode ) { return; } if ( !secondary && cJSON_IsNumber (damageNode) ) { fireModeStats->baseDamage = (short)cJSON_ToIntegerOpt (damageNode, 0); } else if ( cJSON_IsObject (damageNode) ) { damageSettings_t darea; cJSON *node = NULL; qhandle_t areaHandle = 0; memset (&darea, 0, sizeof (darea)); node = cJSON_GetObjectItem (damageNode, "damageradius"); if ( node ) { cJSON *child = NULL; const char *s = NULL; darea.radial = qtrue; child = cJSON_GetObjectItem (node, "start"); darea.radiusParams.startRadius = (float)cJSON_ToNumber (child); child = cJSON_GetObjectItem (node, "end"); darea.radiusParams.endRadius = (float)cJSON_ToNumber (child); child = cJSON_GetObjectItem (node, "parm"); darea.radiusParams.generic1 = cJSON_ToInteger (child); child = cJSON_GetObjectItem (node, "falloff"); s = cJSON_ToStringOpt (child, "constant"); if ( Q_stricmp (s, "constant") == 0 ) darea.radiusParams.damageFunc = DF_CONSTANT; else if ( Q_stricmp (s, "linear") == 0 ) darea.radiusParams.damageFunc = DF_LINEAR; else if ( Q_stricmp (s, "gaussian") == 0 ) darea.radiusParams.damageFunc = DF_GAUSSIAN; else { darea.radiusParams.damageFunc = DF_CONSTANT; Com_Printf ("Unknown damage falloff type used: %s. Defaulting to linear falloff.\n", s); } child = cJSON_GetObjectItem (node, "function"); s = cJSON_ToStringOpt (child, "constant"); if ( Q_stricmp (s, "linear") == 0 ) darea.radiusParams.radiusFunc = RF_LINEAR; else if ( Q_stricmp (s, "nonlinear") == 0 ) darea.radiusParams.radiusFunc = RF_NONLINEAR; else if ( Q_stricmp (s, "clamp") == 0 ) darea.radiusParams.radiusFunc = RF_CLAMP; else if ( Q_stricmp (s, "wave") == 0 ) darea.radiusParams.radiusFunc = RF_WAVE; else if ( Q_stricmp (s, "constant") == 0 ) darea.radiusParams.radiusFunc = RF_CONSTANT; else { darea.radiusParams.radiusFunc = RF_CONSTANT; Com_Printf ("Unknown radius function used: %s; Defaulting to constant radius.\n", s); } } node = cJSON_GetObjectItem (damageNode, "duration"); darea.lifetime = cJSON_ToIntegerOpt (node, 0); node = cJSON_GetObjectItem (damageNode, "delay"); darea.delay = cJSON_ToIntegerOpt (node, 0); node = cJSON_GetObjectItem (damageNode, "damage"); darea.damage = cJSON_ToIntegerOpt (node, 0); fireModeStats->baseDamage = darea.damage; node = cJSON_GetObjectItem (damageNode, "damagedelay"); darea.damageDelay = cJSON_ToIntegerOpt (node, 0); node = cJSON_GetObjectItem (damageNode, "penetration"); switch ( cJSON_ToIntegerOpt (node, 0) ) { default: case 0: darea.penetrationType = PT_NONE; break; case 1: darea.penetrationType = PT_SHIELD; break; case 2: darea.penetrationType = PT_SHIELD_ARMOR; break; case 3: darea.penetrationType = PT_SHIELD_ARMOR_BUILDING; break; } node = cJSON_GetObjectItem (damageNode, "damagetype"); if ( node ) { int i = 0; const char *types[NUM_DAMAGE_TYPES]; int numTypes = cJSON_ReadStringArray (node, NUM_DAMAGE_TYPES, types); for ( i = 0; i < numTypes; i++ ) { if ( Q_stricmp (types[i], "annihilate") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_ANNIHILATION); darea.damageType = dType; } else if ( Q_stricmp (types[i], "concussion") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_CONCUSSION); darea.damageType = dType; } else if ( Q_stricmp (types[i], "cut") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_CUT); darea.damageType = dType; } else if ( Q_stricmp (types[i], "disintegrate") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_DISINTEGRATE); darea.damageType = dType; } else if ( Q_stricmp (types[i], "electric") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_ELECTRIC); darea.damageType = dType; } else if ( Q_stricmp (types[i], "explosion") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_EXPLOSION); darea.damageType = dType; } else if ( Q_stricmp (types[i], "fire") == 0 ) { int dType = (int)darea.damageType; dType |= ( 1 << DT_FIRE ); darea.damageType = dType; } else if ( Q_stricmp (types[i], "freeze") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_FREEZE); darea.damageType = dType; } else if ( Q_stricmp (types[i], "implosion") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_IMPLOSION); darea.damageType = dType; } else if ( Q_stricmp (types[i], "stun") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_STUN); darea.damageType = dType; } else if ( Q_stricmp (types[i], "carbonite") == 0 ) { int dType = (int)darea.damageType; dType |= (1 << DT_CARBONITE); darea.damageType = dType; } else { Com_Printf ("Unknown damage type used: %s.\n", types[i]); } } } areaHandle = JKG_RegisterDamageSettings (&darea); if ( !secondary ) { fireModeStats->damageTypeHandle = areaHandle; } else { fireModeStats->secondaryDmgHandle = areaHandle; } } }
static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) { cJSON *path = NULL; cJSON *value = NULL; cJSON *parent = NULL; enum patch_operation opcode = INVALID; unsigned char *parent_pointer = NULL; unsigned char *child_pointer = NULL; int status = 0; path = get_object_item(patch, "path", case_sensitive); if (!cJSON_IsString(path)) { /* malformed patch. */ status = 2; goto cleanup; } opcode = decode_patch_operation(patch, case_sensitive); if (opcode == INVALID) { status = 3; goto cleanup; } else if (opcode == TEST) { /* compare value: {...} with the given path */ status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive); goto cleanup; } /* special case for replacing the root */ if (path->valuestring[0] == '\0') { if (opcode == REMOVE) { static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL}; overwrite_item(object, invalid); status = 0; goto cleanup; } if ((opcode == REPLACE) || (opcode == ADD)) { value = get_object_item(patch, "value", case_sensitive); if (value == NULL) { /* missing "value" for add/replace. */ status = 7; goto cleanup; } value = cJSON_Duplicate(value, 1); if (value == NULL) { /* out of memory for add/replace. */ status = 8; goto cleanup; } overwrite_item(object, *value); /* delete the duplicated value */ cJSON_free(value); value = NULL; /* the string "value" isn't needed */ if (object->string != NULL) { cJSON_free(object->string); object->string = NULL; } status = 0; goto cleanup; } } if ((opcode == REMOVE) || (opcode == REPLACE)) { /* Get rid of old. */ cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive); if (old_item == NULL) { status = 13; goto cleanup; } cJSON_Delete(old_item); if (opcode == REMOVE) { /* For Remove, this job is done. */ status = 0; goto cleanup; } } /* Copy/Move uses "from". */ if ((opcode == MOVE) || (opcode == COPY)) { cJSON *from = get_object_item(patch, "from", case_sensitive); if (from == NULL) { /* missing "from" for copy/move. */ status = 4; goto cleanup; } if (opcode == MOVE) { value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive); } if (opcode == COPY) { value = get_item_from_pointer(object, from->valuestring, case_sensitive); } if (value == NULL) { /* missing "from" for copy/move. */ status = 5; goto cleanup; } if (opcode == COPY) { value = cJSON_Duplicate(value, 1); } if (value == NULL) { /* out of memory for copy/move. */ status = 6; goto cleanup; } } else /* Add/Replace uses "value". */ { value = get_object_item(patch, "value", case_sensitive); if (value == NULL) { /* missing "value" for add/replace. */ status = 7; goto cleanup; } value = cJSON_Duplicate(value, 1); if (value == NULL) { /* out of memory for add/replace. */ status = 8; goto cleanup; } } /* Now, just add "value" to "path". */ /* split pointer in parent and child */ parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); if (child_pointer != NULL) { child_pointer[0] = '\0'; child_pointer++; } parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); decode_pointer_inplace(child_pointer); /* add, remove, replace, move, copy, test. */ if ((parent == NULL) || (child_pointer == NULL)) { /* Couldn't find object to add to. */ status = 9; goto cleanup; } else if (cJSON_IsArray(parent)) { if (strcmp((char*)child_pointer, "-") == 0) { cJSON_AddItemToArray(parent, value); value = NULL; } else { size_t index = 0; if (!decode_array_index_from_pointer(child_pointer, &index)) { status = 11; goto cleanup; } if (!insert_item_in_array(parent, index, value)) { status = 10; goto cleanup; } value = NULL; } } else if (cJSON_IsObject(parent)) { cJSON_DeleteItemFromObject(parent, (char*)child_pointer); cJSON_AddItemToObject(parent, (char*)child_pointer, value); value = NULL; } cleanup: if (value != NULL) { cJSON_Delete(value); } if (parent_pointer != NULL) { cJSON_free(parent_pointer); } return status; }
static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch) { cJSON *op = NULL; cJSON *path = NULL; cJSON *value = NULL; cJSON *parent = NULL; int opcode = 0; unsigned char *parentptr = NULL; unsigned char *childptr = NULL; op = cJSON_GetObjectItem(patch, "op"); path = cJSON_GetObjectItem(patch, "path"); if (!op || !path) { /* malformed patch. */ return 2; } /* decode operation */ if (!strcmp(op->valuestring, "add")) { opcode = 0; } else if (!strcmp(op->valuestring, "remove")) { opcode = 1; } else if (!strcmp(op->valuestring, "replace")) { opcode = 2; } else if (!strcmp(op->valuestring, "move")) { opcode = 3; } else if (!strcmp(op->valuestring, "copy")) { opcode = 4; } else if (!strcmp(op->valuestring, "test")) { /* compare value: {...} with the given path */ return cJSONUtils_Compare(cJSONUtils_GetPointer(object, path->valuestring), cJSON_GetObjectItem(patch, "value")); } else { /* unknown opcode. */ return 3; } /* Remove/Replace */ if ((opcode == 1) || (opcode == 2)) { /* Get rid of old. */ cJSON_Delete(cJSONUtils_PatchDetach(object, (unsigned char*)path->valuestring)); if (opcode == 1) { /* For Remove, this is job done. */ return 0; } } /* Copy/Move uses "from". */ if ((opcode == 3) || (opcode == 4)) { cJSON *from = cJSON_GetObjectItem(patch, "from"); if (!from) { /* missing "from" for copy/move. */ return 4; } if (opcode == 3) { /* move */ value = cJSONUtils_PatchDetach(object, (unsigned char*)from->valuestring); } if (opcode == 4) { /* copy */ value = cJSONUtils_GetPointer(object, from->valuestring); } if (!value) { /* missing "from" for copy/move. */ return 5; } if (opcode == 4) { value = cJSON_Duplicate(value, 1); } if (!value) { /* out of memory for copy/move. */ return 6; } } else /* Add/Replace uses "value". */ { value = cJSON_GetObjectItem(patch, "value"); if (!value) { /* missing "value" for add/replace. */ return 7; } value = cJSON_Duplicate(value, 1); if (!value) { /* out of memory for add/replace. */ return 8; } } /* Now, just add "value" to "path". */ /* split pointer in parent and child */ parentptr = cJSONUtils_strdup((unsigned char*)path->valuestring); childptr = (unsigned char*)strrchr((char*)parentptr, '/'); if (childptr) { *childptr++ = '\0'; } parent = cJSONUtils_GetPointer(object, (char*)parentptr); cJSONUtils_InplaceDecodePointerString(childptr); /* add, remove, replace, move, copy, test. */ if (!parent) { /* Couldn't find object to add to. */ free(parentptr); cJSON_Delete(value); return 9; } else if (cJSON_IsArray(parent)) { if (!strcmp((char*)childptr, "-")) { cJSON_AddItemToArray(parent, value); } else { cJSON_InsertItemInArray(parent, atoi((char*)childptr), value); } } else if (cJSON_IsObject(parent)) { cJSON_DeleteItemFromObject(parent, (char*)childptr); cJSON_AddItemToObject(parent, (char*)childptr, value); } else { cJSON_Delete(value); } free(parentptr); return 0; }
static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) { cJSON *from_child = NULL; cJSON *to_child = NULL; cJSON *patch = NULL; if (to == NULL) { /* patch to delete everything */ return cJSON_CreateNull(); } if (!cJSON_IsObject(to) || !cJSON_IsObject(from)) { return cJSON_Duplicate(to, 1); } sort_object(from, case_sensitive); sort_object(to, case_sensitive); from_child = from->child; to_child = to->child; patch = cJSON_CreateObject(); while (from_child || to_child) { int diff; if (from_child != NULL) { if (to_child != NULL) { diff = strcmp(from_child->string, to_child->string); } else { diff = -1; } } else { diff = 1; } if (diff < 0) { /* from has a value that to doesn't have -> remove */ cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull()); from_child = from_child->next; } else if (diff > 0) { /* to has a value that from doesn't have -> add to patch */ cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1)); to_child = to_child->next; } else { /* object key exists in both objects */ if (!compare_json(from_child, to_child, case_sensitive)) { /* not identical --> generate a patch */ cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child)); } /* next key in the object */ from_child = from_child->next; to_child = to_child->next; } } if (patch->child == NULL) { /* no patch generated */ cJSON_Delete(patch); return NULL; } return patch; }
static void treasure_create_json_object_test(void) { struct rnd *rnd = rnd_alloc_fake_median(); struct treasure treasure; treasure_initialize(&treasure); treasure_generate_magic_items(&treasure, rnd, 3, ANY_MAGIC_ITEM); struct cJSON *json_object = treasure_create_json_object(&treasure); assert(cJSON_IsObject(json_object)); char *json_string = cJSON_PrintUnformatted(json_object); char const *expected = "{" "\"struct\":\"treasure\"," "\"rev\":0," "\"type\":null," "\"coins\":{" "\"struct\":\"coins\"," "\"rev\":0," "\"pp\":0," "\"gp\":0," "\"ep\":0," "\"sp\":0," "\"cp\":0" "}," "\"gems\":[]," "\"jewelry\":[]," "\"maps\":[]," "\"magic_items\":[" "{" "\"struct\":\"magic_item\"," "\"rev\":0," "\"experience_points\":2000," "\"true_description\":\"cloak of protection +2\"," "\"true_details\":[]," "\"true_value_in_cp\":4000000," "\"type\":\"miscellaneous\"" "}," "{" "\"struct\":\"magic_item\"," "\"rev\":0," "\"experience_points\":2000," "\"true_description\":\"cloak of protection +2\"," "\"true_details\":[]," "\"true_value_in_cp\":4000000," "\"type\":\"miscellaneous\"" "}," "{" "\"struct\":\"magic_item\"," "\"rev\":0," "\"experience_points\":2000," "\"true_description\":\"cloak of protection +2\"," "\"true_details\":[]," "\"true_value_in_cp\":4000000," "\"type\":\"miscellaneous\"" "}" "]" "}"; assert(str_eq(expected, json_string)); free(json_string); cJSON_Delete(json_object); treasure_finalize(&treasure); rnd_free(rnd); }