Skip to content

Commit

Permalink
Implement brute force recursive descent
Browse files Browse the repository at this point in the history
  • Loading branch information
arr2036 committed Aug 5, 2015
1 parent fd3d4c6 commit 1fd2dfe
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 7 deletions.
11 changes: 6 additions & 5 deletions raddb/mods-available/json
Expand Up @@ -14,13 +14,14 @@
# above implementation.
#
# Selectors currently implemented are:
# - $ Root node (only valid at the start of the path).
# - @ Current node (only valid at the start of the path).
# - .<name> A field within an object.
# - [<idx>] Index within an array.
# - $ Root node (only valid at the start of the path).
# - @ Current node (only valid at the start of the path).
# - .<name> A field within an object.
# - [<idx>] Index within an array.
# - [<start>:<end>[:<step>]] A slice within an array (identical to the Python syntax).
# - [<idx>,<start>:<end>] Multiple indexes/slices within an array.
# - .* All the children of the current node
# - .* All the children of the current node.
# - .. Recursive descent.
#
# Automatic casting will occur between JSON and attribute types where possible.
#
Expand Down
64 changes: 63 additions & 1 deletion src/modules/rlm_json/jpath.c
Expand Up @@ -266,9 +266,70 @@ static int jpath_evaluate(TALLOC_CTX *ctx, value_data_t ***tail,
} else return 0;
}

/*
* @todo Brute force it more efficiently.
*/
case JPATH_SELECTOR_RECURSIVE_DESCENT:
{
int i;

if (fr_json_object_is_type(object, json_type_array)) {
struct array_list *array_obj;

/*
* Descend into each element of the array
*/
array_obj = json_object_get_array(object);
for (i = 0; i < array_obj->length; i++) {
ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
array_obj->array[i], node);
if (ret < 0) return ret;
if (ret == 1) child_matched = true;
}

/*
* On the way back up, evaluate the object's fields
*/
ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
object, node->next);
if (ret < 0) return ret;
if (ret == 1) child_matched = true;

return child_matched ? 1 : 0;
} else if (fr_json_object_is_type(object, json_type_object)) {
/*
* Descend into each field of the object
*/
json_object_object_foreach(object, field_name, field_value) {
rad_assert(field_name);
ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
field_value, node);
if (ret < 0) return ret;
if (ret == 1) child_matched = true;
}

/*
* On the way back up, evaluate the object's fields
*/
ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
object, node->next);
if (ret < 0) return ret;
if (ret == 1) child_matched = true;

return child_matched ? 1 : 0;
}

/*
* Descend down to the level of the leaf
*
* Parser guarantees that the recursive descent operator
* is never the last in a jpath sequence.
*/
return jpath_evaluate(ctx, tail, dst_type, dst_enumv, object, node->next);
}

case JPATH_SELECTOR_FILTER_EXPRESSION:
case JPATH_SELECTOR_EXPRESSION:
case JPATH_SELECTOR_RECURSIVE_DESCENT:
case JPATH_SELECTOR_INVALID:
case JPATH_SELECTOR_ROOT:
case JPATH_SELECTOR_CURRENT:
Expand Down Expand Up @@ -884,3 +945,4 @@ do { \

return p - in;
}

70 changes: 69 additions & 1 deletion src/tests/modules/json/eval.unlang
Expand Up @@ -471,7 +471,7 @@ update request {
&Tmp-Integer-0 !* ANY
}

# 35 An expanded field name with bad chars
# 35. An expanded field name with bad chars
update request {
&Tmp-String-0 := "\
{\
Expand All @@ -488,6 +488,74 @@ if (&Tmp-String-1 == 'baz') {
} else {
test_fail
}
update request {
&Tmp-String-1 !* ANY
}

update request {
&Tmp-String-0 := "\
{\
\"my_array\": [0, 1, 2, 3, 4, 5], \
\"my_object\": { \
\"foo\": \"bar\", \
\"num\" : 42, \
\"my_deep_object\": { \
\"foo\": \"baz\", \
\"num\": 99, \
\"bool\": true\
}, \
\"my_cats\": [\
\"fluffy\", \
\"scratchy\", \
\"flat\" \
], \
}, \
\"my_cats\": [\
\"spikey\", \
\"clawy\", \
\"woofy\" \
], \
}"
}

# 36. Recursive descent with field match
map json &Tmp-String-0 {
&Tmp-String-1 += '$..bool'
}
if (("%{Tmp-String-1[#]}" == 1) && (Tmp-String-1 == 'yes')) {
test_pass
} else {
test_fail
}
update request {
&Tmp-String-1 !* ANY
}

# 37. Recursive descent with element match (2nd element in each array)
map json &Tmp-String-0 {
&Tmp-String-1 += '$..[1]'
}
if (("%{Tmp-String-1[#]}" == 3) && (&Tmp-String-1[0] == '1') && (&Tmp-String-1[1] == 'scratchy') && (&Tmp-String-1[2] == 'clawy')) {
test_pass
} else {
test_fail
}
update request {
&Tmp-String-1 !* ANY
}

# 38. Recursive descent with field then element match
map json &Tmp-String-0 {
&Tmp-String-1 += '$..my_cats[2]'
}
if (("%{Tmp-String-1[#]}" == 2) && (&Tmp-String-1[0] == 'flat') && (&Tmp-String-1[1] == 'woofy')) {
test_pass
} else {
test_fail
}
update request {
&Tmp-String-1 !* ANY
}

# Disabled until a json-c >= 0.10 is available in travis
## xx. Unsigned 64bit integers
Expand Down
7 changes: 7 additions & 0 deletions src/tests/modules/json/parser.unlang
Expand Up @@ -261,6 +261,13 @@ if ("%{jpathvalidate:$..[0]}" == '6:$..[0]') {
test_fail
}

# 37. Expect success - Recursive descent followed by two selectors
if ("%{jpathvalidate:$..foo[0]}" == '9:$..foo[0]') {
test_pass
} else {
test_fail
}

# 37. Expect success - Recursive descent followed by wildcard
if ("%{jpathvalidate:$..*}" == '4:$..*') {
test_pass
Expand Down

0 comments on commit 1fd2dfe

Please sign in to comment.