From 58fd67033505d448e484041f928d3fe7c92cadc6 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 19 Jun 2015 17:13:03 -0600 Subject: [PATCH] json: make it easier to type-check when getting from object While working in qemu_monitor_json, I repeatedly found myself getting a value then checking if it was an object. Add some wrappers to make this task easier. * src/util/virjson.c (virJSONValueObjectGetByType) (virJSONValueObjectGetObject, virJSONValueObjectGetArray): New functions. (virJSONValueObjectGetString, virJSONValueObjectGetNumberInt) (virJSONValueObjectGetNumberUint) (virJSONValueObjectGetNumberLong) (virJSONValueObjectGetNumberUlong) (virJSONValueObjectGetNumberDouble) (virJSONValueObjectGetBoolean): Simplify. (virJSONValueIsNull): Change return type. * src/util/virjson.h: Reflect changes. * src/libvirt_private.syms (virjson.h): Export them. * tests/jsontest.c (testJSONLookup): New test. Signed-off-by: Eric Blake --- src/libvirt_private.syms | 3 + src/util/virjson.c | 76 ++++++++++++------------ src/util/virjson.h | 8 ++- tests/jsontest.c | 123 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+), 38 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 51044b0af8..1566d11e41 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1632,13 +1632,16 @@ virJSONValueObjectCreate; virJSONValueObjectCreateVArgs; virJSONValueObjectForeachKeyValue; virJSONValueObjectGet; +virJSONValueObjectGetArray; virJSONValueObjectGetBoolean; +virJSONValueObjectGetByType; virJSONValueObjectGetKey; virJSONValueObjectGetNumberDouble; virJSONValueObjectGetNumberInt; virJSONValueObjectGetNumberLong; virJSONValueObjectGetNumberUint; virJSONValueObjectGetNumberUlong; +virJSONValueObjectGetObject; virJSONValueObjectGetString; virJSONValueObjectGetValue; virJSONValueObjectHasKey; diff --git a/src/util/virjson.c b/src/util/virjson.c index 7d4ece6465..29e2c39636 100644 --- a/src/util/virjson.c +++ b/src/util/virjson.c @@ -766,6 +766,21 @@ virJSONValueObjectGet(virJSONValuePtr object, } +/* Return the value associated with KEY within OBJECT, but return NULL + * if the key is missing or if value is not the correct TYPE. */ +virJSONValuePtr +virJSONValueObjectGetByType(virJSONValuePtr object, + const char *key, + virJSONType type) +{ + virJSONValuePtr value = virJSONValueObjectGet(object, key); + + if (value && value->type == type) + return value; + return NULL; +} + + int virJSONValueObjectKeysNumber(virJSONValuePtr object) { @@ -1057,13 +1072,10 @@ virJSONValueNewArrayFromBitmap(virBitmapPtr bitmap) } -int +bool virJSONValueIsNull(virJSONValuePtr val) { - if (val->type != VIR_JSON_TYPE_NULL) - return 0; - - return 1; + return val->type == VIR_JSON_TYPE_NULL; } @@ -1071,11 +1083,8 @@ const char * virJSONValueObjectGetString(virJSONValuePtr object, const char *key) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return NULL; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return NULL; @@ -1088,11 +1097,8 @@ virJSONValueObjectGetNumberInt(virJSONValuePtr object, const char *key, int *value) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return -1; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return -1; @@ -1105,11 +1111,8 @@ virJSONValueObjectGetNumberUint(virJSONValuePtr object, const char *key, unsigned int *value) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return -1; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return -1; @@ -1122,11 +1125,8 @@ virJSONValueObjectGetNumberLong(virJSONValuePtr object, const char *key, long long *value) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return -1; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return -1; @@ -1139,11 +1139,8 @@ virJSONValueObjectGetNumberUlong(virJSONValuePtr object, const char *key, unsigned long long *value) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return -1; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return -1; @@ -1156,11 +1153,8 @@ virJSONValueObjectGetNumberDouble(virJSONValuePtr object, const char *key, double *value) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return -1; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return -1; @@ -1173,11 +1167,8 @@ virJSONValueObjectGetBoolean(virJSONValuePtr object, const char *key, bool *value) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return -1; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return -1; @@ -1185,15 +1176,26 @@ virJSONValueObjectGetBoolean(virJSONValuePtr object, } +virJSONValuePtr +virJSONValueObjectGetObject(virJSONValuePtr object, const char *key) +{ + return virJSONValueObjectGetByType(object, key, VIR_JSON_TYPE_OBJECT); +} + + +virJSONValuePtr +virJSONValueObjectGetArray(virJSONValuePtr object, const char *key) +{ + return virJSONValueObjectGetByType(object, key, VIR_JSON_TYPE_ARRAY); +} + + int virJSONValueObjectIsNull(virJSONValuePtr object, const char *key) { - virJSONValuePtr val; - if (object->type != VIR_JSON_TYPE_OBJECT) - return -1; + virJSONValuePtr val = virJSONValueObjectGet(object, key); - val = virJSONValueObjectGet(object, key); if (!val) return -1; diff --git a/src/util/virjson.h b/src/util/virjson.h index e871b2ea9f..a7df6e591b 100644 --- a/src/util/virjson.h +++ b/src/util/virjson.h @@ -110,6 +110,8 @@ int virJSONValueArrayAppend(virJSONValuePtr object, virJSONValuePtr value); int virJSONValueObjectHasKey(virJSONValuePtr object, const char *key); virJSONValuePtr virJSONValueObjectGet(virJSONValuePtr object, const char *key); +virJSONValuePtr virJSONValueObjectGetByType(virJSONValuePtr object, + const char *key, virJSONType type); bool virJSONValueIsArray(virJSONValuePtr array); int virJSONValueArraySize(const virJSONValue *array); @@ -129,7 +131,11 @@ int virJSONValueGetNumberDouble(virJSONValuePtr object, double *value); int virJSONValueGetBoolean(virJSONValuePtr object, bool *value); int virJSONValueGetArrayAsBitmap(const virJSONValue *val, virBitmapPtr *bitmap) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); -int virJSONValueIsNull(virJSONValuePtr object); +bool virJSONValueIsNull(virJSONValuePtr object); +virJSONValuePtr virJSONValueObjectGetObject(virJSONValuePtr object, + const char *key); +virJSONValuePtr virJSONValueObjectGetArray(virJSONValuePtr object, + const char *key); const char *virJSONValueObjectGetString(virJSONValuePtr object, const char *key); int virJSONValueObjectGetNumberInt(virJSONValuePtr object, const char *key, int *value); diff --git a/tests/jsontest.c b/tests/jsontest.c index f226c9251e..34a07ee264 100644 --- a/tests/jsontest.c +++ b/tests/jsontest.c @@ -118,6 +118,114 @@ testJSONAddRemove(const void *data) } +static int +testJSONLookup(const void *data) +{ + const struct testInfo *info = data; + virJSONValuePtr json; + virJSONValuePtr value = NULL; + char *result = NULL; + int rc; + int number; + const char *str; + int ret = -1; + + json = virJSONValueFromString(info->doc); + if (!json) { + VIR_TEST_VERBOSE("Fail to parse %s\n", info->doc); + ret = -1; + goto cleanup; + } + + value = virJSONValueObjectGetObject(json, "a"); + if (value) { + if (!info->pass) { + VIR_TEST_VERBOSE("lookup for 'a' in '%s' should have failed\n", + info->doc); + goto cleanup; + } else { + result = virJSONValueToString(value, false); + if (STRNEQ_NULLABLE(result, "{}")) { + VIR_TEST_VERBOSE("lookup for 'a' in '%s' found '%s' but " + "should have found '{}'\n", + info->doc, NULLSTR(result)); + goto cleanup; + } + VIR_FREE(result); + } + } else if (info->pass) { + VIR_TEST_VERBOSE("lookup for 'a' in '%s' should have succeeded\n", + info->doc); + goto cleanup; + } + + number = 2; + rc = virJSONValueObjectGetNumberInt(json, "b", &number); + if (rc == 0) { + if (!info->pass) { + VIR_TEST_VERBOSE("lookup for 'b' in '%s' should have failed\n", + info->doc); + goto cleanup; + } else if (number != 1) { + VIR_TEST_VERBOSE("lookup for 'b' in '%s' found %d but " + "should have found 1\n", + info->doc, number); + goto cleanup; + } + } else if (info->pass) { + VIR_TEST_VERBOSE("lookup for 'b' in '%s' should have succeeded\n", + info->doc); + goto cleanup; + } + + str = virJSONValueObjectGetString(json, "c"); + if (str) { + if (!info->pass) { + VIR_TEST_VERBOSE("lookup for 'c' in '%s' should have failed\n", + info->doc); + goto cleanup; + } else if (STRNEQ(str, "str")) { + VIR_TEST_VERBOSE("lookup for 'c' in '%s' found '%s' but " + "should have found 'str'\n", info->doc, str); + goto cleanup; + } + } else if (info->pass) { + VIR_TEST_VERBOSE("lookup for 'c' in '%s' should have succeeded\n", + info->doc); + goto cleanup; + } + + value = virJSONValueObjectGetArray(json, "d"); + if (value) { + if (!info->pass) { + VIR_TEST_VERBOSE("lookup for 'd' in '%s' should have failed\n", + info->doc); + goto cleanup; + } else { + result = virJSONValueToString(value, false); + if (STRNEQ_NULLABLE(result, "[]")) { + VIR_TEST_VERBOSE("lookup for 'd' in '%s' found '%s' but " + "should have found '[]'\n", + info->doc, NULLSTR(result)); + goto cleanup; + } + VIR_FREE(result); + } + } else if (info->pass) { + VIR_TEST_VERBOSE("lookup for 'd' in '%s' should have succeeded\n", + info->doc); + goto cleanup; + } + + ret = 0; + + cleanup: + virJSONValueFree(json); + VIR_FREE(result); + return ret; +} + + static int testJSONCopy(const void *data) { @@ -319,6 +427,21 @@ mymain(void) "[ {[\"key1\", \"key2\"]: \"value\"} ]"); DO_TEST_PARSE_FAIL("object with unterminated key", "{ \"key:7 }"); + DO_TEST_FULL("lookup on array", Lookup, + "[ 1 ]", NULL, false); + DO_TEST_FULL("lookup on string", Lookup, + "\"str\"", NULL, false); + DO_TEST_FULL("lookup on integer", Lookup, + "1", NULL, false); + DO_TEST_FULL("lookup with missing key", Lookup, + "{ }", NULL, false); + DO_TEST_FULL("lookup with wrong type", Lookup, + "{ \"a\": 1, \"b\": \"str\", \"c\": [], \"d\": {} }", + NULL, false); + DO_TEST_FULL("lookup with correct type", Lookup, + "{ \"a\": {}, \"b\": 1, \"c\": \"str\", \"d\": [] }", + NULL, true); + return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }