1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2025-03-20 07:59:00 +00:00
libvirt/src/util/virjson.c

2070 lines
50 KiB
C
Raw Normal View History

/*
2012-12-12 17:53:50 +00:00
* virjson.c: JSON object parsing/formatting
*
* Copyright (C) 2009-2010, 2012-2015 Red Hat, Inc.
build: consistently use C99 varargs macros Prior to this patch, there was an inconsistent mix between GNU and C99. For consistency, and potential portability to other compilers, stick with the C99 vararg macro syntax. * src/conf/cpu_conf.c (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/conf/domain_conf.c (virDomainReportError): Likewise. * src/conf/domain_event.c (eventReportError): Likewise. * src/conf/interface_conf.c (virInterfaceReportError): Likewise. * src/conf/network_conf.c (virNetworkReportError): Likewise. * src/conf/node_device_conf.h (virNodeDeviceReportError): Likewise. * src/conf/secret_conf.h (virSecretReportError): Likewise. * src/conf/storage_conf.h (virStorageReportError): Likewise. * src/esx/esx_device_monitor.c (ESX_ERROR): Use C99 rather than GNU vararg macro syntax. * src/esx/esx_driver.c (ESX_ERROR): Likewise. * src/esx/esx_interface_driver.c (ESX_ERROR): Likewise. * src/esx/esx_network_driver.c (ESX_ERROR): Likewise. * src/esx/esx_secret_driver.c (ESX_ERROR): Likewise. * src/esx/esx_storage_driver.c (ESX_ERROR): Likewise. * src/esx/esx_util.c (ESX_ERROR): Likewise. * src/esx/esx_vi.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_methods.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_types.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vmx.c (ESX_ERROR): Likewise. * src/util/hostusb.c (usbReportError): Use C99 rather than GNU vararg macro syntax. * src/util/json.c (virJSONError): Likewise. * src/util/macvtap.c (ReportError): Likewise. * src/util/pci.c (pciReportError): Likewise. * src/util/stats_linux.c (virStatsError): Likewise. * src/util/util.c (virUtilError): Likewise. * src/util/xml.c (virXMLError): Likewise. * src/xen/proxy_internal.c (virProxyError): Use C99 rather than GNU vararg macro syntax. * src/xen/sexpr.c (virSexprError): Likewise. * src/xen/xen_driver.c (xenUnifiedError): Likewise. * src/xen/xen_hypervisor.c (virXenError): Likewise. * src/xen/xen_inotify.c (virXenInotifyError): Likewise. * src/xen/xend_internal.c (virXendError): Likewise. * src/xen/xm_internal.c (xenXMError): Likewise. * src/xen/xs_internal.c (virXenStoreError): Likewise. * src/cpu/cpu.h (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/datatypes.c (virLibConnError): Likewise. * src/interface/netcf_driver.c (interfaceReportError): Likewise. * src/libvirt.c (virLibStreamError): Likewise. * src/lxc/lxc_conf.h (lxcError): Likewise. * src/network/bridge_driver.c (networkReportError): Likewise. * src/nodeinfo.c (nodeReportError): Likewise. * src/opennebula/one_conf.h (oneError): Likewise. * src/openvz/openvz_conf.h (openvzError): Likewise. * src/phyp/phyp_driver.c (PHYP_ERROR): Likewise. * src/qemu/qemu_conf.h (qemuReportError): Likewise. * src/remote/remote_driver.c (errorf): Likewise. * src/security/security_driver.h (virSecurityReportError): Likewise. * src/test/test_driver.c (testError): Likewise. * src/uml/uml_conf.h (umlReportError): Likewise. * src/vbox/vbox_driver.c (vboxError): Likewise. * src/vbox/vbox_tmpl.c (vboxError): Likewise.
2010-03-01 16:38:28 -07:00
* Copyright (C) 2009 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#include <config.h>
2012-12-12 17:53:50 +00:00
#include "virjson.h"
#include "viralloc.h"
#include "virerror.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
#include "virstring.h"
#include "virbuffer.h"
#include "virenum.h"
#include "virbitmap.h"
#if WITH_YAJL
# include <yajl/yajl_gen.h>
# include <yajl/yajl_parse.h>
#endif
/* XXX fixme */
#define VIR_FROM_THIS VIR_FROM_NONE
VIR_LOG_INIT("util.json");
typedef struct _virJSONObject virJSONObject;
typedef struct _virJSONObjectPair virJSONObjectPair;
typedef struct _virJSONArray virJSONArray;
struct _virJSONObjectPair {
char *key;
virJSONValue *value;
};
struct _virJSONObject {
size_t npairs;
virJSONObjectPair *pairs;
};
struct _virJSONArray {
size_t nvalues;
virJSONValue **values;
};
struct _virJSONValue {
int type; /* enum virJSONType */
union {
virJSONObject object;
virJSONArray array;
char *string;
char *number; /* int/float/etc format is context defined so we can't parse it here :-( */
int boolean;
} data;
};
typedef struct _virJSONParserState virJSONParserState;
struct _virJSONParserState {
virJSONValue *value;
char *key;
};
typedef struct _virJSONParser virJSONParser;
struct _virJSONParser {
virJSONValue *head;
virJSONParserState *state;
size_t nstate;
int wrap;
};
virJSONType
virJSONValueGetType(const virJSONValue *value)
{
return value->type;
}
/**
* virJSONValueObjectAddVArgs:
* @objptr: pointer to a pointer to a JSON object to add the values to
* @args: a key-value argument pairs, terminated by NULL
*
* Adds the key-value pairs supplied as variable argument list to @obj.
*
* Keys look like s:name the first letter is a type code:
* Explanation of type codes:
* s: string value, must be non-null
* S: string value, omitted if null
*
* i: signed integer value
* j: signed integer value, error if negative
* k: signed integer value, omitted if negative
* z: signed integer value, omitted if zero
* y: signed integer value, omitted if zero, error if negative
*
* I: signed long integer value
* J: signed long integer value, error if negative
* K: signed long integer value, omitted if negative
* Z: signed long integer value, omitted if zero
* Y: signed long integer value, omitted if zero, error if negative
*
* u: unsigned integer value
* p: unsigned integer value, omitted if zero
*
* U: unsigned long integer value (see below for quirks)
* P: unsigned long integer value, omitted if zero
*
* b: boolean value
* B: boolean value, omitted if false
* T: boolean value specified by a virTristate(Bool|Switch) value, omitted on
* the _ABSENT value
*
* d: double precision floating point number
* n: json null value
*
* The following two cases take a pointer to a pointer to a virJSONValue *. The
* pointer is cleared when the virJSONValue *is stolen into the object.
* a: json object, must be non-NULL
* A: json object, omitted if NULL
*
* m: a bitmap represented as a JSON array, must be non-NULL
* M: a bitmap represented as a JSON array, omitted if NULL
*
* The value corresponds to the selected type.
*
* Returns -1 on error. 1 on success, if at least one key:pair was valid 0
* in case of no error but nothing was filled.
*/
int
virJSONValueObjectAddVArgs(virJSONValue **objptr,
va_list args)
{
g_autoptr(virJSONValue) newobj = NULL;
virJSONValue *obj = *objptr;
char type;
char *key;
int rc;
if (obj == NULL)
newobj = obj = virJSONValueNewObject();
while ((key = va_arg(args, char *)) != NULL) {
if (strlen(key) < 3 || key[1] != ':') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' is too short or malformed"),
key);
return -1;
}
type = key[0];
key += 2;
/* This doesn't support maps, but no command uses those. */
switch (type) {
case 'S':
case 's': {
char *val = va_arg(args, char *);
if (!val) {
if (type == 'S')
continue;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not have null value"),
key);
return -1;
}
rc = virJSONValueObjectAppendString(obj, key, val);
} break;
case 'z':
case 'y':
case 'k':
case 'j':
case 'i': {
int val = va_arg(args, int);
if (val < 0 && (type == 'j' || type == 'y')) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not be negative"),
key);
return -1;
}
if (!val && (type == 'z' || type == 'y'))
continue;
if (val < 0 && type == 'k')
continue;
rc = virJSONValueObjectAppendNumberInt(obj, key, val);
} break;
case 'p':
case 'u': {
unsigned int val = va_arg(args, unsigned int);
if (!val && type == 'p')
continue;
rc = virJSONValueObjectAppendNumberUint(obj, key, val);
} break;
case 'Z':
case 'Y':
case 'K':
case 'J':
case 'I': {
long long val = va_arg(args, long long);
if (val < 0 && (type == 'J' || type == 'Y')) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not be negative"),
key);
return -1;
}
if (!val && (type == 'Z' || type == 'Y'))
continue;
if (val < 0 && type == 'K')
continue;
rc = virJSONValueObjectAppendNumberLong(obj, key, val);
} break;
case 'P':
case 'U': {
/* qemu silently truncates numbers larger than LLONG_MAX,
* so passing the full range of unsigned 64 bit integers
* is not safe here. Pass them as signed 64 bit integers
* instead.
*/
long long val = va_arg(args, long long);
if (!val && type == 'P')
continue;
rc = virJSONValueObjectAppendNumberLong(obj, key, val);
} break;
case 'd': {
double val = va_arg(args, double);
rc = virJSONValueObjectAppendNumberDouble(obj, key, val);
} break;
case 'T':
case 'B':
case 'b': {
int val = va_arg(args, int);
if (!val && type == 'B')
continue;
if (type == 'T') {
if (val == VIR_TRISTATE_BOOL_ABSENT)
continue;
if (val == VIR_TRISTATE_BOOL_NO)
val = 0;
else
val = 1;
}
rc = virJSONValueObjectAppendBoolean(obj, key, val);
} break;
case 'n': {
rc = virJSONValueObjectAppendNull(obj, key);
} break;
case 'A':
case 'a': {
virJSONValue **val = va_arg(args, virJSONValue **);
if (!(*val)) {
if (type == 'A')
continue;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not have null value"),
key);
return -1;
}
rc = virJSONValueObjectAppend(obj, key, val);
} break;
case 'M':
case 'm': {
virBitmap *map = va_arg(args, virBitmap *);
g_autoptr(virJSONValue) jsonMap = virJSONValueNewArray();
ssize_t pos = -1;
if (!map) {
if (type == 'M')
continue;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not have null value"),
key);
return -1;
}
while ((pos = virBitmapNextSetBit(map, pos)) > -1) {
g_autoptr(virJSONValue) newelem = virJSONValueNewNumberLong(pos);
if (virJSONValueArrayAppend(jsonMap, &newelem) < 0)
return -1;
}
if ((rc = virJSONValueObjectAppend(obj, key, &jsonMap)) < 0)
return -1;
} break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported data type '%c' for arg '%s'"), type, key - 2);
return -1;
}
if (rc < 0)
return -1;
}
/* verify that we added at least one key-value pair */
if (virJSONValueObjectKeysNumber(obj) == 0)
return 0;
if (newobj)
*objptr = g_steal_pointer(&newobj);
return 1;
}
int
virJSONValueObjectAdd(virJSONValue **objptr, ...)
{
va_list args;
int ret;
va_start(args, objptr);
ret = virJSONValueObjectAddVArgs(objptr, args);
va_end(args);
return ret;
}
void
virJSONValueFree(virJSONValue *value)
{
size_t i;
if (!value)
return;
switch ((virJSONType) value->type) {
case VIR_JSON_TYPE_OBJECT:
for (i = 0; i < value->data.object.npairs; i++) {
g_free(value->data.object.pairs[i].key);
virJSONValueFree(value->data.object.pairs[i].value);
}
g_free(value->data.object.pairs);
break;
case VIR_JSON_TYPE_ARRAY:
for (i = 0; i < value->data.array.nvalues; i++)
virJSONValueFree(value->data.array.values[i]);
g_free(value->data.array.values);
break;
case VIR_JSON_TYPE_STRING:
g_free(value->data.string);
break;
case VIR_JSON_TYPE_NUMBER:
g_free(value->data.number);
break;
case VIR_JSON_TYPE_BOOLEAN:
case VIR_JSON_TYPE_NULL:
break;
}
g_free(value);
}
void
virJSONValueHashFree(void *opaque)
{
virJSONValueFree(opaque);
}
virJSONValue *
virJSONValueNewString(char *data)
{
virJSONValue *val;
if (!data)
return virJSONValueNewNull();
val = g_new0(virJSONValue, 1);
val->type = VIR_JSON_TYPE_STRING;
val->data.string = data;
return val;
}
/**
* virJSONValueNewNumber:
* @data: string representing the number
*
* Creates a new virJSONValue of VIR_JSON_TYPE_NUMBER type. Note that this
* function takes ownership of @data.
*/
static virJSONValue *
virJSONValueNewNumber(char *data)
{
virJSONValue *val;
val = g_new0(virJSONValue, 1);
val->type = VIR_JSON_TYPE_NUMBER;
val->data.number = data;
return val;
}
virJSONValue *
virJSONValueNewNumberInt(int data)
{
return virJSONValueNewNumber(g_strdup_printf("%i", data));
}
virJSONValue *
virJSONValueNewNumberUint(unsigned int data)
{
return virJSONValueNewNumber(g_strdup_printf("%u", data));
}
virJSONValue *
virJSONValueNewNumberLong(long long data)
{
return virJSONValueNewNumber(g_strdup_printf("%lld", data));
}
virJSONValue *
virJSONValueNewNumberUlong(unsigned long long data)
{
return virJSONValueNewNumber(g_strdup_printf("%llu", data));
}
virJSONValue *
virJSONValueNewNumberDouble(double data)
{
char *str = NULL;
if (virDoubleToStr(&str, data) < 0)
return NULL;
return virJSONValueNewNumber(str);
}
virJSONValue *
virJSONValueNewBoolean(int boolean_)
{
virJSONValue *val;
val = g_new0(virJSONValue, 1);
val->type = VIR_JSON_TYPE_BOOLEAN;
val->data.boolean = boolean_;
return val;
}
virJSONValue *
virJSONValueNewNull(void)
{
virJSONValue *val;
val = g_new0(virJSONValue, 1);
val->type = VIR_JSON_TYPE_NULL;
return val;
}
virJSONValue *
virJSONValueNewArray(void)
{
virJSONValue *val = g_new0(virJSONValue, 1);
val->type = VIR_JSON_TYPE_ARRAY;
return val;
}
virJSONValue *
virJSONValueNewObject(void)
{
virJSONValue *val = g_new0(virJSONValue, 1);
val->type = VIR_JSON_TYPE_OBJECT;
return val;
}
static int
virJSONValueObjectInsert(virJSONValue *object,
const char *key,
virJSONValue **value,
bool prepend)
{
virJSONObjectPair pair = { NULL, *value };
int ret = -1;
if (object->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("expecting JSON object"));
return -1;
}
if (virJSONValueObjectHasKey(object, key)) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("duplicate key '%s'"), key);
return -1;
}
pair.key = g_strdup(key);
if (prepend) {
ret = VIR_INSERT_ELEMENT(object->data.object.pairs, 0,
object->data.object.npairs, pair);
} else {
VIR_APPEND_ELEMENT(object->data.object.pairs,
object->data.object.npairs, pair);
ret = 0;
}
if (ret == 0)
*value = NULL;
VIR_FREE(pair.key);
return ret;
}
int
virJSONValueObjectAppend(virJSONValue *object,
const char *key,
virJSONValue **value)
{
return virJSONValueObjectInsert(object, key, value, false);
}
static int
virJSONValueObjectInsertString(virJSONValue *object,
const char *key,
const char *value,
bool prepend)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewString(g_strdup(value));
return virJSONValueObjectInsert(object, key, &jvalue, prepend);
}
int
virJSONValueObjectAppendString(virJSONValue *object,
const char *key,
const char *value)
{
return virJSONValueObjectInsertString(object, key, value, false);
}
int
virJSONValueObjectAppendStringPrintf(virJSONValue *object,
const char *key,
const char *fmt,
...)
{
va_list ap;
g_autofree char *str = NULL;
va_start(ap, fmt);
str = g_strdup_vprintf(fmt, ap);
va_end(ap);
return virJSONValueObjectInsertString(object, key, str, false);
}
int
virJSONValueObjectPrependString(virJSONValue *object,
const char *key,
const char *value)
{
return virJSONValueObjectInsertString(object, key, value, true);
}
int
virJSONValueObjectAppendNumberInt(virJSONValue *object,
const char *key,
int number)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewNumberInt(number);
if (virJSONValueObjectAppend(object, key, &jvalue) < 0)
return -1;
return 0;
}
int
virJSONValueObjectAppendNumberUint(virJSONValue *object,
const char *key,
unsigned int number)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewNumberUint(number);
if (virJSONValueObjectAppend(object, key, &jvalue) < 0)
return -1;
return 0;
}
int
virJSONValueObjectAppendNumberLong(virJSONValue *object,
const char *key,
long long number)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewNumberLong(number);
if (virJSONValueObjectAppend(object, key, &jvalue) < 0)
return -1;
return 0;
}
int
virJSONValueObjectAppendNumberUlong(virJSONValue *object,
const char *key,
unsigned long long number)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewNumberUlong(number);
if (virJSONValueObjectAppend(object, key, &jvalue) < 0)
return -1;
return 0;
}
int
virJSONValueObjectAppendNumberDouble(virJSONValue *object,
const char *key,
double number)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewNumberDouble(number);
/* virJSONValueNewNumberDouble may return NULL if locale setting fails */
if (!jvalue)
return -1;
if (virJSONValueObjectAppend(object, key, &jvalue) < 0)
return -1;
return 0;
}
int
virJSONValueObjectAppendBoolean(virJSONValue *object,
const char *key,
int boolean_)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewBoolean(boolean_);
if (virJSONValueObjectAppend(object, key, &jvalue) < 0)
return -1;
return 0;
}
int
virJSONValueObjectAppendNull(virJSONValue *object,
const char *key)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewNull();
if (virJSONValueObjectAppend(object, key, &jvalue) < 0)
return -1;
return 0;
}
int
virJSONValueArrayAppend(virJSONValue *array,
virJSONValue **value)
{
if (array->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("expecting JSON array"));
return -1;
}
VIR_REALLOC_N(array->data.array.values, array->data.array.nvalues + 1);
array->data.array.values[array->data.array.nvalues] = g_steal_pointer(value);
array->data.array.nvalues++;
return 0;
}
int
virJSONValueArrayAppendString(virJSONValue *object,
const char *value)
{
g_autoptr(virJSONValue) jvalue = virJSONValueNewString(g_strdup(value));
if (virJSONValueArrayAppend(object, &jvalue) < 0)
return -1;
return 0;
}
/**
* virJSONValueArrayConcat:
* @a: JSON value array (destination)
* @c: JSON value array (source)
*
* Merges the members of @c array into @a. The values are stolen from @c.
*/
int
virJSONValueArrayConcat(virJSONValue *a,
virJSONValue *c)
{
size_t i;
if (a->type != VIR_JSON_TYPE_ARRAY ||
c->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("expecting JSON array"));
return -1;
}
a->data.array.values = g_renew(virJSONValue *, a->data.array.values,
a->data.array.nvalues + c->data.array.nvalues);
for (i = 0; i < c->data.array.nvalues; i++)
a->data.array.values[a->data.array.nvalues++] = g_steal_pointer(&c->data.array.values[i]);
c->data.array.nvalues = 0;
return 0;
}
bool
virJSONValueObjectHasKey(virJSONValue *object,
const char *key)
{
size_t i;
if (object->type != VIR_JSON_TYPE_OBJECT)
return false;
for (i = 0; i < object->data.object.npairs; i++) {
if (STREQ(object->data.object.pairs[i].key, key))
return true;
}
return false;
}
virJSONValue *
virJSONValueObjectGet(virJSONValue *object,
const char *key)
{
size_t i;
if (object->type != VIR_JSON_TYPE_OBJECT)
return NULL;
for (i = 0; i < object->data.object.npairs; i++) {
if (STREQ(object->data.object.pairs[i].key, key))
return object->data.object.pairs[i].value;
}
return NULL;
}
/* Return the value associated with KEY within OBJECT, but return NULL
* if the key is missing or if value is not the correct TYPE. */
virJSONValue *
virJSONValueObjectGetByType(virJSONValue *object,
const char *key,
virJSONType type)
{
virJSONValue *value = virJSONValueObjectGet(object, key);
if (value && value->type == type)
return value;
return NULL;
}
/* Steal the value associated with KEY within OBJECT, but return NULL
* if the key is missing or if value is not the correct TYPE. */
static virJSONValue *
virJSONValueObjectStealByType(virJSONValue *object,
const char *key,
virJSONType type)
{
virJSONValue *value;
if (virJSONValueObjectRemoveKey(object, key, &value) <= 0)
return NULL;
if (value && value->type == type)
return value;
return NULL;
}
int
virJSONValueObjectKeysNumber(virJSONValue *object)
{
if (object->type != VIR_JSON_TYPE_OBJECT)
return -1;
return object->data.object.npairs;
}
const char *
virJSONValueObjectGetKey(virJSONValue *object,
unsigned int n)
{
if (object->type != VIR_JSON_TYPE_OBJECT)
return NULL;
if (n >= object->data.object.npairs)
return NULL;
return object->data.object.pairs[n].key;
}
/* Remove the key-value pair tied to @key out of @object. If @value is
* not NULL, the dropped value object is returned instead of freed.
* Returns 1 on success, 0 if no key was found, and -1 on error. */
int
virJSONValueObjectRemoveKey(virJSONValue *object,
const char *key,
virJSONValue **value)
{
size_t i;
if (value)
*value = NULL;
if (object->type != VIR_JSON_TYPE_OBJECT)
return -1;
for (i = 0; i < object->data.object.npairs; i++) {
if (STREQ(object->data.object.pairs[i].key, key)) {
if (value) {
*value = g_steal_pointer(&object->data.object.pairs[i].value);
}
VIR_FREE(object->data.object.pairs[i].key);
virJSONValueFree(object->data.object.pairs[i].value);
VIR_DELETE_ELEMENT(object->data.object.pairs, i,
object->data.object.npairs);
return 1;
}
}
return 0;
}
virJSONValue *
virJSONValueObjectGetValue(virJSONValue *object,
unsigned int n)
{
if (object->type != VIR_JSON_TYPE_OBJECT)
return NULL;
if (n >= object->data.object.npairs)
return NULL;
return object->data.object.pairs[n].value;
}
bool
virJSONValueIsObject(virJSONValue *object)
{
if (object)
return object->type == VIR_JSON_TYPE_OBJECT;
else
return false;
}
bool
virJSONValueIsArray(virJSONValue *array)
{
return array->type == VIR_JSON_TYPE_ARRAY;
}
size_t
virJSONValueArraySize(const virJSONValue *array)
{
return array->data.array.nvalues;
}
virJSONValue *
virJSONValueArrayGet(virJSONValue *array,
unsigned int element)
{
if (array->type != VIR_JSON_TYPE_ARRAY)
return NULL;
if (element >= array->data.array.nvalues)
return NULL;
return array->data.array.values[element];
}
virJSONValue *
virJSONValueArraySteal(virJSONValue *array,
unsigned int element)
{
virJSONValue *ret = NULL;
if (array->type != VIR_JSON_TYPE_ARRAY)
return NULL;
if (element >= array->data.array.nvalues)
return NULL;
ret = array->data.array.values[element];
VIR_DELETE_ELEMENT(array->data.array.values,
element,
array->data.array.nvalues);
return ret;
}
/**
* virJSONValueArrayForeachSteal:
* @array: array to iterate
* @cb: callback called on every member of the array
* @opaque: custom data for the callback
*
* Iterates members of the array and calls the callback on every single member.
* The return codes of the callback are interpreted as follows:
* 0: callback claims ownership of the array element and is responsible for
* freeing it
* 1: callback doesn't claim ownership of the element
* -1: callback doesn't claim ownership of the element and iteration does not
* continue
*
* Returns 0 if all members were iterated and/or stolen by the callback; -1
* on callback failure or if the JSON value object is not an array.
* The rest of the members stay in possession of the array and it's condensed.
*/
int
virJSONValueArrayForeachSteal(virJSONValue *array,
virJSONArrayIteratorFunc cb,
void *opaque)
{
size_t i;
size_t j = 0;
int ret = 0;
int rc;
if (array->type != VIR_JSON_TYPE_ARRAY)
return -1;
for (i = 0; i < array->data.array.nvalues; i++) {
if ((rc = cb(i, array->data.array.values[i], opaque)) < 0) {
ret = -1;
break;
}
if (rc == 0)
array->data.array.values[i] = NULL;
}
/* condense the remaining entries at the beginning */
for (i = 0; i < array->data.array.nvalues; i++) {
if (!array->data.array.values[i])
continue;
array->data.array.values[j++] = array->data.array.values[i];
}
array->data.array.nvalues = j;
return ret;
}
const char *
virJSONValueGetString(virJSONValue *string)
{
if (string->type != VIR_JSON_TYPE_STRING)
return NULL;
return string->data.string;
}
const char *
virJSONValueGetNumberString(virJSONValue *number)
{
if (number->type != VIR_JSON_TYPE_NUMBER)
return NULL;
return number->data.number;
}
int
virJSONValueGetNumberInt(virJSONValue *number,
int *value)
{
if (number->type != VIR_JSON_TYPE_NUMBER)
return -1;
return virStrToLong_i(number->data.number, NULL, 10, value);
}
int
virJSONValueGetNumberUint(virJSONValue *number,
unsigned int *value)
{
if (number->type != VIR_JSON_TYPE_NUMBER)
return -1;
return virStrToLong_ui(number->data.number, NULL, 10, value);
}
int
virJSONValueGetNumberLong(virJSONValue *number,
long long *value)
{
if (number->type != VIR_JSON_TYPE_NUMBER)
return -1;
return virStrToLong_ll(number->data.number, NULL, 10, value);
}
int
virJSONValueGetNumberUlong(virJSONValue *number,
unsigned long long *value)
{
if (number->type != VIR_JSON_TYPE_NUMBER)
return -1;
return virStrToLong_ull(number->data.number, NULL, 10, value);
}
int
virJSONValueGetNumberDouble(virJSONValue *number,
double *value)
{
if (number->type != VIR_JSON_TYPE_NUMBER)
return -1;
return virStrToDouble(number->data.number, NULL, value);
}
int
virJSONValueGetBoolean(virJSONValue *val,
bool *value)
{
if (val->type != VIR_JSON_TYPE_BOOLEAN)
return -1;
*value = val->data.boolean;
return 0;
}
const char *
virJSONValueObjectGetString(virJSONValue *object,
const char *key)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return NULL;
return virJSONValueGetString(val);
}
void
virJSONValueObjectReplaceValue(virJSONValue *object,
const char *key,
virJSONValue **newval)
{
size_t i;
if (object->type != VIR_JSON_TYPE_OBJECT ||
!*newval)
return;
for (i = 0; i < object->data.object.npairs; i++) {
if (STREQ(object->data.object.pairs[i].key, key)) {
virJSONValueFree(object->data.object.pairs[i].value);
object->data.object.pairs[i].value = g_steal_pointer(newval);
}
}
}
/**
* virJSONValueObjectGetStringOrNumber:
* @object: JSON value object
* @key: name of the property in @object to get
*
* Gets a property named @key from the JSON object @object. The value may be
* a number or a string and is returned as a string. In cases when the property
* is not present or is not a string or number NULL is returned.
*/
const char *
virJSONValueObjectGetStringOrNumber(virJSONValue *object,
const char *key)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return NULL;
if (val->type == VIR_JSON_TYPE_STRING)
return val->data.string;
else if (val->type == VIR_JSON_TYPE_NUMBER)
return val->data.number;
return NULL;
}
int
virJSONValueObjectGetNumberInt(virJSONValue *object,
const char *key,
int *value)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return -1;
return virJSONValueGetNumberInt(val, value);
}
int
virJSONValueObjectGetNumberUint(virJSONValue *object,
const char *key,
unsigned int *value)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return -1;
return virJSONValueGetNumberUint(val, value);
}
int
virJSONValueObjectGetNumberLong(virJSONValue *object,
const char *key,
long long *value)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return -1;
return virJSONValueGetNumberLong(val, value);
}
int
virJSONValueObjectGetNumberUlong(virJSONValue *object,
const char *key,
unsigned long long *value)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return -1;
return virJSONValueGetNumberUlong(val, value);
}
int
virJSONValueObjectGetNumberDouble(virJSONValue *object,
const char *key,
double *value)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return -1;
return virJSONValueGetNumberDouble(val, value);
}
int
virJSONValueObjectGetBoolean(virJSONValue *object,
const char *key,
bool *value)
{
virJSONValue *val = virJSONValueObjectGet(object, key);
if (!val)
return -1;
return virJSONValueGetBoolean(val, value);
}
virJSONValue *
virJSONValueObjectGetObject(virJSONValue *object, const char *key)
{
return virJSONValueObjectGetByType(object, key, VIR_JSON_TYPE_OBJECT);
}
virJSONValue *
virJSONValueObjectGetArray(virJSONValue *object, const char *key)
{
return virJSONValueObjectGetByType(object, key, VIR_JSON_TYPE_ARRAY);
}
virJSONValue *
virJSONValueObjectStealArray(virJSONValue *object, const char *key)
{
return virJSONValueObjectStealByType(object, key, VIR_JSON_TYPE_ARRAY);
}
virJSONValue *
virJSONValueObjectStealObject(virJSONValue *object,
const char *key)
{
return virJSONValueObjectStealByType(object, key, VIR_JSON_TYPE_OBJECT);
}
char **
virJSONValueObjectGetStringArray(virJSONValue *object, const char *key)
{
g_auto(GStrv) ret = NULL;
virJSONValue *data;
size_t n;
size_t i;
data = virJSONValueObjectGetArray(object, key);
if (!data) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s is missing or not an array"),
key);
return NULL;
}
n = virJSONValueArraySize(data);
ret = g_new0(char *, n + 1);
for (i = 0; i < n; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
if (!child) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s array element is missing item %zu"),
key, i);
return NULL;
}
if (!(tmp = virJSONValueGetString(child))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s array element does not contain a string"),
key);
return NULL;
}
ret[i] = g_strdup(tmp);
}
return g_steal_pointer(&ret);
}
/**
* virJSONValueObjectForeachKeyValue:
* @object: JSON object to iterate
* @cb: callback to call on key-value pairs contained in the object
* @opaque: generic data for the callback
*
* Iterates all key=value pairs in @object. Iteration breaks if @cb returns
* negative value.
*
* Returns 0 if all elements were iterated, -2 if @cb returned negative value
* during iteration and -1 on generic errors.
*/
int
virJSONValueObjectForeachKeyValue(virJSONValue *object,
virJSONValueObjectIteratorFunc cb,
void *opaque)
{
size_t i;
if (object->type != VIR_JSON_TYPE_OBJECT)
return -1;
for (i = 0; i < object->data.object.npairs; i++) {
virJSONObjectPair *elem = object->data.object.pairs + i;
if (cb(elem->key, elem->value, opaque) < 0)
return -2;
}
return 0;
}
virJSONValue *
virJSONValueCopy(const virJSONValue *in)
{
size_t i;
virJSONValue *out = NULL;
if (!in)
return NULL;
switch ((virJSONType) in->type) {
case VIR_JSON_TYPE_OBJECT:
out = virJSONValueNewObject();
out->data.object.pairs = g_new0(virJSONObjectPair, in->data.object.npairs);
out->data.object.npairs = in->data.object.npairs;
for (i = 0; i < in->data.object.npairs; i++) {
out->data.object.pairs[i].key = g_strdup(in->data.object.pairs[i].key);
out->data.object.pairs[i].value = virJSONValueCopy(in->data.object.pairs[i].value);
}
break;
case VIR_JSON_TYPE_ARRAY:
out = virJSONValueNewArray();
out->data.array.values = g_new0(virJSONValue *, in->data.array.nvalues);
out->data.array.nvalues = in->data.array.nvalues;
for (i = 0; i < in->data.array.nvalues; i++) {
out->data.array.values[i] = virJSONValueCopy(in->data.array.values[i]);
}
break;
/* No need to error out in the following cases */
case VIR_JSON_TYPE_STRING:
out = virJSONValueNewString(g_strdup(in->data.string));
break;
case VIR_JSON_TYPE_NUMBER:
out = virJSONValueNewNumber(g_strdup(in->data.number));
break;
case VIR_JSON_TYPE_BOOLEAN:
out = virJSONValueNewBoolean(in->data.boolean);
break;
case VIR_JSON_TYPE_NULL:
out = virJSONValueNewNull();
break;
}
return out;
}
#if WITH_YAJL
static int
virJSONParserInsertValue(virJSONParser *parser,
virJSONValue **value)
{
if (!parser->head) {
parser->head = g_steal_pointer(value);
} else {
virJSONParserState *state;
if (!parser->nstate) {
VIR_DEBUG("got a value to insert without a container");
return -1;
}
state = &parser->state[parser->nstate-1];
switch (state->value->type) {
case VIR_JSON_TYPE_OBJECT: {
if (!state->key) {
VIR_DEBUG("missing key when inserting object value");
return -1;
}
if (virJSONValueObjectAppend(state->value,
state->key,
value) < 0)
return -1;
VIR_FREE(state->key);
} break;
case VIR_JSON_TYPE_ARRAY: {
if (state->key) {
VIR_DEBUG("unexpected key when inserting array value");
return -1;
}
if (virJSONValueArrayAppend(state->value,
value) < 0)
return -1;
} break;
default:
VIR_DEBUG("unexpected value type, not a container");
return -1;
}
}
return 0;
}
static int
virJSONParserHandleNull(void *ctx)
{
virJSONParser *parser = ctx;
g_autoptr(virJSONValue) value = virJSONValueNewNull();
VIR_DEBUG("parser=%p", parser);
if (virJSONParserInsertValue(parser, &value) < 0)
return 0;
return 1;
}
static int
virJSONParserHandleBoolean(void *ctx,
int boolean_)
{
virJSONParser *parser = ctx;
g_autoptr(virJSONValue) value = virJSONValueNewBoolean(boolean_);
VIR_DEBUG("parser=%p boolean=%d", parser, boolean_);
if (virJSONParserInsertValue(parser, &value) < 0)
return 0;
return 1;
}
static int
virJSONParserHandleNumber(void *ctx,
const char *s,
size_t l)
{
virJSONParser *parser = ctx;
g_autoptr(virJSONValue) value = virJSONValueNewNumber(g_strndup(s, l));
VIR_DEBUG("parser=%p str=%s", parser, value->data.number);
if (virJSONParserInsertValue(parser, &value) < 0)
return 0;
return 1;
}
static int
virJSONParserHandleString(void *ctx,
const unsigned char *stringVal,
size_t stringLen)
{
virJSONParser *parser = ctx;
g_autoptr(virJSONValue) value = virJSONValueNewString(g_strndup((const char *)stringVal, stringLen));
VIR_DEBUG("parser=%p str=%p", parser, (const char *)stringVal);
if (virJSONParserInsertValue(parser, &value) < 0)
return 0;
return 1;
}
static int
virJSONParserHandleMapKey(void *ctx,
const unsigned char *stringVal,
size_t stringLen)
{
virJSONParser *parser = ctx;
virJSONParserState *state;
VIR_DEBUG("parser=%p key=%p", parser, (const char *)stringVal);
if (!parser->nstate)
return 0;
state = &parser->state[parser->nstate-1];
if (state->key)
return 0;
state->key = g_strndup((const char *)stringVal, stringLen);
return 1;
}
static int
virJSONParserHandleStartMap(void *ctx)
{
virJSONParser *parser = ctx;
g_autoptr(virJSONValue) value = virJSONValueNewObject();
virJSONValue *tmp = value;
VIR_DEBUG("parser=%p", parser);
if (virJSONParserInsertValue(parser, &value) < 0)
return 0;
VIR_REALLOC_N(parser->state, parser->nstate + 1);
parser->state[parser->nstate].value = tmp;
parser->state[parser->nstate].key = NULL;
parser->nstate++;
return 1;
}
static int
virJSONParserHandleEndMap(void *ctx)
{
virJSONParser *parser = ctx;
virJSONParserState *state;
VIR_DEBUG("parser=%p", parser);
if (!parser->nstate)
return 0;
state = &(parser->state[parser->nstate-1]);
if (state->key) {
VIR_FREE(state->key);
return 0;
}
VIR_DELETE_ELEMENT(parser->state, parser->nstate - 1, parser->nstate);
return 1;
}
static int
virJSONParserHandleStartArray(void *ctx)
{
virJSONParser *parser = ctx;
g_autoptr(virJSONValue) value = virJSONValueNewArray();
virJSONValue *tmp = value;
VIR_DEBUG("parser=%p", parser);
if (virJSONParserInsertValue(parser, &value) < 0)
return 0;
VIR_REALLOC_N(parser->state, parser->nstate + 1);
parser->state[parser->nstate].value = tmp;
parser->state[parser->nstate].key = NULL;
parser->nstate++;
return 1;
}
static int
virJSONParserHandleEndArray(void *ctx)
{
virJSONParser *parser = ctx;
virJSONParserState *state;
VIR_DEBUG("parser=%p", parser);
if (!(parser->nstate - parser->wrap))
return 0;
state = &(parser->state[parser->nstate-1]);
if (state->key) {
VIR_FREE(state->key);
return 0;
}
VIR_DELETE_ELEMENT(parser->state, parser->nstate - 1, parser->nstate);
return 1;
}
static const yajl_callbacks parserCallbacks = {
virJSONParserHandleNull,
virJSONParserHandleBoolean,
NULL,
NULL,
virJSONParserHandleNumber,
virJSONParserHandleString,
virJSONParserHandleStartMap,
virJSONParserHandleMapKey,
virJSONParserHandleEndMap,
virJSONParserHandleStartArray,
virJSONParserHandleEndArray
};
/* XXX add an incremental streaming parser - yajl trivially supports it */
virJSONValue *
virJSONValueFromString(const char *jsonstring)
{
yajl_handle hand;
virJSONParser parser = { NULL, NULL, 0, 0 };
virJSONValue *ret = NULL;
int rc;
size_t len = strlen(jsonstring);
VIR_DEBUG("string=%s", jsonstring);
hand = yajl_alloc(&parserCallbacks, NULL, &parser);
if (!hand) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to create JSON parser"));
return NULL;
}
/* Yajl 2 is nice enough to default to rejecting trailing garbage. */
rc = yajl_parse(hand, (const unsigned char *)jsonstring, len);
if (rc != yajl_status_ok ||
yajl_complete_parse(hand) != yajl_status_ok) {
unsigned char *errstr = yajl_get_error(hand, 1,
(const unsigned char*)jsonstring,
strlen(jsonstring));
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse json %s: %s"),
jsonstring, (const char*) errstr);
yajl_free_error(hand, errstr);
virJSONValueFree(parser.head);
goto cleanup;
}
if (parser.nstate != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse json %s: unterminated string/map/array"),
jsonstring);
virJSONValueFree(parser.head);
} else {
ret = parser.head;
}
cleanup:
yajl_free(hand);
if (parser.nstate) {
size_t i;
for (i = 0; i < parser.nstate; i++)
VIR_FREE(parser.state[i].key);
VIR_FREE(parser.state);
}
VIR_DEBUG("result=%p", ret);
return ret;
}
static int
virJSONValueToStringOne(virJSONValue *object,
yajl_gen g)
{
size_t i;
VIR_DEBUG("object=%p type=%d gen=%p", object, object->type, g);
switch (object->type) {
case VIR_JSON_TYPE_OBJECT:
if (yajl_gen_map_open(g) != yajl_gen_status_ok)
return -1;
for (i = 0; i < object->data.object.npairs; i++) {
if (yajl_gen_string(g,
(unsigned char *)object->data.object.pairs[i].key,
strlen(object->data.object.pairs[i].key))
!= yajl_gen_status_ok)
return -1;
if (virJSONValueToStringOne(object->data.object.pairs[i].value, g) < 0)
return -1;
}
if (yajl_gen_map_close(g) != yajl_gen_status_ok)
return -1;
break;
case VIR_JSON_TYPE_ARRAY:
if (yajl_gen_array_open(g) != yajl_gen_status_ok)
return -1;
for (i = 0; i < object->data.array.nvalues; i++) {
if (virJSONValueToStringOne(object->data.array.values[i], g) < 0)
return -1;
}
if (yajl_gen_array_close(g) != yajl_gen_status_ok)
return -1;
break;
case VIR_JSON_TYPE_STRING:
if (yajl_gen_string(g, (unsigned char *)object->data.string,
strlen(object->data.string)) != yajl_gen_status_ok)
return -1;
break;
case VIR_JSON_TYPE_NUMBER:
if (yajl_gen_number(g, object->data.number,
strlen(object->data.number)) != yajl_gen_status_ok)
return -1;
break;
case VIR_JSON_TYPE_BOOLEAN:
if (yajl_gen_bool(g, object->data.boolean) != yajl_gen_status_ok)
return -1;
break;
case VIR_JSON_TYPE_NULL:
if (yajl_gen_null(g) != yajl_gen_status_ok)
return -1;
break;
default:
return -1;
}
return 0;
}
int
virJSONValueToBuffer(virJSONValue *object,
virBuffer *buf,
bool pretty)
{
yajl_gen g;
const unsigned char *str;
size_t len;
int ret = -1;
VIR_DEBUG("object=%p", object);
g = yajl_gen_alloc(NULL);
if (!g) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to create JSON formatter"));
goto cleanup;
}
yajl_gen_config(g, yajl_gen_beautify, pretty ? 1 : 0);
yajl_gen_config(g, yajl_gen_indent_string, pretty ? " " : " ");
yajl_gen_config(g, yajl_gen_validate_utf8, 1);
if (virJSONValueToStringOne(object, g) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to convert virJSONValue to yajl data"));
goto cleanup;
}
if (yajl_gen_get_buf(g, &str, &len) != yajl_gen_status_ok) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to format JSON"));
goto cleanup;
}
virBufferAdd(buf, (const char *) str, len);
ret = 0;
cleanup:
yajl_gen_free(g);
return ret;
}
#else
virJSONValue *
virJSONValueFromString(const char *jsonstring G_GNUC_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("No JSON parser implementation is available"));
return NULL;
}
int
virJSONValueToBuffer(virJSONValue *object G_GNUC_UNUSED,
virBuffer *buf G_GNUC_UNUSED,
bool pretty G_GNUC_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("No JSON parser implementation is available"));
return -1;
}
#endif
char *
virJSONValueToString(virJSONValue *object,
bool pretty)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
if (virJSONValueToBuffer(object, &buf, pretty) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
/**
* virJSONStringReformat:
* @jsonstr: string to reformat
* @pretty: use the pretty formatter
*
* Reformats a JSON string by passing it to the parser and then to the
* formatter. If @pretty is true the JSON is formatted for human eye
* compatibility.
*
* Returns the reformatted JSON string on success; NULL and a libvirt error on
* failure.
*/
char *
virJSONStringReformat(const char *jsonstr,
bool pretty)
{
g_autoptr(virJSONValue) json = NULL;
if (!(json = virJSONValueFromString(jsonstr)))
return NULL;
return virJSONValueToString(json, pretty);
}
static virJSONValue *
virJSONValueObjectDeflattenKeys(virJSONValue *json);
static int
virJSONValueObjectDeflattenWorker(const char *key,
virJSONValue *value,
void *opaque)
{
virJSONValue *retobj = opaque;
g_autoptr(virJSONValue) newval = NULL;
virJSONValue *existobj;
g_auto(GStrv) tokens = NULL;
/* non-nested keys only need to be copied */
if (!strchr(key, '.')) {
if (virJSONValueIsObject(value))
newval = virJSONValueObjectDeflattenKeys(value);
else
newval = virJSONValueCopy(value);
if (!newval)
return -1;
if (virJSONValueObjectHasKey(retobj, key)) {
virReportError(VIR_ERR_INVALID_ARG,
_("can't deflatten colliding key '%s'"), key);
return -1;
}
if (virJSONValueObjectAppend(retobj, key, &newval) < 0)
return -1;
return 0;
}
if (!(tokens = g_strsplit(key, ".", 2)))
return -1;
if (!tokens[0] || !tokens[1]) {
virReportError(VIR_ERR_INVALID_ARG,
_("invalid nested value key '%s'"), key);
return -1;
}
if (!(existobj = virJSONValueObjectGet(retobj, tokens[0]))) {
virJSONValue *newobj = virJSONValueNewObject();
existobj = newobj;
if (virJSONValueObjectAppend(retobj, tokens[0], &newobj) < 0)
return -1;
} else {
if (!virJSONValueIsObject(existobj)) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("mixing nested objects and values is forbidden in "
"JSON deflattening"));
return -1;
}
}
return virJSONValueObjectDeflattenWorker(tokens[1], value, existobj);
}
static virJSONValue *
virJSONValueObjectDeflattenKeys(virJSONValue *json)
{
g_autoptr(virJSONValue) deflattened = virJSONValueNewObject();
if (virJSONValueObjectForeachKeyValue(json,
virJSONValueObjectDeflattenWorker,
deflattened) < 0)
return NULL;
return g_steal_pointer(&deflattened);
}
/**
* virJSONValueObjectDeflattenArrays:
*
* Reconstruct JSON arrays from objects which only have sequential numeric
* keys starting from 0.
*/
static void
virJSONValueObjectDeflattenArrays(virJSONValue *json)
{
g_autofree virJSONValue **arraymembers = NULL;
virJSONObject *obj;
size_t i;
if (!json ||
json->type != VIR_JSON_TYPE_OBJECT)
return;
obj = &json->data.object;
arraymembers = g_new0(virJSONValue *, obj->npairs);
for (i = 0; i < obj->npairs; i++)
virJSONValueObjectDeflattenArrays(obj->pairs[i].value);
for (i = 0; i < obj->npairs; i++) {
virJSONObjectPair *pair = obj->pairs + i;
unsigned int keynum;
if (virStrToLong_uip(pair->key, NULL, 10, &keynum) < 0)
return;
if (keynum >= obj->npairs)
return;
if (arraymembers[keynum])
return;
arraymembers[keynum] = pair->value;
}
for (i = 0; i < obj->npairs; i++)
g_free(obj->pairs[i].key);
g_free(json->data.object.pairs);
i = obj->npairs;
json->type = VIR_JSON_TYPE_ARRAY;
json->data.array.nvalues = i;
json->data.array.values = g_steal_pointer(&arraymembers);
}
/**
* virJSONValueObjectDeflatten:
*
* In some cases it's possible to nest JSON objects by prefixing object members
* with the parent object name followed by the dot and then the attribute name
* rather than directly using a nested value object (e.g qemu's JSON
* pseudo-protocol in backing file definition).
*
* This function will attempt to reverse the process and provide a nested json
* hierarchy so that the parsers can be kept simple and we still can use the
* weird syntax some users might use.
*/
virJSONValue *
virJSONValueObjectDeflatten(virJSONValue *json)
{
virJSONValue *deflattened;
if (!(deflattened = virJSONValueObjectDeflattenKeys(json)))
return NULL;
virJSONValueObjectDeflattenArrays(deflattened);
return deflattened;
}