[libvirt] [PATCH 6/9] util: json: Properly implement JSON deflattening

Peter Krempa pkrempa at redhat.com
Tue Jun 27 12:46:47 UTC 2017


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 deflatens now a single object fully even with nested
keys.

Additionally it's not necessary now to stick with the "file." prefix for
the properties.
---
 src/util/virjson.c                                 | 67 ++++++++++++++++------
 tests/virjsondata/deflatten-basic-generic-out.json | 20 +++++++
 .../deflatten-concat-double-key-out.json           |  9 ---
 tests/virjsondata/deflatten-concat-out.json        |  7 +--
 tests/virjsondata/deflatten-deep-file-out.json     | 24 ++++++--
 tests/virjsondata/deflatten-deep-generic-out.json  | 27 +++++++++
 tests/virjsondata/deflatten-double-key-out.json    |  6 --
 tests/virjsontest.c                                |  8 +--
 8 files changed, 121 insertions(+), 47 deletions(-)
 create mode 100644 tests/virjsondata/deflatten-basic-generic-out.json
 delete mode 100644 tests/virjsondata/deflatten-concat-double-key-out.json
 create mode 100644 tests/virjsondata/deflatten-deep-generic-out.json
 delete mode 100644 tests/virjsondata/deflatten-double-key-out.json

diff --git a/src/util/virjson.c b/src/util/virjson.c
index a8e28cd1b..635b78e3a 100644
--- a/src/util/virjson.c
+++ b/src/util/virjson.c
@@ -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);
diff --git a/tests/virjsondata/deflatten-basic-generic-out.json b/tests/virjsondata/deflatten-basic-generic-out.json
new file mode 100644
index 000000000..ab639aa48
--- /dev/null
+++ b/tests/virjsondata/deflatten-basic-generic-out.json
@@ -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"
+    }
+  }
+}
diff --git a/tests/virjsondata/deflatten-concat-double-key-out.json b/tests/virjsondata/deflatten-concat-double-key-out.json
deleted file mode 100644
index 5624ef123..000000000
--- a/tests/virjsondata/deflatten-concat-double-key-out.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "file": {
-    "nest": {
-      "into": "is already here"
-    },
-    "nest.into": 2,
-    "nest.there": "too"
-  }
-}
diff --git a/tests/virjsondata/deflatten-concat-out.json b/tests/virjsondata/deflatten-concat-out.json
index 539d2cc30..be417e53a 100644
--- a/tests/virjsondata/deflatten-concat-out.json
+++ b/tests/virjsondata/deflatten-concat-out.json
@@ -1,9 +1,8 @@
 {
   "file": {
     "nest": {
-
-    },
-    "nest.into": 2,
-    "nest.there": "too"
+      "into": 2,
+      "there": "too"
+    }
   }
 }
diff --git a/tests/virjsondata/deflatten-deep-file-out.json b/tests/virjsondata/deflatten-deep-file-out.json
index a5910c9f7..d4614eeaf 100644
--- a/tests/virjsondata/deflatten-deep-file-out.json
+++ b/tests/virjsondata/deflatten-deep-file-out.json
@@ -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"
+            }
+          }
+        }
+      }
+    }
   }
 }
diff --git a/tests/virjsondata/deflatten-deep-generic-out.json b/tests/virjsondata/deflatten-deep-generic-out.json
new file mode 100644
index 000000000..7ea521a8f
--- /dev/null
+++ b/tests/virjsondata/deflatten-deep-generic-out.json
@@ -0,0 +1,27 @@
+{
+  "foo": {
+    "double": {
+      "nest1": "some",
+      "nest2": "more"
+    },
+    "very": {
+      "deeply": {
+        "nested": {
+          "object": {
+            "chains": {
+              "nest1": "some",
+              "nest2": "stuff"
+            }
+          }
+        }
+      }
+    }
+  },
+  "bar": {
+    "double": {
+      "nest3": {
+        "even": "objects"
+      }
+    }
+  }
+}
diff --git a/tests/virjsondata/deflatten-double-key-out.json b/tests/virjsondata/deflatten-double-key-out.json
deleted file mode 100644
index ca6766e5b..000000000
--- a/tests/virjsondata/deflatten-double-key-out.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "file": {
-    "nest": 1,
-    "nest.into": 2
-  }
-}
diff --git a/tests/virjsontest.c b/tests/virjsontest.c
index d69b22bf3..b3ce6591a 100644
--- a/tests/virjsontest.c
+++ b/tests/virjsontest.c
@@ -514,13 +514,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;
 }
-- 
2.12.2




More information about the libvir-list mailing list