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