mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 05:35:25 +00:00
util: json: Properly implement JSON deflattening
As it turns out sometimes users pass in an arbitrarily nested structure e.g. for the qemu backing chains JSON pseudo protocol. This new implementation deflattens now a single object fully even with nested keys. Additionally it's not necessary now to stick with the "file." prefix for the properties.
This commit is contained in:
parent
7f1209ad1e
commit
d40f4b3e67
@ -1974,23 +1974,60 @@ virJSONValueObjectDeflattenWorker(const char *key,
|
||||
{
|
||||
virJSONValuePtr retobj = opaque;
|
||||
virJSONValuePtr newval = NULL;
|
||||
const char *newkey;
|
||||
virJSONValuePtr existobj;
|
||||
char **tokens = NULL;
|
||||
size_t ntokens = 0;
|
||||
int ret = -1;
|
||||
|
||||
if (!(newkey = STRSKIP(key, "file."))) {
|
||||
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||
_("JSON object is neither nested nor flattened"));
|
||||
return -1;
|
||||
/* non-nested keys only need to be copied */
|
||||
if (!strchr(key, '.')) {
|
||||
if (!(newval = virJSONValueCopy(value)))
|
||||
return -1;
|
||||
|
||||
if (virJSONValueObjectHasKey(retobj, key)) {
|
||||
virReportError(VIR_ERR_INVALID_ARG,
|
||||
_("can't deflatten colliding key '%s'"), key);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virJSONValueObjectAppend(retobj, key, newval) < 0)
|
||||
goto cleanup;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(newval = virJSONValueCopy(value)))
|
||||
return -1;
|
||||
if (!(tokens = virStringSplitCount(key, ".", 2, &ntokens)))
|
||||
goto cleanup;
|
||||
|
||||
if (virJSONValueObjectAppend(retobj, newkey, newval) < 0) {
|
||||
virJSONValueFree(newval);
|
||||
return -1;
|
||||
if (ntokens != 2) {
|
||||
virReportError(VIR_ERR_INVALID_ARG,
|
||||
_("invalid nested value key '%s'"), key);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (!(existobj = virJSONValueObjectGet(retobj, tokens[0]))) {
|
||||
if (!(existobj = virJSONValueNewObject()))
|
||||
goto cleanup;
|
||||
|
||||
if (virJSONValueObjectAppend(retobj, tokens[0], existobj) < 0)
|
||||
goto cleanup;
|
||||
|
||||
} else {
|
||||
if (!virJSONValueIsObject(existobj)) {
|
||||
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||
_("mixing nested objects and values is forbidden in "
|
||||
"JSON deflattening"));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
ret = virJSONValueObjectDeflattenWorker(tokens[1], value, existobj);
|
||||
|
||||
cleanup:
|
||||
virStringListFreeCount(tokens, ntokens);
|
||||
virJSONValueFree(newval);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -2005,9 +2042,6 @@ virJSONValueObjectDeflattenWorker(const char *key,
|
||||
* This function will attempt to reverse the process and provide a nested json
|
||||
* hierarchy so that the parsers can be kept simple and we still can use the
|
||||
* weird syntax some users might use.
|
||||
*
|
||||
* Currently this function will flatten out just the 'file.' prefix into a new
|
||||
* tree. Any other syntax will be rejected.
|
||||
*/
|
||||
virJSONValuePtr
|
||||
virJSONValueObjectDeflatten(virJSONValuePtr json)
|
||||
@ -2023,10 +2057,7 @@ virJSONValueObjectDeflatten(virJSONValuePtr json)
|
||||
deflattened) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virJSONValueObjectCreate(&ret, "a:file", deflattened, NULL) < 0)
|
||||
goto cleanup;
|
||||
|
||||
deflattened = NULL;
|
||||
VIR_STEAL_PTR(ret, deflattened);
|
||||
|
||||
cleanup:
|
||||
virJSONValueFree(deflattened);
|
||||
|
20
tests/virjsondata/deflatten-basic-generic-out.json
Normal file
20
tests/virjsondata/deflatten-basic-generic-out.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"foo": {
|
||||
"int": 1,
|
||||
"string": "string",
|
||||
"object": {
|
||||
"data": "value",
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
"bar": {
|
||||
"int": 1
|
||||
},
|
||||
"blurb": {
|
||||
"string": "string",
|
||||
"object": {
|
||||
"data": "value",
|
||||
"foo": "bar"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"file": {
|
||||
"nest": {
|
||||
"into": "is already here"
|
||||
},
|
||||
"nest.into": 2,
|
||||
"nest.there": "too"
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
{
|
||||
"file": {
|
||||
"nest": {
|
||||
|
||||
},
|
||||
"nest.into": 2,
|
||||
"nest.there": "too"
|
||||
"into": 2,
|
||||
"there": "too"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,23 @@
|
||||
{
|
||||
"file": {
|
||||
"double.nest1": "some",
|
||||
"double.nest2": "more",
|
||||
"double.nest3": {
|
||||
"even": "objects"
|
||||
"double": {
|
||||
"nest1": "some",
|
||||
"nest2": "more",
|
||||
"nest3": {
|
||||
"even": "objects"
|
||||
}
|
||||
},
|
||||
"very.deeply.nested.object.chains.nest1": "some",
|
||||
"very.deeply.nested.object.chains.nest2": "stuff"
|
||||
"very": {
|
||||
"deeply": {
|
||||
"nested": {
|
||||
"object": {
|
||||
"chains": {
|
||||
"nest1": "some",
|
||||
"nest2": "stuff"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
tests/virjsondata/deflatten-deep-generic-out.json
Normal file
27
tests/virjsondata/deflatten-deep-generic-out.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"foo": {
|
||||
"double": {
|
||||
"nest1": "some",
|
||||
"nest2": "more"
|
||||
},
|
||||
"very": {
|
||||
"deeply": {
|
||||
"nested": {
|
||||
"object": {
|
||||
"chains": {
|
||||
"nest1": "some",
|
||||
"nest2": "stuff"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bar": {
|
||||
"double": {
|
||||
"nest3": {
|
||||
"even": "objects"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"file": {
|
||||
"nest": 1,
|
||||
"nest.into": 2
|
||||
}
|
||||
}
|
@ -512,13 +512,13 @@ mymain(void)
|
||||
|
||||
DO_TEST_DEFLATTEN("unflattened", true);
|
||||
DO_TEST_DEFLATTEN("basic-file", true);
|
||||
DO_TEST_DEFLATTEN("basic-generic", false);
|
||||
DO_TEST_DEFLATTEN("basic-generic", true);
|
||||
DO_TEST_DEFLATTEN("deep-file", true);
|
||||
DO_TEST_DEFLATTEN("deep-generic", false);
|
||||
DO_TEST_DEFLATTEN("deep-generic", true);
|
||||
DO_TEST_DEFLATTEN("nested", true);
|
||||
DO_TEST_DEFLATTEN("double-key", true);
|
||||
DO_TEST_DEFLATTEN("double-key", false);
|
||||
DO_TEST_DEFLATTEN("concat", true);
|
||||
DO_TEST_DEFLATTEN("concat-double-key", true);
|
||||
DO_TEST_DEFLATTEN("concat-double-key", false);
|
||||
|
||||
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user