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/error.h"
  16#include "qapi/qmp/json-parser.h"
  17#include "qapi/qmp/qjson.h"
  18#include "qapi/qmp/qbool.h"
  19#include "qapi/qmp/qdict.h"
  20#include "qapi/qmp/qlist.h"
  21#include "qapi/qmp/qnum.h"
  22#include "qapi/qmp/qstring.h"
  23#include "qemu/unicode.h"
  24
  25typedef struct JSONParsingState
  26{
  27    JSONMessageParser parser;
  28    QObject *result;
  29    Error *err;
  30} JSONParsingState;
  31
  32static void consume_json(void *opaque, QObject *json, Error *err)
  33{
  34    JSONParsingState *s = opaque;
  35
  36    assert(!json != !err);
  37    assert(!s->result || !s->err);
  38
  39    if (s->result) {
  40        qobject_unref(s->result);
  41        s->result = NULL;
  42        error_setg(&s->err, "Expecting at most one JSON value");
  43    }
  44    if (s->err) {
  45        qobject_unref(json);
  46        error_free(err);
  47        return;
  48    }
  49    s->result = json;
  50    s->err = err;
  51}
  52
  53/*
  54 * Parse @string as JSON value.
  55 * If @ap is non-null, interpolate %-escapes.
  56 * Takes ownership of %p arguments.
  57 * On success, return the JSON value.
  58 * On failure, store an error through @errp and return NULL.
  59 * Ownership of %p arguments becomes indeterminate then.  To avoid
  60 * leaks, callers passing %p must terminate on error, e.g. by passing
  61 * &error_abort.
  62 */
  63static QObject *qobject_from_jsonv(const char *string, va_list *ap,
  64                                   Error **errp)
  65{
  66    JSONParsingState state = {};
  67
  68    json_message_parser_init(&state.parser, consume_json, &state, ap);
  69    json_message_parser_feed(&state.parser, string, strlen(string));
  70    json_message_parser_flush(&state.parser);
  71    json_message_parser_destroy(&state.parser);
  72
  73    if (!state.result && !state.err) {
  74        error_setg(&state.err, "Expecting a JSON value");
  75    }
  76
  77    error_propagate(errp, state.err);
  78    return state.result;
  79}
  80
  81QObject *qobject_from_json(const char *string, Error **errp)
  82{
  83    return qobject_from_jsonv(string, NULL, errp);
  84}
  85
  86/*
  87 * Parse @string as JSON value with %-escapes interpolated.
  88 * Abort on error.  Do not use with untrusted @string.
  89 * Return the resulting QObject.  It is never null.
  90 */
  91QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap)
  92{
  93    va_list ap_copy;
  94    QObject *obj;
  95
  96    /* va_copy() is needed when va_list is an array type */
  97    va_copy(ap_copy, ap);
  98    obj = qobject_from_jsonv(string, &ap_copy, &error_abort);
  99    va_end(ap_copy);
 100
 101    assert(obj);
 102    return obj;
 103}
 104
 105/*
 106 * Parse @string as JSON value with %-escapes interpolated.
 107 * Abort on error.  Do not use with untrusted @string.
 108 * Return the resulting QObject.  It is never null.
 109 */
 110QObject *qobject_from_jsonf_nofail(const char *string, ...)
 111{
 112    QObject *obj;
 113    va_list ap;
 114
 115    va_start(ap, string);
 116    obj = qobject_from_vjsonf_nofail(string, ap);
 117    va_end(ap);
 118
 119    return obj;
 120}
 121
 122/*
 123 * Parse @string as JSON object with %-escapes interpolated.
 124 * Abort on error.  Do not use with untrusted @string.
 125 * Return the resulting QDict.  It is never null.
 126 */
 127QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap)
 128{
 129    QDict *qdict;
 130
 131    qdict = qobject_to(QDict, qobject_from_vjsonf_nofail(string, ap));
 132    assert(qdict);
 133    return qdict;
 134}
 135
 136/*
 137 * Parse @string as JSON object with %-escapes interpolated.
 138 * Abort on error.  Do not use with untrusted @string.
 139 * Return the resulting QDict.  It is never null.
 140 */
 141QDict *qdict_from_jsonf_nofail(const char *string, ...)
 142{
 143    QDict *qdict;
 144    va_list ap;
 145
 146    va_start(ap, string);
 147    qdict = qdict_from_vjsonf_nofail(string, ap);
 148    va_end(ap);
 149    return qdict;
 150}
 151
 152static void to_json(const QObject *obj, QString *str, int pretty, int indent);
 153
 154static void json_pretty_newline(QString *str, bool pretty, int indent)
 155{
 156    int i;
 157
 158    if (pretty) {
 159        qstring_append(str, "\n");
 160        for (i = 0; i < indent; i++) {
 161            qstring_append(str, "    ");
 162        }
 163    }
 164}
 165
 166static void to_json(const QObject *obj, QString *str, int pretty, int indent)
 167{
 168    switch (qobject_type(obj)) {
 169    case QTYPE_QNULL:
 170        qstring_append(str, "null");
 171        break;
 172    case QTYPE_QNUM: {
 173        QNum *val = qobject_to(QNum, obj);
 174        char *buffer = qnum_to_string(val);
 175        qstring_append(str, buffer);
 176        g_free(buffer);
 177        break;
 178    }
 179    case QTYPE_QSTRING: {
 180        QString *val = qobject_to(QString, obj);
 181        const char *ptr;
 182        int cp;
 183        char buf[16];
 184        char *end;
 185
 186        ptr = qstring_get_str(val);
 187        qstring_append(str, "\"");
 188
 189        for (; *ptr; ptr = end) {
 190            cp = mod_utf8_codepoint(ptr, 6, &end);
 191            switch (cp) {
 192            case '\"':
 193                qstring_append(str, "\\\"");
 194                break;
 195            case '\\':
 196                qstring_append(str, "\\\\");
 197                break;
 198            case '\b':
 199                qstring_append(str, "\\b");
 200                break;
 201            case '\f':
 202                qstring_append(str, "\\f");
 203                break;
 204            case '\n':
 205                qstring_append(str, "\\n");
 206                break;
 207            case '\r':
 208                qstring_append(str, "\\r");
 209                break;
 210            case '\t':
 211                qstring_append(str, "\\t");
 212                break;
 213            default:
 214                if (cp < 0) {
 215                    cp = 0xFFFD; /* replacement character */
 216                }
 217                if (cp > 0xFFFF) {
 218                    /* beyond BMP; need a surrogate pair */
 219                    snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
 220                             0xD800 + ((cp - 0x10000) >> 10),
 221                             0xDC00 + ((cp - 0x10000) & 0x3FF));
 222                } else if (cp < 0x20 || cp >= 0x7F) {
 223                    snprintf(buf, sizeof(buf), "\\u%04X", cp);
 224                } else {
 225                    buf[0] = cp;
 226                    buf[1] = 0;
 227                }
 228                qstring_append(str, buf);
 229            }
 230        };
 231
 232        qstring_append(str, "\"");
 233        break;
 234    }
 235    case QTYPE_QDICT: {
 236        QDict *val = qobject_to(QDict, obj);
 237        const char *comma = pretty ? "," : ", ";
 238        const char *sep = "";
 239        const QDictEntry *entry;
 240        QString *qkey;
 241
 242        qstring_append(str, "{");
 243
 244        for (entry = qdict_first(val);
 245             entry;
 246             entry = qdict_next(val, entry)) {
 247            qstring_append(str, sep);
 248            json_pretty_newline(str, pretty, indent + 1);
 249
 250            qkey = qstring_from_str(qdict_entry_key(entry));
 251            to_json(QOBJECT(qkey), str, pretty, indent + 1);
 252            qobject_unref(qkey);
 253
 254            qstring_append(str, ": ");
 255            to_json(qdict_entry_value(entry), str, pretty, indent + 1);
 256            sep = comma;
 257        }
 258
 259        json_pretty_newline(str, pretty, indent);
 260        qstring_append(str, "}");
 261        break;
 262    }
 263    case QTYPE_QLIST: {
 264        QList *val = qobject_to(QList, obj);
 265        const char *comma = pretty ? "," : ", ";
 266        const char *sep = "";
 267        QListEntry *entry;
 268
 269        qstring_append(str, "[");
 270
 271        QLIST_FOREACH_ENTRY(val, entry) {
 272            qstring_append(str, sep);
 273            json_pretty_newline(str, pretty, indent + 1);
 274            to_json(qlist_entry_obj(entry), str, pretty, indent + 1);
 275            sep = comma;
 276        }
 277
 278        json_pretty_newline(str, pretty, indent);
 279        qstring_append(str, "]");
 280        break;
 281    }
 282    case QTYPE_QBOOL: {
 283        QBool *val = qobject_to(QBool, obj);
 284
 285        if (qbool_get_bool(val)) {
 286            qstring_append(str, "true");
 287        } else {
 288            qstring_append(str, "false");
 289        }
 290        break;
 291    }
 292    default:
 293        abort();
 294    }
 295}
 296
 297QString *qobject_to_json(const QObject *obj)
 298{
 299    QString *str = qstring_new();
 300
 301    to_json(obj, str, 0, 0);
 302
 303    return str;
 304}
 305
 306QString *qobject_to_json_pretty(const QObject *obj)
 307{
 308    QString *str = qstring_new();
 309
 310    to_json(obj, str, 1, 0);
 311
 312    return str;
 313}
 314