mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-03 04:45:46 +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 retobj = opaque;
|
||||||
virJSONValuePtr newval = NULL;
|
virJSONValuePtr newval = NULL;
|
||||||
const char *newkey;
|
virJSONValuePtr existobj;
|
||||||
|
char **tokens = NULL;
|
||||||
|
size_t ntokens = 0;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
if (!(newkey = STRSKIP(key, "file."))) {
|
/* non-nested keys only need to be copied */
|
||||||
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
if (!strchr(key, '.')) {
|
||||||
_("JSON object is neither nested nor flattened"));
|
if (!(newval = virJSONValueCopy(value)))
|
||||||
return -1;
|
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)))
|
if (!(tokens = virStringSplitCount(key, ".", 2, &ntokens)))
|
||||||
return -1;
|
goto cleanup;
|
||||||
|
|
||||||
if (virJSONValueObjectAppend(retobj, newkey, newval) < 0) {
|
if (ntokens != 2) {
|
||||||
virJSONValueFree(newval);
|
virReportError(VIR_ERR_INVALID_ARG,
|
||||||
return -1;
|
_("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
|
* 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
|
* hierarchy so that the parsers can be kept simple and we still can use the
|
||||||
* weird syntax some users might use.
|
* 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
|
virJSONValuePtr
|
||||||
virJSONValueObjectDeflatten(virJSONValuePtr json)
|
virJSONValueObjectDeflatten(virJSONValuePtr json)
|
||||||
@ -2023,10 +2057,7 @@ virJSONValueObjectDeflatten(virJSONValuePtr json)
|
|||||||
deflattened) < 0)
|
deflattened) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (virJSONValueObjectCreate(&ret, "a:file", deflattened, NULL) < 0)
|
VIR_STEAL_PTR(ret, deflattened);
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
deflattened = NULL;
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
virJSONValueFree(deflattened);
|
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": {
|
"file": {
|
||||||
"nest": {
|
"nest": {
|
||||||
|
"into": 2,
|
||||||
},
|
"there": "too"
|
||||||
"nest.into": 2,
|
}
|
||||||
"nest.there": "too"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,23 @@
|
|||||||
{
|
{
|
||||||
"file": {
|
"file": {
|
||||||
"double.nest1": "some",
|
"double": {
|
||||||
"double.nest2": "more",
|
"nest1": "some",
|
||||||
"double.nest3": {
|
"nest2": "more",
|
||||||
"even": "objects"
|
"nest3": {
|
||||||
|
"even": "objects"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"very.deeply.nested.object.chains.nest1": "some",
|
"very": {
|
||||||
"very.deeply.nested.object.chains.nest2": "stuff"
|
"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("unflattened", true);
|
||||||
DO_TEST_DEFLATTEN("basic-file", 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-file", true);
|
||||||
DO_TEST_DEFLATTEN("deep-generic", false);
|
DO_TEST_DEFLATTEN("deep-generic", true);
|
||||||
DO_TEST_DEFLATTEN("nested", 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", true);
|
||||||
DO_TEST_DEFLATTEN("concat-double-key", true);
|
DO_TEST_DEFLATTEN("concat-double-key", false);
|
||||||
|
|
||||||
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user