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/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#include "qemu/unicode.h"
  25
  26typedef struct JSONParsingState
  27{
  28    JSONMessageParser parser;
  29    va_list *ap;
  30    QObject *result;
  31} JSONParsingState;
  32
  33static void parse_json(JSONMessageParser *parser, GQueue *tokens)
  34{
  35    JSONParsingState *s = container_of(parser, JSONParsingState, parser);
  36    s->result = json_parser_parse(tokens, s->ap);
  37}
  38
  39QObject *qobject_from_jsonv(const char *string, va_list *ap)
  40{
  41    JSONParsingState state = {};
  42
  43    state.ap = ap;
  44
  45    json_message_parser_init(&state.parser, parse_json);
  46    json_message_parser_feed(&state.parser, string, strlen(string));
  47    json_message_parser_flush(&state.parser);
  48    json_message_parser_destroy(&state.parser);
  49
  50    return state.result;
  51}
  52
  53QObject *qobject_from_json(const char *string)
  54{
  55    return qobject_from_jsonv(string, NULL);
  56}
  57
  58/*
  59 * IMPORTANT: This function aborts on error, thus it must not
  60 * be used with untrusted arguments.
  61 */
  62QObject *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);
  69    va_end(ap);
  70
  71    assert(obj != NULL);
  72    return obj;
  73}
  74
  75typedef struct ToJsonIterState
  76{
  77    int indent;
  78    int pretty;
  79    int count;
  80    QString *str;
  81} ToJsonIterState;
  82
  83static void to_json(const QObject *obj, QString *str, int pretty, int indent);
  84
  85static 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
 110static 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
 129static 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
 280QString *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
 289QString *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