From 547a7c778afc230a7f7f3ac3efb2816f99a4f893 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 26 Apr 2013 08:59:02 -0600 Subject: [PATCH] json: support removing a value from an object In an upcoming patch, I need the way to safely transfer a nested virJSON object out of its parent container for independent use, even after the parent is freed. * src/util/virjson.h (virJSONValueObjectRemoveKey): New function. (_virJSONObject, _virJSONArray): Use correct type. * src/util/virjson.c (virJSONValueObjectRemoveKey): Implement it. * src/libvirt_private.syms (virjson.h): Export it. * tests/jsontest.c (mymain): Test it. Signed-off-by: Eric Blake --- src/libvirt_private.syms | 1 + src/util/virjson.c | 34 +++++++++++++++- src/util/virjson.h | 10 +++-- tests/jsontest.c | 83 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 121 insertions(+), 7 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 94c02abfe0..cdd0b41baa 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1427,6 +1427,7 @@ virJSONValueObjectGetValue; virJSONValueObjectHasKey; virJSONValueObjectIsNull; virJSONValueObjectKeysNumber; +virJSONValueObjectRemoveKey; virJSONValueToString; diff --git a/src/util/virjson.c b/src/util/virjson.c index 92138d3673..9410b664db 100644 --- a/src/util/virjson.c +++ b/src/util/virjson.c @@ -1,7 +1,7 @@ /* * virjson.c: JSON object parsing/formatting * - * Copyright (C) 2009-2010, 2012 Red Hat, Inc. + * Copyright (C) 2009-2010, 2012-2013 Red Hat, Inc. * Copyright (C) 2009 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -448,6 +448,38 @@ const char *virJSONValueObjectGetKey(virJSONValuePtr object, unsigned int n) return object->data.object.pairs[n].key; } +/* Remove the key-value pair tied to @key out of @object. If @value is + * not NULL, the dropped value object is returned instead of freed. + * Returns 1 on success, 0 if no key was found, and -1 on error. */ +int +virJSONValueObjectRemoveKey(virJSONValuePtr object, const char *key, + virJSONValuePtr *value) +{ + int i; + + if (value) + *value = NULL; + + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + for (i = 0 ; i < object->data.object.npairs ; i++) { + if (STREQ(object->data.object.pairs[i].key, key)) { + if (value) { + *value = object->data.object.pairs[i].value; + object->data.object.pairs[i].value = NULL; + } + VIR_FREE(object->data.object.pairs[i].key); + virJSONValueFree(object->data.object.pairs[i].value); + VIR_DELETE_ELEMENT(object->data.object.pairs, i, + object->data.object.npairs); + return 1; + } + } + + return 0; +} + virJSONValuePtr virJSONValueObjectGetValue(virJSONValuePtr object, unsigned int n) { if (object->type != VIR_JSON_TYPE_OBJECT) diff --git a/src/util/virjson.h b/src/util/virjson.h index 67f4398f43..db11396258 100644 --- a/src/util/virjson.h +++ b/src/util/virjson.h @@ -1,7 +1,7 @@ /* * virjson.h: JSON object parsing/formatting * - * Copyright (C) 2009, 2012 Red Hat, Inc. + * Copyright (C) 2009, 2012-2013 Red Hat, Inc. * Copyright (C) 2009 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -55,12 +55,12 @@ struct _virJSONObjectPair { }; struct _virJSONObject { - unsigned int npairs; + size_t npairs; virJSONObjectPairPtr pairs; }; struct _virJSONArray { - unsigned int nvalues; + size_t nvalues; virJSONValuePtr *values; }; @@ -131,6 +131,10 @@ int virJSONValueObjectAppendNumberDouble(virJSONValuePtr object, const char *key int virJSONValueObjectAppendBoolean(virJSONValuePtr object, const char *key, int boolean); int virJSONValueObjectAppendNull(virJSONValuePtr object, const char *key); +int virJSONValueObjectRemoveKey(virJSONValuePtr object, const char *key, + virJSONValuePtr *value) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + virJSONValuePtr virJSONValueFromString(const char *jsonstring); char *virJSONValueToString(virJSONValuePtr object, bool pretty); diff --git a/tests/jsontest.c b/tests/jsontest.c index 98a60690f8..a37a980238 100644 --- a/tests/jsontest.c +++ b/tests/jsontest.c @@ -11,6 +11,7 @@ struct testInfo { const char *doc; + const char *expect; bool pass; }; @@ -54,20 +55,89 @@ cleanup: } +static int +testJSONAddRemove(const void *data) +{ + const struct testInfo *info = data; + virJSONValuePtr json; + virJSONValuePtr name; + char *result = NULL; + int ret = -1; + + json = virJSONValueFromString(info->doc); + + switch (virJSONValueObjectRemoveKey(json, "name", &name)) { + case 1: + if (!info->pass) { + if (virTestGetVerbose()) + fprintf(stderr, "should not remove from non-object %s\n", + info->doc); + goto cleanup; + } + break; + case -1: + if (!info->pass) + ret = 0; + else if (virTestGetVerbose()) + fprintf(stderr, "Fail to recognize non-object %s\n", info->doc); + goto cleanup; + default: + if (virTestGetVerbose()) + fprintf(stderr, "unexpected result when removing from %s\n", + info->doc); + goto cleanup; + } + if (STRNEQ_NULLABLE(virJSONValueGetString(name), "sample")) { + if (virTestGetVerbose()) + fprintf(stderr, "unexpected value after removing name: %s\n", + NULLSTR(virJSONValueGetString(name))); + goto cleanup; + } + if (virJSONValueObjectRemoveKey(json, "name", NULL)) { + if (virTestGetVerbose()) + fprintf(stderr, "%s", + "unexpected success when removing missing key\n"); + goto cleanup; + } + if (virJSONValueObjectAppendString(json, "newname", "foo") < 0) { + if (virTestGetVerbose()) + fprintf(stderr, "%s", "unexpected failure adding new key\n"); + goto cleanup; + } + if (!(result = virJSONValueToString(json, false))) { + if (virTestGetVerbose()) + fprintf(stderr, "%s", "failed to stringize result\n"); + goto cleanup; + } + if (STRNEQ(info->expect, result)) { + if (virTestGetVerbose()) + virtTestDifference(stderr, info->expect, result); + goto cleanup; + } + ret = 0; + +cleanup: + virJSONValueFree(json); + virJSONValueFree(name); + VIR_FREE(result); + return ret; +} + + static int mymain(void) { int ret = 0; -#define DO_TEST_FULL(name, cmd, doc, pass) \ +#define DO_TEST_FULL(name, cmd, doc, expect, pass) \ do { \ - struct testInfo info = { doc, pass }; \ + struct testInfo info = { doc, expect, pass }; \ if (virtTestRun(name, 1, testJSON ## cmd, &info) < 0) \ ret = -1; \ } while (0) #define DO_TEST_PARSE(name, doc) \ - DO_TEST_FULL(name, FromString, doc, true) + DO_TEST_FULL(name, FromString, doc, NULL, true) DO_TEST_PARSE("Simple", "{\"return\": {}, \"id\": \"libvirt-1\"}"); DO_TEST_PARSE("NotSoSimple", "{\"QMP\": {\"version\": {\"qemu\":" @@ -105,6 +175,13 @@ mymain(void) "\"query-uuid\"}, {\"name\": \"query-migrate\"}, {\"name\": " "\"query-balloon\"}], \"id\": \"libvirt-2\"}"); + DO_TEST_FULL("add and remove", AddRemove, + "{\"name\": \"sample\", \"value\": true}", + "{\"value\":true,\"newname\":\"foo\"}", + true); + DO_TEST_FULL("add and remove", AddRemove, + "[ 1 ]", NULL, false); + return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } -- GitLab