/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
json_object_private.h \
json_tokener.h \
json_util.h \
+ json_visit.h \
linkhash.h \
math_compat.h \
printbuf.h \
json_object_iterator.c \
json_tokener.c \
json_util.c \
+ json_visit.c \
linkhash.c \
printbuf.c \
random_seed.c
--- /dev/null
+/*
+ * 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
+}
+
--- /dev/null
+
+#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_ */
TESTS+= test_set_serializer.test
TESTS+= test_compare.test
TESTS+= test_set_value.test
+TESTS+= test_visit.test
check_PROGRAMS=
check_PROGRAMS += $(TESTS:.test=)
--- /dev/null
+#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;
+}
+
--- /dev/null
+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
+================================
+
--- /dev/null
+#!/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 $?