json_pointer: introduce json_pointer_get_internal() for internal usage

For JSON patch, we require that we get access to the parent of a JSON
object as well in order to do some operations via the API.

For example, given the object:
{
  "foo": "bar",
  "array", [ 1, 2, 3]
}

Using JSON pointer with the path
 * '/foo' will return 'bar' of type string
 * '/array/0' will return '1', of type integer

The problem is, that if we do 'json_object_put()' on any of the objects
above, this will not detach them from the parent, because there is no
information back to the parent.

One way to fix this, is to introduce links back to the parent, and have
these links be made by 'json_object_array_{put,insert}_idx()' and
'json_object_object_add{_ex}()'[1].

[1] For json_object_object_add_ex() we would need to de-constify the second
parameter, as we need to change it's internal state when being added to a
parent object. It may break some applications, but who knows.

But, since this information is needed mostly for JSON patch, another way to
address this, is to also retrieve the parent of an object via JSON pointer
and use json_object_object_del() and json_object_array_del_idx() on the
object's parent.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
This commit is contained in:
Alexandru Ardelean
2021-04-20 16:23:47 +03:00
committed by Eric Hawicz
parent 43d3118935
commit 5a46a3b76d
3 changed files with 76 additions and 17 deletions

View File

@@ -18,6 +18,7 @@ JSONC_PRIVATE {
array_list_new;
array_list_put_idx;
array_list_sort;
json_pointer_get_internal;
json_hex_chars;
json_parse_double;
json_parse_int64;

View File

@@ -100,6 +100,18 @@ void _json_c_set_last_err(const char *err_fmt, ...);
extern const char *json_hex_chars;
struct json_pointer_get_result {
struct json_object *parent;
struct json_object *obj;
union {
const char *key;
uint32_t index;
} id;
};
int json_pointer_get_internal(struct json_object *obj, const char *path,
struct json_pointer_get_result *res);
#ifdef __cplusplus
}
#endif

View File

@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <string.h>
#include "json_object_private.h"
#include "json_pointer.h"
#include "strdup_compat.h"
#include "vasprintf_compat.h"
@@ -88,14 +89,13 @@ check_oob:
}
static int json_pointer_get_single_path(struct json_object *obj, char *path,
struct json_object **value)
struct json_object **value, size_t *idx)
{
if (json_object_is_type(obj, json_type_array))
{
size_t idx;
if (!is_valid_index(obj, path, &idx))
if (!is_valid_index(obj, path, idx))
return -1;
obj = json_object_array_get_idx(obj, idx);
obj = json_object_array_get_idx(obj, *idx);
if (obj)
{
if (value)
@@ -147,9 +147,11 @@ static int json_pointer_set_single_path(struct json_object *parent, const char *
return -1;
}
static int json_pointer_get_recursive(struct json_object *obj, char *path,
struct json_object **value)
static int json_pointer_result_get_recursive(struct json_object *obj, char *path,
struct json_pointer_get_result *res)
{
struct json_object *parent_obj = obj;
size_t idx;
char *endp;
int rc;
@@ -166,24 +168,47 @@ static int json_pointer_get_recursive(struct json_object *obj, char *path,
*endp = '\0';
/* If we err-ed here, return here */
if ((rc = json_pointer_get_single_path(obj, path, &obj)))
if ((rc = json_pointer_get_single_path(obj, path, &obj, &idx)))
return rc;
if (endp)
{
/* Put the slash back, so that the sanity check passes on next recursion level */
*endp = '/';
return json_pointer_get_recursive(obj, endp, value);
return json_pointer_result_get_recursive(obj, endp, res);
}
/* We should be at the end of the recursion here */
if (value)
*value = obj;
if (res) {
res->parent = parent_obj;
res->obj = obj;
if (json_object_is_type(res->parent, json_type_array))
res->id.index = idx;
else
res->id.key = path;
}
return 0;
}
int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res)
static int json_pointer_object_get_recursive(struct json_object *obj, char *path,
struct json_object **value)
{
struct json_pointer_get_result res;
int rc;
rc = json_pointer_result_get_recursive(obj, path, &res);
if (rc)
return rc;
if (value)
*value = res.obj;
return 0;
}
int json_pointer_get_internal(struct json_object *obj, const char *path,
struct json_pointer_get_result *res)
{
char *path_copy = NULL;
int rc;
@@ -196,8 +221,11 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje
if (path[0] == '\0')
{
if (res)
*res = obj;
if (res) {
res->parent = NULL;
res->obj = obj;
}
res->id.key = NULL;
return 0;
}
@@ -207,12 +235,30 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje
errno = ENOMEM;
return -1;
}
rc = json_pointer_get_recursive(obj, path_copy, res);
rc = json_pointer_result_get_recursive(obj, path_copy, res);
/* re-map the path string to the const-path string */
if (rc == 0 && res->id.key && !json_object_is_type(res->parent, json_type_array))
res->id.key = path + (res->id.key - path_copy);
free(path_copy);
return rc;
}
int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res)
{
struct json_pointer_get_result jpres;
int rc;
rc = json_pointer_get_internal(obj, path, &jpres);
if (rc)
return rc;
if (res)
*res = jpres.obj;
return 0;
}
int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...)
{
char *path_copy = NULL;
@@ -239,7 +285,7 @@ int json_pointer_getf(struct json_object *obj, struct json_object **res, const c
goto out;
}
rc = json_pointer_get_recursive(obj, path_copy, res);
rc = json_pointer_object_get_recursive(obj, path_copy, res);
out:
free(path_copy);
@@ -286,7 +332,7 @@ int json_pointer_set(struct json_object **obj, const char *path, struct json_obj
return -1;
}
path_copy[endp - path] = '\0';
rc = json_pointer_get_recursive(*obj, path_copy, &set);
rc = json_pointer_object_get_recursive(*obj, path_copy, &set);
free(path_copy);
if (rc)
@@ -341,7 +387,7 @@ int json_pointer_setf(struct json_object **obj, struct json_object *value, const
}
*endp = '\0';
rc = json_pointer_get_recursive(*obj, path_copy, &set);
rc = json_pointer_object_get_recursive(*obj, path_copy, &set);
if (rc)
goto out;