The next patch will add pretty indentation to the JSON visitor.
But in order to support pretty output in the type_any() callback,
we need to prefix every line of the QObject visitor by the current
indentation in the JSON visitor.  Hence, a new function
qobject_to_json_pretty_indent(), and the old function becomes a
thin wrapper to the expanded behavior.

While at it, change 'pretty' to be a bool, to match its usage.

Note that the new simple_pretty() test is a bit sensitive to our
current notion of prettiness, as well as to the hash ordering in
QDict (most of the tests in check-qobject-json intentionally do
not compare the original string to the round-trip string, because
we liberally accept more input forms than the canonical form that
we output).

Signed-off-by: Eric Blake <ebl...@redhat.com>

---
v3: no change
v2: no change
---
 include/qapi/qmp/qobject-json.h |  1 +
 qobject/qobject-json.c          | 40 ++++++++++++++++++-------
 tests/check-qobject-json.c      | 65 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 95 insertions(+), 11 deletions(-)

diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
index 02b1f2c..d420c71 100644
--- a/include/qapi/qmp/qobject-json.h
+++ b/include/qapi/qmp/qobject-json.h
@@ -23,5 +23,6 @@ QObject *qobject_from_jsonv(const char *string, va_list *ap) 
GCC_FMT_ATTR(1, 0);

 QString *qobject_to_json(const QObject *obj);
 QString *qobject_to_json_pretty(const QObject *obj);
+QString *qobject_to_json_pretty_prefix(const QObject *obj, const char *prefix);

 #endif /* QJSON_H */
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 963de07..4469835 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -70,12 +70,14 @@ QObject *qobject_from_jsonf(const char *string, ...)
 typedef struct ToJsonIterState
 {
     int indent;
-    int pretty;
+    bool pretty;
     int count;
     QString *str;
+    const char *prefix;
 } ToJsonIterState;

-static void to_json(const QObject *obj, QString *str, int pretty, int indent);
+static void to_json(const QObject *obj, QString *str, bool pretty, int indent,
+                    const char *prefix);

 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 {
@@ -86,13 +88,13 @@ static void to_json_dict_iter(const char *key, QObject 
*obj, void *opaque)
     }

     if (s->pretty) {
-        qstring_append_format(s->str, "\n%*s", 4 * s->indent, "");
+        qstring_append_format(s->str, "\n%s%*s", s->prefix, 4 * s->indent, "");
     }

     qstring_append_json_string(s->str, key);

     qstring_append(s->str, ": ");
-    to_json(obj, s->str, s->pretty, s->indent);
+    to_json(obj, s->str, s->pretty, s->indent, s->prefix);
     s->count++;
 }

@@ -105,14 +107,15 @@ static void to_json_list_iter(QObject *obj, void *opaque)
     }

     if (s->pretty) {
-        qstring_append_format(s->str, "\n%*s", 4 * s->indent, "");
+        qstring_append_format(s->str, "\n%s%*s", s->prefix, 4 * s->indent, "");
     }

-    to_json(obj, s->str, s->pretty, s->indent);
+    to_json(obj, s->str, s->pretty, s->indent, s->prefix);
     s->count++;
 }

-static void to_json(const QObject *obj, QString *str, int pretty, int indent)
+static void to_json(const QObject *obj, QString *str, bool pretty, int indent,
+                    const char *prefix)
 {
     switch (qobject_type(obj)) {
     case QTYPE_QNULL:
@@ -136,10 +139,11 @@ static void to_json(const QObject *obj, QString *str, int 
pretty, int indent)
         s.str = str;
         s.indent = indent + 1;
         s.pretty = pretty;
+        s.prefix = prefix;
         qstring_append(str, "{");
         qdict_iter(val, to_json_dict_iter, &s);
         if (pretty) {
-            qstring_append_format(str, "\n%*s", 4 * indent, "");
+            qstring_append_format(str, "\n%s%*s", prefix, 4 * indent, "");
         }
         qstring_append(str, "}");
         break;
@@ -152,10 +156,11 @@ static void to_json(const QObject *obj, QString *str, int 
pretty, int indent)
         s.str = str;
         s.indent = indent + 1;
         s.pretty = pretty;
+        s.prefix = prefix;
         qstring_append(str, "[");
         qlist_iter(val, (void *)to_json_list_iter, &s);
         if (pretty) {
-            qstring_append_format(str, "\n%*s", 4 * indent, "");
+            qstring_append_format(str, "\n%s%*s", prefix, 4 * indent, "");
         }
         qstring_append(str, "]");
         break;
@@ -186,7 +191,7 @@ QString *qobject_to_json(const QObject *obj)
 {
     QString *str = qstring_new();

-    to_json(obj, str, 0, 0);
+    to_json(obj, str, false, 0, "");

     return str;
 }
@@ -195,7 +200,20 @@ QString *qobject_to_json_pretty(const QObject *obj)
 {
     QString *str = qstring_new();

-    to_json(obj, str, 1, 0);
+    to_json(obj, str, true, 0, "");
+
+    return str;
+}
+
+/*
+ * Pretty-print JSON representing obj, inserting prefix after every newline.
+ * Note that prefix is NOT applied before the first character.
+ */
+QString *qobject_to_json_pretty_prefix(const QObject *obj, const char *prefix)
+{
+    QString *str = qstring_new();
+
+    to_json(obj, str, true, 0, prefix);

     return str;
 }
diff --git a/tests/check-qobject-json.c b/tests/check-qobject-json.c
index d889501..96bb017 100644
--- a/tests/check-qobject-json.c
+++ b/tests/check-qobject-json.c
@@ -1388,6 +1388,70 @@ static void simple_whitespace(void)
     }
 }

+static void simple_pretty(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        LiteralQObject decoded;
+    } test_cases[] = {
+        {
+            /* The first line is intentionally not prefixed. */
+            .encoded =
+            "[\n"
+            "        43,\n"
+            "        42\n"
+            "    ]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        {
+            .encoded =
+            "[\n"
+            "        43,\n"
+            "        {\n"
+            "            \"a\": 32,\n"
+            "            \"h\": \"b\"\n"
+            "        },\n"
+            "        [\n"
+            "        ],\n"
+            "        42\n"
+            "    ]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QDICT(((LiteralQDictEntry[]){
+                                    { "a", QLIT_QINT(32) },
+                                    { "h", QLIT_QSTR("b") },
+                                    { } })),
+                        QLIT_QLIST(((LiteralQObject[]){
+                                    { } })),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        { }
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QLIST);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+        str = qobject_to_json_pretty_prefix(obj, "    ");
+        qobject_decref(obj);
+        g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded);
+        QDECREF(str);
+    }
+}
+
 static void simple_varargs(void)
 {
     QObject *embedded_obj;
@@ -1525,6 +1589,7 @@ int main(int argc, char **argv)
     g_test_add_func("/lists/simple_list", simple_list);

     g_test_add_func("/whitespace/simple_whitespace", simple_whitespace);
+    g_test_add_func("/whitespace/pretty", simple_pretty);

     g_test_add_func("/varargs/simple_varargs", simple_varargs);

-- 
2.5.5


Reply via email to