forked from json-c/json-c
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a json_c_visit() function to provide a way to iterate over a tree…
… of json-c objects.
- Loading branch information
Showing
8 changed files
with
410 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
|
Oops, something went wrong.