* [Qemu-devel] [PATCH 2/3] Provide marshalling mechanism for json
2009-11-11 19:24 [Qemu-devel] [PATCH 1/3] QDict: Introduce qdict_iter() Anthony Liguori
@ 2009-11-11 19:24 ` Anthony Liguori
2009-11-11 19:24 ` [Qemu-devel] [PATCH 3/3] Add test suite for json marshalling Anthony Liguori
1 sibling, 0 replies; 7+ messages in thread
From: Anthony Liguori @ 2009-11-11 19:24 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Luiz Capitulino
This introduces qobject_to_json which will convert a QObject to a JSON string
representation.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
qjson.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
qjson.h | 3 +
2 files changed, 181 insertions(+), 0 deletions(-)
diff --git a/qjson.c b/qjson.c
index 45207f2..7270909 100644
--- a/qjson.c
+++ b/qjson.c
@@ -15,6 +15,11 @@
#include "json-parser.h"
#include "json-streamer.h"
#include "qjson.h"
+#include "qint.h"
+#include "qlist.h"
+#include "qbool.h"
+#include "qfloat.h"
+#include "qdict.h"
typedef struct JSONParsingState
{
@@ -58,3 +63,176 @@ QObject *qobject_from_jsonf(const char *string, ...)
return state.result;
}
+
+typedef struct ToJsonIterState
+{
+ int count;
+ QString *str;
+} ToJsonIterState;
+
+static void to_json(const QObject *obj, QString *str);
+
+static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
+{
+ ToJsonIterState *s = opaque;
+ QString *qkey;
+
+ if (s->count) {
+ qstring_append(s->str, ", ");
+ }
+
+ qkey = qstring_from_str(key);
+ to_json(QOBJECT(qkey), s->str);
+ QDECREF(qkey);
+
+ qstring_append(s->str, ": ");
+ to_json(obj, s->str);
+ s->count++;
+}
+
+static void to_json_list_iter(QObject *obj, void *opaque)
+{
+ ToJsonIterState *s = opaque;
+
+ if (s->count) {
+ qstring_append(s->str, ", ");
+ }
+
+ to_json(obj, s->str);
+ s->count++;
+}
+
+static void to_json(const QObject *obj, QString *str)
+{
+ switch (qobject_type(obj)) {
+ case QTYPE_QINT: {
+ QInt *val = qobject_to_qint(obj);
+ char buffer[1024];
+
+ snprintf(buffer, sizeof(buffer), "%" PRId64, qint_get_int(val));
+ qstring_append(str, buffer);
+ break;
+ }
+ case QTYPE_QSTRING: {
+ QString *val = qobject_to_qstring(obj);
+ const char *ptr;
+
+ ptr = qstring_get_str(val);
+ qstring_append(str, "\"");
+ while (*ptr) {
+ if ((ptr[0] & 0xE0) == 0xE0 &&
+ (ptr[1] & 0x80) && (ptr[2] & 0x80)) {
+ uint16_t wchar;
+ char escape[7];
+
+ wchar = (ptr[0] & 0x0F) << 12;
+ wchar |= (ptr[1] & 0x3F) << 6;
+ wchar |= (ptr[2] & 0x3F);
+ ptr += 2;
+
+ snprintf(escape, sizeof(escape), "\\u%04X", wchar);
+ qstring_append(str, escape);
+ } else if ((ptr[0] & 0xE0) == 0xC0 && (ptr[1] & 0x80)) {
+ uint16_t wchar;
+ char escape[7];
+
+ wchar = (ptr[0] & 0x1F) << 6;
+ wchar |= (ptr[1] & 0x3F);
+ ptr++;
+
+ snprintf(escape, sizeof(escape), "\\u%04X", wchar);
+ qstring_append(str, escape);
+ } else switch (ptr[0]) {
+ case '\"':
+ qstring_append(str, "\\\"");
+ break;
+ case '\\':
+ qstring_append(str, "\\\\");
+ break;
+ case '\b':
+ qstring_append(str, "\\b");
+ break;
+ case '\n':
+ qstring_append(str, "\\n");
+ break;
+ case '\r':
+ qstring_append(str, "\\r");
+ break;
+ case '\t':
+ qstring_append(str, "\\t");
+ break;
+ default: {
+ char buf[2] = { ptr[0], 0 };
+ qstring_append(str, buf);
+ break;
+ }
+ }
+ ptr++;
+ }
+ qstring_append(str, "\"");
+ break;
+ }
+ case QTYPE_QDICT: {
+ ToJsonIterState s;
+ QDict *val = qobject_to_qdict(obj);
+
+ s.count = 0;
+ s.str = str;
+ qstring_append(str, "{");
+ qdict_iter(val, to_json_dict_iter, &s);
+ qstring_append(str, "}");
+ break;
+ }
+ case QTYPE_QLIST: {
+ ToJsonIterState s;
+ QList *val = qobject_to_qlist(obj);
+
+ s.count = 0;
+ s.str = str;
+ qstring_append(str, "[");
+ qlist_iter(val, (void *)to_json_list_iter, &s);
+ qstring_append(str, "]");
+ break;
+ }
+ case QTYPE_QFLOAT: {
+ QFloat *val = qobject_to_qfloat(obj);
+ char buffer[1024];
+ int len;
+
+ len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val));
+ while (len > 0 && buffer[len - 1] == '0') {
+ len--;
+ }
+
+ if (len && buffer[len - 1] == '.') {
+ buffer[len - 1] = 0;
+ } else {
+ buffer[len] = 0;
+ }
+
+ qstring_append(str, buffer);
+ break;
+ }
+ case QTYPE_QBOOL: {
+ QBool *val = qobject_to_qbool(obj);
+
+ if (qbool_get_int(val)) {
+ qstring_append(str, "true");
+ } else {
+ qstring_append(str, "false");
+ }
+ break;
+ }
+ case QTYPE_NONE:
+ break;
+ }
+}
+
+QString *qobject_to_json(const QObject *obj)
+{
+ QString *str = qstring_new();
+
+ to_json(obj, str);
+
+ return str;
+}
diff --git a/qjson.h b/qjson.h
index 38be643..7fce742 100644
--- a/qjson.h
+++ b/qjson.h
@@ -15,9 +15,12 @@
#define QJSON_H
#include "qobject.h"
+#include "qstring.h"
QObject *qobject_from_json(const char *string);
QObject *qobject_from_jsonf(const char *string, ...)
__attribute__((__format__ (__printf__, 1, 2)));
+QString *qobject_to_json(const QObject *obj);
+
#endif /* QJSON_H */
--
1.6.2.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 3/3] Add test suite for json marshalling
2009-11-11 19:24 [Qemu-devel] [PATCH 1/3] QDict: Introduce qdict_iter() Anthony Liguori
2009-11-11 19:24 ` [Qemu-devel] [PATCH 2/3] Provide marshalling mechanism for json Anthony Liguori
@ 2009-11-11 19:24 ` Anthony Liguori
2009-11-13 3:14 ` Jamie Lokier
1 sibling, 1 reply; 7+ messages in thread
From: Anthony Liguori @ 2009-11-11 19:24 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Luiz Capitulino
By reusing the qjson test suite. After checking that we can demarshal, marshal
again and compared to the expected decoded value. This doesn't work so well
for floats because they cannot be accurately represented in decimal but we
try our best.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
check-qjson.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 75 insertions(+), 5 deletions(-)
diff --git a/check-qjson.c b/check-qjson.c
index f763de6..4b591a5 100644
--- a/check-qjson.c
+++ b/check-qjson.c
@@ -27,12 +27,13 @@ START_TEST(escaped_string)
struct {
const char *encoded;
const char *decoded;
+ int skip;
} test_cases[] = {
{ "\"\\\"\"", "\"" },
{ "\"hello world \\\"embedded string\\\"\"",
"hello world \"embedded string\"" },
{ "\"hello world\\nwith new line\"", "hello world\nwith new line" },
- { "\"single byte utf-8 \\u0020\"", "single byte utf-8 " },
+ { "\"single byte utf-8 \\u0020\"", "single byte utf-8 ", .skip = 1 },
{ "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" },
{ "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" },
{}
@@ -50,6 +51,13 @@ START_TEST(escaped_string)
str = qobject_to_qstring(obj);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+ if (test_cases[i].skip == 0) {
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+
+ qobject_decref(obj);
+ }
+
QDECREF(str);
}
}
@@ -80,6 +88,11 @@ START_TEST(simple_string)
str = qobject_to_qstring(obj);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+
+ qobject_decref(obj);
+
QDECREF(str);
}
}
@@ -149,12 +162,13 @@ START_TEST(simple_number)
struct {
const char *encoded;
int64_t decoded;
+ int skip;
} test_cases[] = {
{ "0", 0 },
{ "1234", 1234 },
{ "1", 1 },
{ "-32", -32 },
- { "-0", 0 },
+ { "-0", 0, .skip = 1 },
{ },
};
@@ -168,6 +182,13 @@ START_TEST(simple_number)
qint = qobject_to_qint(obj);
fail_unless(qint_get_int(qint) == test_cases[i].decoded);
+ if (test_cases[i].skip == 0) {
+ QString *str;
+
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+ QDECREF(str);
+ }
QDECREF(qint);
}
@@ -180,11 +201,12 @@ START_TEST(float_number)
struct {
const char *encoded;
double decoded;
+ int skip;
} test_cases[] = {
{ "32.43", 32.43 },
{ "0.222", 0.222 },
{ "-32.12313", -32.12313 },
- { "-32.20e-10", -32.20e-10 },
+ { "-32.20e-10", -32.20e-10, .skip = 1 },
{ },
};
@@ -199,6 +221,14 @@ START_TEST(float_number)
qfloat = qobject_to_qfloat(obj);
fail_unless(qfloat_get_double(qfloat) == test_cases[i].decoded);
+ if (test_cases[i].skip == 0) {
+ QString *str;
+
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+ QDECREF(str);
+ }
+
QDECREF(qfloat);
}
}
@@ -246,6 +276,7 @@ START_TEST(keyword_literal)
{
QObject *obj;
QBool *qbool;
+ QString *str;
obj = qobject_from_json("true");
fail_unless(obj != NULL);
@@ -254,6 +285,10 @@ START_TEST(keyword_literal)
qbool = qobject_to_qbool(obj);
fail_unless(qbool_get_int(qbool) != 0);
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), "true") == 0);
+ QDECREF(str);
+
QDECREF(qbool);
obj = qobject_from_json("false");
@@ -263,6 +298,10 @@ START_TEST(keyword_literal)
qbool = qobject_to_qbool(obj);
fail_unless(qbool_get_int(qbool) == 0);
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), "false") == 0);
+ QDECREF(str);
+
QDECREF(qbool);
obj = qobject_from_jsonf("%i", false);
@@ -385,7 +424,7 @@ START_TEST(simple_dict)
LiteralQObject decoded;
} test_cases[] = {
{
- .encoded = "{\"foo\":42,\"bar\":\"hello world\"}",
+ .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
.decoded = QLIT_QDICT(((LiteralQDictEntry[]){
{ "foo", QLIT_QINT(42) },
{ "bar", QLIT_QSTR("hello world") },
@@ -397,7 +436,7 @@ START_TEST(simple_dict)
{ }
})),
}, {
- .encoded = "{\"foo\":43}",
+ .encoded = "{\"foo\": 43}",
.decoded = QLIT_QDICT(((LiteralQDictEntry[]){
{ "foo", QLIT_QINT(43) },
{ }
@@ -408,6 +447,7 @@ START_TEST(simple_dict)
for (i = 0; test_cases[i].encoded; i++) {
QObject *obj;
+ QString *str;
obj = qobject_from_json(test_cases[i].encoded);
fail_unless(obj != NULL);
@@ -415,7 +455,16 @@ START_TEST(simple_dict)
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+ str = qobject_to_json(obj);
+ qobject_decref(obj);
+
+ obj = qobject_from_json(qstring_get_str(str));
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QDICT);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
qobject_decref(obj);
+ QDECREF(str);
}
}
END_TEST
@@ -453,6 +502,7 @@ START_TEST(simple_list)
for (i = 0; test_cases[i].encoded; i++) {
QObject *obj;
+ QString *str;
obj = qobject_from_json(test_cases[i].encoded);
fail_unless(obj != NULL);
@@ -460,7 +510,16 @@ START_TEST(simple_list)
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+ str = qobject_to_json(obj);
+ qobject_decref(obj);
+
+ obj = qobject_from_json(qstring_get_str(str));
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QLIST);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
qobject_decref(obj);
+ QDECREF(str);
}
}
END_TEST
@@ -512,6 +571,7 @@ START_TEST(simple_whitespace)
for (i = 0; test_cases[i].encoded; i++) {
QObject *obj;
+ QString *str;
obj = qobject_from_json(test_cases[i].encoded);
fail_unless(obj != NULL);
@@ -519,7 +579,17 @@ START_TEST(simple_whitespace)
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+ str = qobject_to_json(obj);
qobject_decref(obj);
+
+ obj = qobject_from_json(qstring_get_str(str));
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QLIST);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+ qobject_decref(obj);
+ QDECREF(str);
}
}
END_TEST
--
1.6.2.5
^ permalink raw reply related [flat|nested] 7+ messages in thread