@@ -22,18 +22,19 @@ | |||
/tests/test4 | |||
/tests/testReplaceExisting | |||
/tests/testSubDir | |||
/tests/test_parse_int64 | |||
/tests/test_parse | |||
/tests/test_cast | |||
/tests/test_charcase | |||
/tests/test_compare | |||
/tests/test_double_serializer | |||
/tests/test_locale | |||
/tests/test_null | |||
/tests/test_parse | |||
/tests/test_parse_int64 | |||
/tests/test_printbuf | |||
/tests/test_set_serializer | |||
/tests/test_compare | |||
/tests/test_util_file | |||
/tests/test_set_value | |||
/tests/test_util_file | |||
/tests/test_visit | |||
/tests/*.vg.out | |||
/tests/*.log | |||
/tests/*.trs | |||
@@ -28,6 +28,7 @@ libjson_cinclude_HEADERS = \ | |||
json_object_private.h \ | |||
json_tokener.h \ | |||
json_util.h \ | |||
json_visit.h \ | |||
linkhash.h \ | |||
math_compat.h \ | |||
printbuf.h \ | |||
@@ -43,6 +44,7 @@ libjson_c_la_SOURCES = \ | |||
json_object_iterator.c \ | |||
json_tokener.c \ | |||
json_util.c \ | |||
json_visit.c \ | |||
linkhash.c \ | |||
printbuf.c \ | |||
random_seed.c | |||
@@ -0,0 +1,133 @@ | |||
/* | |||
* Copyright (c) 2016 Eric Haszlakiewicz | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the MIT license. See COPYING for details. | |||
*/ | |||
#include <stdio.h> | |||
#include "config.h" | |||
#include "json_inttypes.h" | |||
#include "json_object.h" | |||
#include "json_visit.h" | |||
#include "linkhash.h" | |||
static int _json_c_visit(json_object *jso, json_object *parent_jso, | |||
const char *jso_key, size_t *jso_index, | |||
json_c_visit_userfunc userfunc, void *userarg); | |||
int json_c_visit(json_object *jso, int future_flags, | |||
json_c_visit_userfunc userfunc, void *userarg) | |||
{ | |||
int ret = _json_c_visit(jso, NULL, NULL, NULL, userfunc, userarg); | |||
switch(ret) | |||
{ | |||
case JSON_C_VISIT_RETURN_CONTINUE: | |||
case JSON_C_VISIT_RETURN_SKIP: | |||
case JSON_C_VISIT_RETURN_POP: | |||
case JSON_C_VISIT_RETURN_STOP: | |||
return 0; | |||
default: | |||
return JSON_C_VISIT_RETURN_ERROR; | |||
} | |||
} | |||
static int _json_c_visit(json_object *jso, json_object *parent_jso, | |||
const char *jso_key, size_t *jso_index, | |||
json_c_visit_userfunc userfunc, void *userarg) | |||
{ | |||
int userret = userfunc(jso, 0, parent_jso, jso_key, jso_index, userarg); | |||
switch(userret) | |||
{ | |||
case JSON_C_VISIT_RETURN_CONTINUE: | |||
break; | |||
case JSON_C_VISIT_RETURN_SKIP: | |||
case JSON_C_VISIT_RETURN_POP: | |||
case JSON_C_VISIT_RETURN_STOP: | |||
case JSON_C_VISIT_RETURN_ERROR: | |||
return userret; | |||
default: | |||
fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", userret); | |||
return JSON_C_VISIT_RETURN_ERROR; | |||
} | |||
switch(json_object_get_type(jso)) | |||
{ | |||
case json_type_null: | |||
case json_type_boolean: | |||
case json_type_double: | |||
case json_type_int: | |||
case json_type_string: | |||
// we already called userfunc above, move on to the next object | |||
return JSON_C_VISIT_RETURN_CONTINUE; | |||
case json_type_object: | |||
{ | |||
json_object_object_foreach(jso, key, child) | |||
{ | |||
userret = _json_c_visit(child, jso, key, NULL, userfunc, userarg); | |||
if (userret == JSON_C_VISIT_RETURN_POP) | |||
break; | |||
if (userret == JSON_C_VISIT_RETURN_STOP || | |||
userret == JSON_C_VISIT_RETURN_ERROR) | |||
return userret; | |||
if (userret != JSON_C_VISIT_RETURN_CONTINUE && | |||
userret != JSON_C_VISIT_RETURN_SKIP) | |||
{ | |||
fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", userret); | |||
return JSON_C_VISIT_RETURN_ERROR; | |||
} | |||
} | |||
break; | |||
} | |||
case json_type_array: | |||
{ | |||
size_t array_len = json_object_array_length(jso); | |||
size_t ii; | |||
for (ii = 0; ii < array_len; ii++) | |||
{ | |||
json_object *child = json_object_array_get_idx(jso, ii); | |||
userret = _json_c_visit(child, jso, NULL, &ii, userfunc, userarg); | |||
if (userret == JSON_C_VISIT_RETURN_POP) | |||
break; | |||
if (userret == JSON_C_VISIT_RETURN_STOP || | |||
userret == JSON_C_VISIT_RETURN_ERROR) | |||
return userret; | |||
if (userret != JSON_C_VISIT_RETURN_CONTINUE && | |||
userret != JSON_C_VISIT_RETURN_SKIP) | |||
{ | |||
fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", userret); | |||
return JSON_C_VISIT_RETURN_ERROR; | |||
} | |||
} | |||
break; | |||
} | |||
default: | |||
fprintf(stderr, "INTERNAL ERROR: _json_c_visit found object of unknown type: %d\n", json_object_get_type(jso)); | |||
return JSON_C_VISIT_RETURN_ERROR; | |||
} | |||
// Call userfunc for the second type on container types, after all | |||
// members of the container have been visited. | |||
// Non-container types will have already returned before this point. | |||
userret = userfunc(jso, JSON_C_VISIT_SECOND, parent_jso, jso_key, jso_index, userarg); | |||
switch(userret) | |||
{ | |||
case JSON_C_VISIT_RETURN_SKIP: | |||
case JSON_C_VISIT_RETURN_POP: | |||
// These are not really sensible during JSON_C_VISIT_SECOND, | |||
// but map them to JSON_C_VISIT_CONTINUE anyway. | |||
// FALLTHROUGH | |||
case JSON_C_VISIT_RETURN_CONTINUE: | |||
return JSON_C_VISIT_RETURN_CONTINUE; | |||
case JSON_C_VISIT_RETURN_STOP: | |||
case JSON_C_VISIT_RETURN_ERROR: | |||
return userret; | |||
default: | |||
fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", userret); | |||
return JSON_C_VISIT_RETURN_ERROR; | |||
} | |||
// NOTREACHED | |||
} | |||
@@ -0,0 +1,91 @@ | |||
#ifndef _json_c_json_visit_h_ | |||
#define _json_c_json_visit_h_ | |||
#include "json_object.h" | |||
typedef int (json_c_visit_userfunc)(json_object *jso, int flags, | |||
json_object *parent_jso, const char *jso_key, | |||
size_t *jso_index, void *userarg); | |||
/** | |||
* Visit each object in the JSON hierarchy starting at jso. | |||
* For each object, userfunc is called, passing the object and userarg. | |||
* If the object has a parent (i.e. anything other than jso itself) | |||
* its parent will be passed as parent_jso, and either jso_key or jso_index | |||
* will be set, depending on whether the parent is an object or an array. | |||
* | |||
* Nodes will be visited depth first, but containers (arrays and objects) | |||
* will be visited twice, the second time with JSON_C_VISIT_SECOND set in | |||
* flags. | |||
* | |||
* userfunc must return one of the defined return values, to indicate | |||
* whether and how to continue visiting nodes, or one of various ways to stop. | |||
* | |||
* Returns 0 if nodes were visited successfully, even if some were | |||
* intentionally skipped due to what userfunc returned. | |||
* Returns <0 if an error occurred during iteration, including if | |||
* userfunc returned JSON_C_VISIT_RETURN_ERROR. | |||
*/ | |||
int json_c_visit(json_object *jso, int future_flags, | |||
json_c_visit_userfunc userfunc, void *userarg); | |||
/** | |||
* Passed to json_c_visit_userfunc as one of the flags values to indicate | |||
* that this is the second time a container (array or object) is being | |||
* called, after all of it's members have been iterated over. | |||
*/ | |||
#define JSON_C_VISIT_SECOND 0x02 | |||
/** | |||
* This json_c_visit_userfunc return value indicates that iteration | |||
* should proceed normally. | |||
*/ | |||
#define JSON_C_VISIT_RETURN_CONTINUE 0 | |||
/** | |||
* This json_c_visit_userfunc return value indicates that iteration | |||
* over the members of the current object should be skipped. | |||
* If the current object isn't a container (array or object), this | |||
* is no different than JSON_C_VISIT_RETURN_CONTINUE. | |||
*/ | |||
#define JSON_C_VISIT_RETURN_SKIP 7547 | |||
/** | |||
* This json_c_visit_userfunc return value indicates that iteration | |||
* of the fields/elements of the <b>containing</b> object should stop | |||
* and continue "popped up" a level of the object hierarchy. | |||
* For example, returning this when handling arg will result in | |||
* arg3 and any other fields being skipped. The next call to userfunc | |||
* will be the JSON_C_VISIT_SECOND call on "foo", followed by a userfunc | |||
* call on "bar". | |||
* <pre> | |||
* { | |||
* "foo": { | |||
* "arg1": 1, | |||
* "arg2": 2, | |||
* "arg3": 3, | |||
* ... | |||
* }, | |||
* "bar": { | |||
* ... | |||
* } | |||
* } | |||
* </pre> | |||
*/ | |||
#define JSON_C_VISIT_RETURN_POP 767 | |||
/** | |||
* This json_c_visit_userfunc return value indicates that iteration | |||
* should stop immediately, and cause json_c_visit to return success. | |||
*/ | |||
#define JSON_C_VISIT_RETURN_STOP 7867 | |||
/** | |||
* This json_c_visit_userfunc return value indicates that iteration | |||
* should stop immediately, and cause json_c_visit to return an error. | |||
*/ | |||
#define JSON_C_VISIT_RETURN_ERROR -1 | |||
#endif /* _json_c_json_visit_h_ */ |
@@ -22,6 +22,7 @@ TESTS+= test_printbuf.test | |||
TESTS+= test_set_serializer.test | |||
TESTS+= test_compare.test | |||
TESTS+= test_set_value.test | |||
TESTS+= test_visit.test | |||
check_PROGRAMS= | |||
check_PROGRAMS += $(TESTS:.test=) | |||
@@ -0,0 +1,111 @@ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <stddef.h> | |||
#include <string.h> | |||
#include <assert.h> | |||
#include "json.h" | |||
#include "json_tokener.h" | |||
#include "json_visit.h" | |||
static json_c_visit_userfunc emit_object; | |||
static json_c_visit_userfunc skip_arrays; | |||
static json_c_visit_userfunc pop_and_stop; | |||
static json_c_visit_userfunc err_on_subobj2; | |||
int main(void) | |||
{ | |||
MC_SET_DEBUG(1); | |||
const char *input = "{\ | |||
\"obj1\": 123,\ | |||
\"obj2\": {\ | |||
\"subobj1\": \"aaa\",\ | |||
\"subobj2\": \"bbb\",\ | |||
\"subobj3\": [ \"elem1\", \"elem2\", true ],\ | |||
},\ | |||
\"obj3\": 1.234,\ | |||
\"obj4\": [ true, false, null ]\ | |||
}"; | |||
json_object *jso = json_tokener_parse(input); | |||
printf("jso.to_string()=%s\n", json_object_to_json_string(jso)); | |||
int rv; | |||
rv = json_c_visit(jso, 0, emit_object, NULL); | |||
printf("json_c_visit(emit_object)=%d\n", rv); | |||
printf("================================\n\n"); | |||
rv = json_c_visit(jso, 0, skip_arrays, NULL); | |||
printf("json_c_visit(skip_arrays)=%d\n", rv); | |||
printf("================================\n\n"); | |||
rv = json_c_visit(jso, 0, pop_and_stop, NULL); | |||
printf("json_c_visit(pop_and_stop)=%d\n", rv); | |||
printf("================================\n\n"); | |||
rv = json_c_visit(jso, 0, err_on_subobj2, NULL); | |||
printf("json_c_visit(err_on_subobj2)=%d\n", rv); | |||
printf("================================\n\n"); | |||
json_object_put(jso); | |||
} | |||
static int emit_object(json_object *jso, int flags, | |||
json_object *parent_jso, | |||
const char *jso_key, | |||
size_t *jso_index, void *userarg) | |||
{ | |||
printf("flags: 0x%x, key: %s, index: %ld, value: %s\n", | |||
flags, | |||
(jso_key ? jso_key : "(null)"), | |||
(jso_index ? (long)*jso_index : -1L), | |||
json_object_to_json_string(jso)); | |||
return JSON_C_VISIT_RETURN_CONTINUE; | |||
} | |||
static int skip_arrays(json_object *jso, int flags, | |||
json_object *parent_jso, | |||
const char *jso_key, | |||
size_t *jso_index, void *userarg) | |||
{ | |||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | |||
if (json_object_get_type(jso) == json_type_array) | |||
return JSON_C_VISIT_RETURN_SKIP; | |||
return JSON_C_VISIT_RETURN_CONTINUE; | |||
} | |||
static int pop_and_stop(json_object *jso, int flags, | |||
json_object *parent_jso, | |||
const char *jso_key, | |||
size_t *jso_index, void *userarg) | |||
{ | |||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | |||
if (jso_key != NULL && strcmp(jso_key, "subobj1") == 0) | |||
{ | |||
printf("POP after handling subobj1\n"); | |||
return JSON_C_VISIT_RETURN_POP; | |||
} | |||
if (jso_key != NULL && strcmp(jso_key, "obj3") == 0) | |||
{ | |||
printf("STOP after handling obj3\n"); | |||
return JSON_C_VISIT_RETURN_STOP; | |||
} | |||
return JSON_C_VISIT_RETURN_CONTINUE; | |||
} | |||
static int err_on_subobj2(json_object *jso, int flags, | |||
json_object *parent_jso, | |||
const char *jso_key, | |||
size_t *jso_index, void *userarg) | |||
{ | |||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | |||
if (jso_key != NULL && strcmp(jso_key, "subobj2") == 0) | |||
{ | |||
printf("ERROR after handling subobj1\n"); | |||
return JSON_C_VISIT_RETURN_ERROR; | |||
} | |||
return JSON_C_VISIT_RETURN_CONTINUE; | |||
} | |||
@@ -0,0 +1,55 @@ | |||
jso.to_string()={ "obj1": 123, "obj2": { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] }, "obj3": 1.234, "obj4": [ true, false, null ] } | |||
flags: 0x0, key: (null), index: -1, value: { "obj1": 123, "obj2": { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] }, "obj3": 1.234, "obj4": [ true, false, null ] } | |||
flags: 0x0, key: obj1, index: -1, value: 123 | |||
flags: 0x0, key: obj2, index: -1, value: { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] } | |||
flags: 0x0, key: subobj1, index: -1, value: "aaa" | |||
flags: 0x0, key: subobj2, index: -1, value: "bbb" | |||
flags: 0x0, key: subobj3, index: -1, value: [ "elem1", "elem2", true ] | |||
flags: 0x0, key: (null), index: 0, value: "elem1" | |||
flags: 0x0, key: (null), index: 1, value: "elem2" | |||
flags: 0x0, key: (null), index: 2, value: true | |||
flags: 0x2, key: subobj3, index: -1, value: [ "elem1", "elem2", true ] | |||
flags: 0x2, key: obj2, index: -1, value: { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] } | |||
flags: 0x0, key: obj3, index: -1, value: 1.234 | |||
flags: 0x0, key: obj4, index: -1, value: [ true, false, null ] | |||
flags: 0x0, key: (null), index: 0, value: true | |||
flags: 0x0, key: (null), index: 1, value: false | |||
flags: 0x0, key: (null), index: 2, value: null | |||
flags: 0x2, key: obj4, index: -1, value: [ true, false, null ] | |||
flags: 0x2, key: (null), index: -1, value: { "obj1": 123, "obj2": { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] }, "obj3": 1.234, "obj4": [ true, false, null ] } | |||
json_c_visit(emit_object)=0 | |||
================================ | |||
flags: 0x0, key: (null), index: -1, value: { "obj1": 123, "obj2": { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] }, "obj3": 1.234, "obj4": [ true, false, null ] } | |||
flags: 0x0, key: obj1, index: -1, value: 123 | |||
flags: 0x0, key: obj2, index: -1, value: { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] } | |||
flags: 0x0, key: subobj1, index: -1, value: "aaa" | |||
flags: 0x0, key: subobj2, index: -1, value: "bbb" | |||
flags: 0x0, key: subobj3, index: -1, value: [ "elem1", "elem2", true ] | |||
flags: 0x2, key: obj2, index: -1, value: { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] } | |||
flags: 0x0, key: obj3, index: -1, value: 1.234 | |||
flags: 0x0, key: obj4, index: -1, value: [ true, false, null ] | |||
flags: 0x2, key: (null), index: -1, value: { "obj1": 123, "obj2": { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] }, "obj3": 1.234, "obj4": [ true, false, null ] } | |||
json_c_visit(skip_arrays)=0 | |||
================================ | |||
flags: 0x0, key: (null), index: -1, value: { "obj1": 123, "obj2": { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] }, "obj3": 1.234, "obj4": [ true, false, null ] } | |||
flags: 0x0, key: obj1, index: -1, value: 123 | |||
flags: 0x0, key: obj2, index: -1, value: { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] } | |||
flags: 0x0, key: subobj1, index: -1, value: "aaa" | |||
POP after handling subobj1 | |||
flags: 0x2, key: obj2, index: -1, value: { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] } | |||
flags: 0x0, key: obj3, index: -1, value: 1.234 | |||
STOP after handling obj3 | |||
json_c_visit(pop_and_stop)=0 | |||
================================ | |||
flags: 0x0, key: (null), index: -1, value: { "obj1": 123, "obj2": { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] }, "obj3": 1.234, "obj4": [ true, false, null ] } | |||
flags: 0x0, key: obj1, index: -1, value: 123 | |||
flags: 0x0, key: obj2, index: -1, value: { "subobj1": "aaa", "subobj2": "bbb", "subobj3": [ "elem1", "elem2", true ] } | |||
flags: 0x0, key: subobj1, index: -1, value: "aaa" | |||
flags: 0x0, key: subobj2, index: -1, value: "bbb" | |||
ERROR after handling subobj1 | |||
json_c_visit(err_on_subobj2)=-1 | |||
================================ | |||
@@ -0,0 +1,12 @@ | |||
#!/bin/sh | |||
# Common definitions | |||
if test -z "$srcdir"; then | |||
srcdir="${0%/*}" | |||
test "$srcdir" = "$0" && srcdir=. | |||
test -z "$srcdir" && srcdir=. | |||
fi | |||
. "$srcdir/test-defs.sh" | |||
run_output_test test_visit | |||
exit $? |