add parser for json path
This commit is contained in:
parent
78cc094d47
commit
fecfa27727
@ -183,6 +183,38 @@ TALER_JSON_contract_part_forget (json_t *json,
|
||||
const char *field);
|
||||
|
||||
|
||||
/**
|
||||
* Called for each path found after expanding a path.
|
||||
*
|
||||
* @param cls the closure.
|
||||
* @param object_id the name of the object that is pointed to.
|
||||
* @param parent the parent of the object at @e object_id.
|
||||
*/
|
||||
typedef void
|
||||
(*TALER_JSON_ExpandPathCallback) (
|
||||
void *cls,
|
||||
const char *object_id,
|
||||
json_t *parent);
|
||||
|
||||
|
||||
/**
|
||||
* Expands a path for a json object. May call the callback several times
|
||||
* if the path contains a wildcard.
|
||||
*
|
||||
* @param json the json object the path references.
|
||||
* @param path the path to expand. Must begin with "$." and follow dot notation,
|
||||
* and may include array indices and wildcards.
|
||||
* @param cb the callback.
|
||||
* @param cb_cls closure for the callback.
|
||||
* @return GNUNET_OK on success, GNUNET_SYSERR if @e path is invalid.
|
||||
*/
|
||||
int
|
||||
TALER_JSON_expand_path (json_t *json,
|
||||
const char *path,
|
||||
TALER_JSON_ExpandPathCallback cb,
|
||||
void *cb_cls);
|
||||
|
||||
|
||||
/**
|
||||
* Extract the Taler error code from the given @a json object.
|
||||
* Note that #TALER_EC_NONE is returned if no "code" is present.
|
||||
|
143
src/json/json.c
143
src/json/json.c
@ -459,6 +459,149 @@ TALER_JSON_contract_part_forget (json_t *json,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a json path.
|
||||
*
|
||||
* @param obj the object that the path is relative to.
|
||||
* @param prev the parent of @e obj.
|
||||
* @param path the path to parse.
|
||||
* @param cb the callback to call, if we get to the end of @e path.
|
||||
* @param cb_cls the closure for the callback.
|
||||
* @return GNUNET_OK on success, GNUNET_SYSERR if @e path is malformed.
|
||||
*/
|
||||
static int
|
||||
parse_path (json_t *obj,
|
||||
json_t *prev,
|
||||
const char *path,
|
||||
TALER_JSON_ExpandPathCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
char *id = GNUNET_strdup (path);
|
||||
char *next_id = strchr (id,
|
||||
'.');
|
||||
char *next_path;
|
||||
char *bracket;
|
||||
json_t *parent = obj;
|
||||
json_t *next_obj = NULL;
|
||||
|
||||
if (NULL != next_id)
|
||||
{
|
||||
bracket = strchr (next_id,
|
||||
'[');
|
||||
*next_id = '\0';
|
||||
next_id++;
|
||||
next_path = GNUNET_strdup (next_id);
|
||||
char *next_dot = strchr (next_id,
|
||||
'.');
|
||||
if (NULL != next_dot)
|
||||
*next_dot = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
cb (cb_cls,
|
||||
id,
|
||||
prev);
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
/* If this is the first time this is called, make sure id is "$" */
|
||||
if ((NULL == prev) &&
|
||||
(0 != strcmp (id,
|
||||
"$")))
|
||||
return GNUNET_SYSERR;
|
||||
|
||||
/* Check for bracketed indices */
|
||||
if (NULL != bracket)
|
||||
{
|
||||
char *end_bracket = strchr (bracket,
|
||||
']');
|
||||
if (NULL == end_bracket)
|
||||
return GNUNET_SYSERR;
|
||||
*end_bracket = '\0';
|
||||
|
||||
*bracket = '\0';
|
||||
bracket++;
|
||||
|
||||
parent = json_object_get (obj,
|
||||
next_id);
|
||||
if (0 == strcmp (bracket,
|
||||
"*"))
|
||||
{
|
||||
size_t index;
|
||||
json_t *value;
|
||||
int ret = GNUNET_OK;
|
||||
json_array_foreach (parent, index, value) {
|
||||
ret = parse_path (value,
|
||||
obj,
|
||||
next_path,
|
||||
cb,
|
||||
cb_cls);
|
||||
if (GNUNET_OK != ret)
|
||||
{
|
||||
GNUNET_free (id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int index;
|
||||
if (1 != sscanf (bracket,
|
||||
"%u",
|
||||
&index))
|
||||
return GNUNET_SYSERR;
|
||||
next_obj = json_array_get (parent,
|
||||
index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No brackets, so just fetch the object by name */
|
||||
next_obj = json_object_get (obj,
|
||||
next_id);
|
||||
}
|
||||
|
||||
if (NULL != next_obj)
|
||||
{
|
||||
return parse_path (next_obj,
|
||||
parent,
|
||||
next_path,
|
||||
cb,
|
||||
cb_cls);
|
||||
}
|
||||
|
||||
GNUNET_free (id);
|
||||
GNUNET_free (next_path);
|
||||
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expands a path for a json object. May call the callback several times
|
||||
* if the path contains a wildcard.
|
||||
*
|
||||
* @param json the json object the path references.
|
||||
* @param path the path to expand. Must begin with "$." and follow dot notation,
|
||||
* and may include array indices and wildcards.
|
||||
* @param cb the callback.
|
||||
* @param cb_cls closure for the callback.
|
||||
* @return GNUNET_OK on success, GNUNET_SYSERR if @e path is invalid.
|
||||
*/
|
||||
int
|
||||
TALER_JSON_expand_path (json_t *json,
|
||||
const char *path,
|
||||
TALER_JSON_ExpandPathCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
return parse_path (json,
|
||||
NULL,
|
||||
path,
|
||||
cb,
|
||||
cb_cls);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract the Taler error code from the given @a json object.
|
||||
* Note that #TALER_EC_NONE is returned if no "code" is present.
|
||||
|
@ -56,6 +56,36 @@ test_amount (void)
|
||||
}
|
||||
|
||||
|
||||
struct TestPath_Closure
|
||||
{
|
||||
const char **object_ids;
|
||||
|
||||
const json_t **parents;
|
||||
|
||||
unsigned int results_length;
|
||||
|
||||
int cmp_result;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
path_cb (void *cls,
|
||||
const char *object_id,
|
||||
json_t *parent)
|
||||
{
|
||||
struct TestPath_Closure *cmp = cls;
|
||||
if (NULL == cmp)
|
||||
return;
|
||||
unsigned int i = cmp->results_length;
|
||||
if ((0 != strcmp (cmp->object_ids[i],
|
||||
object_id)) ||
|
||||
(1 != json_equal (cmp->parents[i],
|
||||
parent)))
|
||||
cmp->cmp_result = 1;
|
||||
cmp->results_length += 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_contract ()
|
||||
{
|
||||
@ -64,6 +94,7 @@ test_contract ()
|
||||
json_t *c1;
|
||||
json_t *c2;
|
||||
json_t *c3;
|
||||
json_t *c4;
|
||||
|
||||
c1 = json_pack ("{s:s, s:{s:s, s:{s:s}}}",
|
||||
"k1", "v1",
|
||||
@ -113,6 +144,119 @@ test_contract ()
|
||||
TALER_JSON_contract_hash (c3,
|
||||
&h2));
|
||||
json_decref (c3);
|
||||
c4 = json_pack ("{s:{s:s}, s:[{s:s}, {s:s}, {s:s}]}",
|
||||
"abc1",
|
||||
"xyz", "value",
|
||||
"fruit",
|
||||
"name", "banana",
|
||||
"name", "apple",
|
||||
"name", "orange");
|
||||
GNUNET_assert (NULL != c4);
|
||||
GNUNET_assert (GNUNET_SYSERR ==
|
||||
TALER_JSON_expand_path (c4,
|
||||
"%.xyz",
|
||||
&path_cb,
|
||||
NULL));
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_JSON_expand_path (c4,
|
||||
"$.nonexistent_id",
|
||||
&path_cb,
|
||||
NULL));
|
||||
GNUNET_assert (GNUNET_SYSERR ==
|
||||
TALER_JSON_expand_path (c4,
|
||||
"$.fruit[n]",
|
||||
&path_cb,
|
||||
NULL));
|
||||
|
||||
{
|
||||
const char *object_ids[] = { "xyz" };
|
||||
const json_t *parents[] = {
|
||||
json_object_get (c4,
|
||||
"abc1")
|
||||
};
|
||||
struct TestPath_Closure tp = {
|
||||
.object_ids = object_ids,
|
||||
.parents = parents,
|
||||
.results_length = 0,
|
||||
.cmp_result = 0
|
||||
};
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_JSON_expand_path (c4,
|
||||
"$.abc1.xyz",
|
||||
&path_cb,
|
||||
&tp));
|
||||
GNUNET_assert (1 == tp.results_length);
|
||||
GNUNET_assert (0 == tp.cmp_result);
|
||||
}
|
||||
{
|
||||
const char *object_ids[] = { "name" };
|
||||
const json_t *parents[] = {
|
||||
json_array_get (json_object_get (c4,
|
||||
"fruit"),
|
||||
0)
|
||||
};
|
||||
struct TestPath_Closure tp = {
|
||||
.object_ids = object_ids,
|
||||
.parents = parents,
|
||||
.results_length = 0,
|
||||
.cmp_result = 0
|
||||
};
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_JSON_expand_path (c4,
|
||||
"$.fruit[0].name",
|
||||
&path_cb,
|
||||
&tp));
|
||||
GNUNET_assert (1 == tp.results_length);
|
||||
GNUNET_assert (0 == tp.cmp_result);
|
||||
}
|
||||
{
|
||||
const char *object_ids[] = { "name", "name", "name" };
|
||||
const json_t *parents[] = {
|
||||
json_array_get (json_object_get (c4,
|
||||
"fruit"),
|
||||
0),
|
||||
json_array_get (json_object_get (c4,
|
||||
"fruit"),
|
||||
1),
|
||||
json_array_get (json_object_get (c4,
|
||||
"fruit"),
|
||||
2)
|
||||
};
|
||||
struct TestPath_Closure tp = {
|
||||
.object_ids = object_ids,
|
||||
.parents = parents,
|
||||
.results_length = 0,
|
||||
.cmp_result = 0
|
||||
};
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_JSON_expand_path (c4,
|
||||
"$.fruit[*].name",
|
||||
&path_cb,
|
||||
&tp));
|
||||
GNUNET_assert (3 == tp.results_length);
|
||||
GNUNET_assert (0 == tp.cmp_result);
|
||||
}
|
||||
{
|
||||
const char *object_ids[] = { "fruit[0]" };
|
||||
const json_t *parents[] = {
|
||||
json_object_get (c4,
|
||||
"fruit")
|
||||
};
|
||||
struct TestPath_Closure tp = {
|
||||
.object_ids = object_ids,
|
||||
.parents = parents,
|
||||
.results_length = 0,
|
||||
.cmp_result = 0
|
||||
};
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_JSON_expand_path (c4,
|
||||
"$.fruit[0]",
|
||||
&path_cb,
|
||||
&tp));
|
||||
GNUNET_assert (1 == tp.results_length);
|
||||
GNUNET_assert (0 == tp.cmp_result);
|
||||
}
|
||||
json_decref (c4);
|
||||
if (0 !=
|
||||
GNUNET_memcmp (&h1,
|
||||
&h2))
|
||||
|
Loading…
Reference in New Issue
Block a user