xref: /qemu/qobject/qjson.c (revision 6e9965d4)
1 /*
2  * QObject JSON integration
3  *
4  * Copyright IBM, Corp. 2009
5  *
6  * Authors:
7  *  Anthony Liguori   <aliguori@us.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "qapi/qmp/json-lexer.h"
16 #include "qapi/qmp/json-parser.h"
17 #include "qapi/qmp/json-streamer.h"
18 #include "qapi/qmp/qjson.h"
19 #include "qapi/qmp/qint.h"
20 #include "qapi/qmp/qlist.h"
21 #include "qapi/qmp/qbool.h"
22 #include "qapi/qmp/qfloat.h"
23 #include "qapi/qmp/qdict.h"
24 
25 typedef struct JSONParsingState
26 {
27     JSONMessageParser parser;
28     va_list *ap;
29     QObject *result;
30 } JSONParsingState;
31 
32 static void parse_json(JSONMessageParser *parser, GQueue *tokens)
33 {
34     JSONParsingState *s = container_of(parser, JSONParsingState, parser);
35     s->result = json_parser_parse(tokens, s->ap);
36 }
37 
38 QObject *qobject_from_jsonv(const char *string, va_list *ap)
39 {
40     JSONParsingState state = {};
41 
42     state.ap = ap;
43 
44     json_message_parser_init(&state.parser, parse_json);
45     json_message_parser_feed(&state.parser, string, strlen(string));
46     json_message_parser_flush(&state.parser);
47     json_message_parser_destroy(&state.parser);
48 
49     return state.result;
50 }
51 
52 QObject *qobject_from_json(const char *string)
53 {
54     return qobject_from_jsonv(string, NULL);
55 }
56 
57 /*
58  * IMPORTANT: This function aborts on error, thus it must not
59  * be used with untrusted arguments.
60  */
61 QObject *qobject_from_jsonf(const char *string, ...)
62 {
63     QObject *obj;
64     va_list ap;
65 
66     va_start(ap, string);
67     obj = qobject_from_jsonv(string, &ap);
68     va_end(ap);
69 
70     assert(obj != NULL);
71     return obj;
72 }
73 
74 typedef struct ToJsonIterState
75 {
76     int indent;
77     int pretty;
78     int count;
79     QString *str;
80 } ToJsonIterState;
81 
82 static void to_json(const QObject *obj, QString *str, int pretty, int indent);
83 
84 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
85 {
86     ToJsonIterState *s = opaque;
87     QString *qkey;
88     int j;
89 
90     if (s->count) {
91         qstring_append(s->str, s->pretty ? "," : ", ");
92     }
93 
94     if (s->pretty) {
95         qstring_append(s->str, "\n");
96         for (j = 0 ; j < s->indent ; j++)
97             qstring_append(s->str, "    ");
98     }
99 
100     qkey = qstring_from_str(key);
101     to_json(QOBJECT(qkey), s->str, s->pretty, s->indent);
102     QDECREF(qkey);
103 
104     qstring_append(s->str, ": ");
105     to_json(obj, s->str, s->pretty, s->indent);
106     s->count++;
107 }
108 
109 static void to_json_list_iter(QObject *obj, void *opaque)
110 {
111     ToJsonIterState *s = opaque;
112     int j;
113 
114     if (s->count) {
115         qstring_append(s->str, s->pretty ? "," : ", ");
116     }
117 
118     if (s->pretty) {
119         qstring_append(s->str, "\n");
120         for (j = 0 ; j < s->indent ; j++)
121             qstring_append(s->str, "    ");
122     }
123 
124     to_json(obj, s->str, s->pretty, s->indent);
125     s->count++;
126 }
127 
128 static void to_json(const QObject *obj, QString *str, int pretty, int indent)
129 {
130     switch (qobject_type(obj)) {
131     case QTYPE_QNULL:
132         qstring_append(str, "null");
133         break;
134     case QTYPE_QINT: {
135         QInt *val = qobject_to_qint(obj);
136         char buffer[1024];
137 
138         snprintf(buffer, sizeof(buffer), "%" PRId64, qint_get_int(val));
139         qstring_append(str, buffer);
140         break;
141     }
142     case QTYPE_QSTRING: {
143         QString *val = qobject_to_qstring(obj);
144         const char *ptr;
145         int cp;
146         char buf[16];
147         char *end;
148 
149         ptr = qstring_get_str(val);
150         qstring_append(str, "\"");
151 
152         for (; *ptr; ptr = end) {
153             cp = mod_utf8_codepoint(ptr, 6, &end);
154             switch (cp) {
155             case '\"':
156                 qstring_append(str, "\\\"");
157                 break;
158             case '\\':
159                 qstring_append(str, "\\\\");
160                 break;
161             case '\b':
162                 qstring_append(str, "\\b");
163                 break;
164             case '\f':
165                 qstring_append(str, "\\f");
166                 break;
167             case '\n':
168                 qstring_append(str, "\\n");
169                 break;
170             case '\r':
171                 qstring_append(str, "\\r");
172                 break;
173             case '\t':
174                 qstring_append(str, "\\t");
175                 break;
176             default:
177                 if (cp < 0) {
178                     cp = 0xFFFD; /* replacement character */
179                 }
180                 if (cp > 0xFFFF) {
181                     /* beyond BMP; need a surrogate pair */
182                     snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
183                              0xD800 + ((cp - 0x10000) >> 10),
184                              0xDC00 + ((cp - 0x10000) & 0x3FF));
185                 } else if (cp < 0x20 || cp >= 0x7F) {
186                     snprintf(buf, sizeof(buf), "\\u%04X", cp);
187                 } else {
188                     buf[0] = cp;
189                     buf[1] = 0;
190                 }
191                 qstring_append(str, buf);
192             }
193         };
194 
195         qstring_append(str, "\"");
196         break;
197     }
198     case QTYPE_QDICT: {
199         ToJsonIterState s;
200         QDict *val = qobject_to_qdict(obj);
201 
202         s.count = 0;
203         s.str = str;
204         s.indent = indent + 1;
205         s.pretty = pretty;
206         qstring_append(str, "{");
207         qdict_iter(val, to_json_dict_iter, &s);
208         if (pretty) {
209             int j;
210             qstring_append(str, "\n");
211             for (j = 0 ; j < indent ; j++)
212                 qstring_append(str, "    ");
213         }
214         qstring_append(str, "}");
215         break;
216     }
217     case QTYPE_QLIST: {
218         ToJsonIterState s;
219         QList *val = qobject_to_qlist(obj);
220 
221         s.count = 0;
222         s.str = str;
223         s.indent = indent + 1;
224         s.pretty = pretty;
225         qstring_append(str, "[");
226         qlist_iter(val, (void *)to_json_list_iter, &s);
227         if (pretty) {
228             int j;
229             qstring_append(str, "\n");
230             for (j = 0 ; j < indent ; j++)
231                 qstring_append(str, "    ");
232         }
233         qstring_append(str, "]");
234         break;
235     }
236     case QTYPE_QFLOAT: {
237         QFloat *val = qobject_to_qfloat(obj);
238         char buffer[1024];
239         int len;
240 
241         /* FIXME: snprintf() is locale dependent; but JSON requires
242          * numbers to be formatted as if in the C locale. Dependence
243          * on C locale is a pervasive issue in QEMU. */
244         /* FIXME: This risks printing Inf or NaN, which are not valid
245          * JSON values. */
246         /* FIXME: the default precision of 6 for %f often causes
247          * rounding errors; we should be using DBL_DECIMAL_DIG (17),
248          * and only rounding to a shorter number if the result would
249          * still produce the same floating point value.  */
250         len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val));
251         while (len > 0 && buffer[len - 1] == '0') {
252             len--;
253         }
254 
255         if (len && buffer[len - 1] == '.') {
256             buffer[len - 1] = 0;
257         } else {
258             buffer[len] = 0;
259         }
260 
261         qstring_append(str, buffer);
262         break;
263     }
264     case QTYPE_QBOOL: {
265         QBool *val = qobject_to_qbool(obj);
266 
267         if (qbool_get_bool(val)) {
268             qstring_append(str, "true");
269         } else {
270             qstring_append(str, "false");
271         }
272         break;
273     }
274     default:
275         abort();
276     }
277 }
278 
279 QString *qobject_to_json(const QObject *obj)
280 {
281     QString *str = qstring_new();
282 
283     to_json(obj, str, 0, 0);
284 
285     return str;
286 }
287 
288 QString *qobject_to_json_pretty(const QObject *obj)
289 {
290     QString *str = qstring_new();
291 
292     to_json(obj, str, 1, 0);
293 
294     return str;
295 }
296