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