]> xenbits.xensource.com Git - libvirt.git/commitdiff
util: json: Properly implement JSON deflattening
authorPeter Krempa <pkrempa@redhat.com>
Tue, 27 Jun 2017 11:48:56 +0000 (13:48 +0200)
committerPeter Krempa <pkrempa@redhat.com>
Tue, 11 Jul 2017 12:13:35 +0000 (14:13 +0200)
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.

src/util/virjson.c
tests/virjsondata/deflatten-basic-generic-out.json [new file with mode: 0644]
tests/virjsondata/deflatten-concat-double-key-out.json [deleted file]
tests/virjsondata/deflatten-concat-out.json
tests/virjsondata/deflatten-deep-file-out.json
tests/virjsondata/deflatten-deep-generic-out.json [new file with mode: 0644]
tests/virjsondata/deflatten-double-key-out.json [deleted file]
tests/virjsontest.c

index d55913a14346afd50c3d4d1d958ac7d521e0115d..665278b0aa89d1eb970576cb0dedd3c989a002ef 100644 (file)
@@ -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 (file)
index 0000000..ab639aa
--- /dev/null
@@ -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 (file)
index 5624ef1..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "file": {
-    "nest": {
-      "into": "is already here"
-    },
-    "nest.into": 2,
-    "nest.there": "too"
-  }
-}
index 539d2cc30619ec67a619d28f230e587136e026c0..be417e53aadc492b803482df4ab2397b9d55425b 100644 (file)
@@ -1,9 +1,8 @@
 {
   "file": {
     "nest": {
-
-    },
-    "nest.into": 2,
-    "nest.there": "too"
+      "into": 2,
+      "there": "too"
+    }
   }
 }
index a5910c9f72660eab9dffde49d8718e45595a3223..d4614eeaf91b9940a0795137a864f1ead7177b31 100644 (file)
@@ -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 (file)
index 0000000..7ea521a
--- /dev/null
@@ -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 (file)
index ca6766e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "file": {
-    "nest": 1,
-    "nest.into": 2
-  }
-}
index e8c4e75f4ad6888401426c948529c3ab4c59dd78..2771cb5cd8beeb163aa63b2369cccdc4c0e8430f 100644 (file)
@@ -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;
 }