| @@ -22,18 +22,19 @@ | |||||
| /tests/test4 | /tests/test4 | ||||
| /tests/testReplaceExisting | /tests/testReplaceExisting | ||||
| /tests/testSubDir | /tests/testSubDir | ||||
| /tests/test_parse_int64 | |||||
| /tests/test_parse | |||||
| /tests/test_cast | /tests/test_cast | ||||
| /tests/test_charcase | /tests/test_charcase | ||||
| /tests/test_compare | |||||
| /tests/test_double_serializer | /tests/test_double_serializer | ||||
| /tests/test_locale | /tests/test_locale | ||||
| /tests/test_null | /tests/test_null | ||||
| /tests/test_parse | |||||
| /tests/test_parse_int64 | |||||
| /tests/test_printbuf | /tests/test_printbuf | ||||
| /tests/test_set_serializer | /tests/test_set_serializer | ||||
| /tests/test_compare | |||||
| /tests/test_util_file | |||||
| /tests/test_set_value | /tests/test_set_value | ||||
| /tests/test_util_file | |||||
| /tests/test_visit | |||||
| /tests/*.vg.out | /tests/*.vg.out | ||||
| /tests/*.log | /tests/*.log | ||||
| /tests/*.trs | /tests/*.trs | ||||
| @@ -28,6 +28,7 @@ libjson_cinclude_HEADERS = \ | |||||
| json_object_private.h \ | json_object_private.h \ | ||||
| json_tokener.h \ | json_tokener.h \ | ||||
| json_util.h \ | json_util.h \ | ||||
| json_visit.h \ | |||||
| linkhash.h \ | linkhash.h \ | ||||
| math_compat.h \ | math_compat.h \ | ||||
| printbuf.h \ | printbuf.h \ | ||||
| @@ -43,6 +44,7 @@ libjson_c_la_SOURCES = \ | |||||
| json_object_iterator.c \ | json_object_iterator.c \ | ||||
| json_tokener.c \ | json_tokener.c \ | ||||
| json_util.c \ | json_util.c \ | ||||
| json_visit.c \ | |||||
| linkhash.c \ | linkhash.c \ | ||||
| printbuf.c \ | printbuf.c \ | ||||
| random_seed.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_set_serializer.test | ||||
| TESTS+= test_compare.test | TESTS+= test_compare.test | ||||
| TESTS+= test_set_value.test | TESTS+= test_set_value.test | ||||
| TESTS+= test_visit.test | |||||
| check_PROGRAMS= | check_PROGRAMS= | ||||
| check_PROGRAMS += $(TESTS:.test=) | 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 $? | |||||