[libvirt] [PATCH 01/14] Introduce a simple API for handling JSON data

Daniel P. Berrange berrange at redhat.com
Thu Nov 26 18:27:19 UTC 2009


This introduces simple API for handling JSON data. There is
an internal data structure 'virJSONValuePtr' which stores a
arbitrary nested JSON value (number, string, array, object,
nul, etc).  There are APIs for constructing/querying objects
and APIs for parsing/formatting string formatted JSON data.

This uses the YAJL library for parsing/formatting from

 http://lloyd.github.com/yajl/

* src/util/json.h, src/util/json.c: Data structures and APIs
  for representing JSON data, and parsing/formatting it
* configure.in: Add check for yajl library
* libvirt.spec.in: Add build requires for yajl
* src/Makefile.am: Add json.c/h
* src/libvirt_private.syms: Export JSON symbols to drivers
---
 configure.in             |   55 +++
 libvirt.spec.in          |   14 +
 po/POTFILES.in           |    1 +
 src/Makefile.am          |    8 +-
 src/libvirt_private.syms |   47 ++
 src/util/json.c          | 1043 ++++++++++++++++++++++++++++++++++++++++++++++
 src/util/json.h          |  131 ++++++
 7 files changed, 1296 insertions(+), 3 deletions(-)
 create mode 100644 src/util/json.c
 create mode 100644 src/util/json.h

diff --git a/configure.in b/configure.in
index f735bba..41f50fc 100644
--- a/configure.in
+++ b/configure.in
@@ -645,6 +645,56 @@ AC_SUBST([SASL_CFLAGS])
 AC_SUBST([SASL_LIBS])
 
 
+dnl YAJL JSON library http://lloyd.github.com/yajl/
+AC_ARG_WITH([yajl],
+  [  --with-yajl         use YAJL for JSON parsing/formatting],
+  [],
+  [with_yajl=check])
+
+YAJL_CFLAGS=
+YAJL_LIBS=
+if test "x$with_yajl" != "xno"; then
+  if test "x$with_yajl" != "xyes" -a "x$with_yajl" != "xcheck"; then
+    YAJL_CFLAGS="-I$with_yajl/include"
+    YAJL_LIBS="-L$with_yajl/lib"
+  fi
+  fail=0
+  old_cppflags="$CPPFLAGS"
+  old_ldflags="$LDFLAGS"
+  CPPFLAGS="$CPPFLAGS $YAJL_CFLAGS"
+  LDFLAGS="$LDFLAGS $YAJL_LIBS"
+  AC_CHECK_HEADER([yajl/yajl_common.h],[],[
+    if test "x$with_yajl" != "xcheck" ; then
+        with_yajl=no
+    else
+        fail=1
+    fi])
+  if test "x$with_yajl" != "xno" ; then
+    AC_CHECK_LIB([yajl], [yajl_parse],[
+      YAJL_LIBS="$YAJL_LIBS -lyajl"
+      with_yajl=yes
+    ],[
+      if test "x$with_yajl" = "xcheck" ; then
+        with_yajl=no
+      else
+        fail=1
+      fi
+    ])
+  fi
+  test $fail = 1 &&
+    AC_MSG_ERROR([You must install the YAJL development package in order to compile libvirt])
+  CPPFLAGS="$old_cppflags"
+  LDFLAGS="$old_ldflags"
+  if test "x$with_yajl" = "xyes" ; then
+    AC_DEFINE_UNQUOTED([HAVE_YAJL], 1,
+      [whether YAJL is available for JSON parsing/formatting])
+  fi
+fi
+AM_CONDITIONAL([HAVE_YAJL], [test "x$with_yajl" = "xyes"])
+AC_SUBST([YAJL_CFLAGS])
+AC_SUBST([YAJL_LIBS])
+
+
 dnl PolicyKit library
 POLKIT_CFLAGS=
 POLKIT_LIBS=
@@ -1859,6 +1909,11 @@ AC_MSG_NOTICE([    sasl: $SASL_CFLAGS $SASL_LIBS])
 else
 AC_MSG_NOTICE([    sasl: no])
 fi
+if test "$with_yajl" != "no" ; then
+AC_MSG_NOTICE([    yajl: $YAJL_CFLAGS $YAJL_LIBS])
+else
+AC_MSG_NOTICE([    yajl: no])
+fi
 if test "$with_avahi" = "yes" ; then
 AC_MSG_NOTICE([   avahi: $AVAHI_CFLAGS $AVAHI_LIBS])
 else
diff --git a/libvirt.spec.in b/libvirt.spec.in
index dba14df..408ad05 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -60,6 +60,7 @@
 %define with_netcf         0%{!?_without_netcf:0}
 %define with_udev          0%{!?_without_udev:0}
 %define with_hal           0%{!?_without_hal:0}
+%define with_yajl          0%{!?_without_yajl:0}
 
 # Non-server/HV driver defaults which are always enabled
 %define with_python        0%{!?_without_python:1}
@@ -141,6 +142,11 @@
 %define with_hal       0%{!?_without_hal:%{server_drivers}}
 %endif
 
+# Enable yajl library for JSON mode with QEMU
+%if 0%{?fedora} >= 13 || 0%{?rhel} >= 6
+%define with_yajl     0%{!?_without_yajl:%{server_drivers}}
+%endif
+
 # Force QEMU to run as non-root
 %if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
 %define qemu_user  qemu
@@ -257,6 +263,9 @@ BuildRequires: hal-devel
 BuildRequires: libudev-devel >= 145
 BuildRequires: libpciaccess-devel >= 0.10.9
 %endif
+%if %{with_yajl}
+BuildRequires: yajl-devel
+%endif
 %if %{with_avahi}
 BuildRequires: avahi-devel
 %endif
@@ -495,6 +504,10 @@ of recent versions of Linux (and other OSes).
 %define _without_udev --without-udev
 %endif
 
+%if ! %{with_yajl}
+%define _without_yajl --without-yajl
+%endif
+
 %configure %{?_without_xen} \
            %{?_without_qemu} \
            %{?_without_openvz} \
@@ -522,6 +535,7 @@ of recent versions of Linux (and other OSes).
            %{?_without_selinux} \
            %{?_without_hal} \
            %{?_without_udev} \
+           %{?_without_yajl} \
            --with-qemu-user=%{qemu_user} \
            --with-qemu-group=%{qemu_group} \
            --with-init-script=redhat \
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 116aa87..9864259 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -50,6 +50,7 @@ src/uml/uml_driver.c
 src/util/bridge.c
 src/util/conf.c
 src/util/iptables.c
+src/util/json.c
 src/util/logging.c
 src/util/pci.c
 src/util/processinfo.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 9a3c9c8..b0775a8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,6 +52,7 @@ UTIL_SOURCES =							\
 		util/hash.c util/hash.h				\
 		util/iptables.c util/iptables.h			\
 		util/ebtables.c util/ebtables.h			\
+		util/json.c util/json.h				\
 		util/logging.c util/logging.h			\
 		util/memory.c util/memory.h			\
 		util/pci.c util/pci.h				\
@@ -283,8 +284,8 @@ noinst_LTLIBRARIES = libvirt_util.la
 libvirt_la_LIBADD = libvirt_util.la
 libvirt_util_la_SOURCES =					\
 		$(UTIL_SOURCES)
-libvirt_util_la_CFLAGS =  $(CAPNG_CFLAGS)
-libvirt_util_la_LDFLAGS =  $(CAPNG_LIBS)
+libvirt_util_la_CFLAGS =  $(CAPNG_CFLAGS) $(YAJL_CFLAGS)
+libvirt_util_la_LDFLAGS =  $(CAPNG_LIBS) $(YAJL_LIBS)
 
 
 noinst_LTLIBRARIES += libvirt_conf.la
@@ -828,12 +829,13 @@ libvirt_lxc_SOURCES =						\
 		$(NODE_INFO_SOURCES)				\
 		$(ENCRYPTION_CONF_SOURCES)			\
 		$(DOMAIN_CONF_SOURCES)
-libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS)
+libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) $(YAJL_LIBS)
 libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la
 libvirt_lxc_CFLAGS =				\
 		$(LIBPARTED_CFLAGS)		\
 		$(NUMACTL_CFLAGS)		\
 		$(CAPNG_CFLAGS)			\
+		$(YAJL_CFLAGS)			\
 		-I at top_srcdir@/src/conf
 endif
 endif
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e880c2e..ee7f3ed 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -269,6 +269,53 @@ virRegisterDeviceMonitor;
 virRegisterSecretDriver;
 
 
+# json.h
+virJSONValueFree;
+virJSONValueNewString;
+virJSONValueNewStringLen;
+virJSONValueNewNumberInt;
+virJSONValueNewNumberUint;
+virJSONValueNewNumberLong;
+virJSONValueNewNumberUlong;
+virJSONValueNewNumberDouble;
+virJSONValueNewBoolean;
+virJSONValueNewNull;
+virJSONValueNewArray;
+virJSONValueNewObject;
+virJSONValueObjectAppend;
+virJSONValueObjectAppendString;
+virJSONValueObjectAppendNumberInt;
+virJSONValueObjectAppendNumberUint;
+virJSONValueObjectAppendNumberLong;
+virJSONValueObjectAppendNumberUlong;
+virJSONValueObjectAppendNumberDouble;
+virJSONValueObjectAppendBoolean;
+virJSONValueObjectAppendNull;
+virJSONValueArrayAppend;
+virJSONValueObjectHasKey;
+virJSONValueObjectGet;
+virJSONValueArraySize;
+virJSONValueArrayGet;
+virJSONValueGetString;
+virJSONValueGetNumberInt;
+virJSONValueGetNumberUint;
+virJSONValueGetNumberLong;
+virJSONValueGetNumberUlong;
+virJSONValueGetNumberDouble;
+virJSONValueGetBoolean;
+virJSONValueIsNull;
+virJSONValueObjectGetString;
+virJSONValueObjectGetNumberInt;
+virJSONValueObjectGetNumberUint;
+virJSONValueObjectGetNumberLong;
+virJSONValueObjectGetNumberUlong;
+virJSONValueObjectGetNumberDouble;
+virJSONValueObjectGetBoolean;
+virJSONValueObjectIsNull;
+virJSONValueFromString;
+virJSONValueToString;
+
+
 # logging.h
 virLogMessage;
 virLogGetNbFilters;
diff --git a/src/util/json.c b/src/util/json.c
new file mode 100644
index 0000000..f359b95
--- /dev/null
+++ b/src/util/json.c
@@ -0,0 +1,1043 @@
+/*
+ * json.h: JSON object parsing/formatting
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ */
+
+
+#include <config.h>
+
+#include "json.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "logging.h"
+#include "util.h"
+
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_parse.h>
+
+/* XXX fixme */
+#define VIR_FROM_THIS VIR_FROM_NONE
+#define ReportError(conn, code, fmt...)                                 \
+    virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__,           \
+                         __FUNCTION__, __LINE__, fmt)
+
+
+typedef struct _virJSONParserState virJSONParserState;
+typedef virJSONParserState *virJSONParserStatePtr;
+struct _virJSONParserState {
+    virJSONValuePtr value;
+    char *key;
+};
+
+typedef struct _virJSONParser virJSONParser;
+typedef virJSONParser *virJSONParserPtr;
+struct _virJSONParser {
+    virJSONValuePtr head;
+    virJSONParserStatePtr state;
+    unsigned int nstate;
+};
+
+
+void virJSONValueFree(virJSONValuePtr value)
+{
+    int i;
+    if (!value)
+        return;
+
+    switch (value->type) {
+    case VIR_JSON_TYPE_OBJECT:
+        for (i = 0 ; i < value->data.array.nvalues ; i++) {
+            VIR_FREE(value->data.object.pairs[i].key);
+            virJSONValueFree(value->data.object.pairs[i].value);
+        }
+        VIR_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]);
+        VIR_FREE(value->data.array.values);
+        break;
+    case VIR_JSON_TYPE_STRING:
+        VIR_FREE(value->data.string);
+        break;
+    case VIR_JSON_TYPE_NUMBER:
+        VIR_FREE(value->data.number);
+        break;
+
+    }
+}
+
+
+virJSONValuePtr virJSONValueNewString(const char *data)
+{
+    virJSONValuePtr val;
+
+    if (!data)
+        return virJSONValueNewNull();
+
+    if (VIR_ALLOC(val) < 0)
+        return NULL;
+
+    val->type = VIR_JSON_TYPE_STRING;
+    if (!(val->data.string = strdup(data))) {
+        VIR_FREE(val);
+        return NULL;
+    }
+
+    return val;
+}
+
+virJSONValuePtr virJSONValueNewStringLen(const char *data, size_t length)
+{
+    virJSONValuePtr val;
+
+    if (!data)
+        return virJSONValueNewNull();
+
+    if (VIR_ALLOC(val) < 0)
+        return NULL;
+
+    val->type = VIR_JSON_TYPE_STRING;
+    if (!(val->data.string = strndup(data, length))) {
+        VIR_FREE(val);
+        return NULL;
+    }
+
+    return val;
+}
+
+static virJSONValuePtr virJSONValueNewNumber(const char *data)
+{
+    virJSONValuePtr val;
+
+    if (VIR_ALLOC(val) < 0)
+        return NULL;
+
+    val->type = VIR_JSON_TYPE_NUMBER;
+    if (!(val->data.number = strdup(data))) {
+        VIR_FREE(val);
+        return NULL;
+    }
+
+    return val;
+}
+
+virJSONValuePtr virJSONValueNewNumberInt(int data)
+{
+    virJSONValuePtr val = NULL;
+    char *str;
+    if (virAsprintf(&str, "%i", data) < 0)
+        return NULL;
+    val = virJSONValueNewNumber(str);
+    VIR_FREE(str);
+    return val;
+}
+
+
+virJSONValuePtr virJSONValueNewNumberUint(unsigned int data)
+{
+    virJSONValuePtr val = NULL;
+    char *str;
+    if (virAsprintf(&str, "%u", data) < 0)
+        return NULL;
+    val = virJSONValueNewNumber(str);
+    VIR_FREE(str);
+    return val;
+}
+
+
+virJSONValuePtr virJSONValueNewNumberLong(long long data)
+{
+    virJSONValuePtr val = NULL;
+    char *str;
+    if (virAsprintf(&str, "%lld", data) < 0)
+        return NULL;
+    val = virJSONValueNewNumber(str);
+    VIR_FREE(str);
+    return val;
+}
+
+
+virJSONValuePtr virJSONValueNewNumberUlong(unsigned long long data)
+{
+    virJSONValuePtr val = NULL;
+    char *str;
+    if (virAsprintf(&str, "%llu", data) < 0)
+        return NULL;
+    val = virJSONValueNewNumber(str);
+    VIR_FREE(str);
+    return val;
+}
+
+
+virJSONValuePtr virJSONValueNewNumberDouble(double data)
+{
+    virJSONValuePtr val = NULL;
+    char *str;
+    if (virAsprintf(&str, "%lf", data) < 0)
+        return NULL;
+    val = virJSONValueNewNumber(str);
+    VIR_FREE(str);
+    return val;
+}
+
+
+virJSONValuePtr virJSONValueNewBoolean(int boolean)
+{
+    virJSONValuePtr val;
+
+    if (VIR_ALLOC(val) < 0)
+        return NULL;
+
+    val->type = VIR_JSON_TYPE_BOOLEAN;
+    val->data.boolean = boolean;
+
+    return val;
+}
+
+virJSONValuePtr virJSONValueNewNull(void)
+{
+    virJSONValuePtr val;
+
+    if (VIR_ALLOC(val) < 0)
+        return NULL;
+
+    val->type = VIR_JSON_TYPE_NULL;
+
+    return val;
+}
+
+virJSONValuePtr virJSONValueNewArray(void)
+{
+    virJSONValuePtr val;
+
+    if (VIR_ALLOC(val) < 0)
+        return NULL;
+
+    val->type = VIR_JSON_TYPE_ARRAY;
+
+    return val;
+}
+
+virJSONValuePtr virJSONValueNewObject(void)
+{
+    virJSONValuePtr val;
+
+    if (VIR_ALLOC(val) < 0)
+        return NULL;
+
+    val->type = VIR_JSON_TYPE_OBJECT;
+
+    return val;
+}
+
+int virJSONValueObjectAppend(virJSONValuePtr object, const char *key, virJSONValuePtr value)
+{
+    char *newkey;
+
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    if (virJSONValueObjectHasKey(object, key))
+        return -1;
+
+    if (!(newkey = strdup(key)))
+        return -1;
+
+    if (VIR_REALLOC_N(object->data.object.pairs,
+                      object->data.object.npairs + 1) < 0) {
+        VIR_FREE(newkey);
+        return -1;
+    }
+
+    object->data.object.pairs[object->data.object.npairs].key = newkey;
+    object->data.object.pairs[object->data.object.npairs].value = value;
+    object->data.object.npairs++;
+
+    return 0;
+}
+
+
+int virJSONValueObjectAppendString(virJSONValuePtr object, const char *key, const char *value)
+{
+    virJSONValuePtr jvalue = virJSONValueNewString(value);
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+int virJSONValueObjectAppendNumberInt(virJSONValuePtr object, const char *key, int number)
+{
+    virJSONValuePtr jvalue = virJSONValueNewNumberInt(number);
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+
+int virJSONValueObjectAppendNumberUint(virJSONValuePtr object, const char *key, unsigned int number)
+{
+    virJSONValuePtr jvalue = virJSONValueNewNumberUint(number);
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+int virJSONValueObjectAppendNumberLong(virJSONValuePtr object, const char *key, long long number)
+{
+    virJSONValuePtr jvalue = virJSONValueNewNumberLong(number);
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+int virJSONValueObjectAppendNumberUlong(virJSONValuePtr object, const char *key, unsigned long long number)
+{
+    virJSONValuePtr jvalue = virJSONValueNewNumberUlong(number);
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+int virJSONValueObjectAppendNumberDouble(virJSONValuePtr object, const char *key, double number)
+{
+    virJSONValuePtr jvalue = virJSONValueNewNumberDouble(number);
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+int virJSONValueObjectAppendBoolean(virJSONValuePtr object, const char *key, int boolean)
+{
+    virJSONValuePtr jvalue = virJSONValueNewBoolean(boolean);
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+int virJSONValueObjectAppendNull(virJSONValuePtr object, const char *key)
+{
+    virJSONValuePtr jvalue = virJSONValueNewNull();
+    if (!jvalue)
+        return -1;
+    if (virJSONValueObjectAppend(object, key, jvalue) < 0) {
+        virJSONValueFree(jvalue);
+        return -1;
+    }
+    return 0;
+}
+
+
+int virJSONValueArrayAppend(virJSONValuePtr array, virJSONValuePtr value)
+{
+    if (array->type != VIR_JSON_TYPE_ARRAY)
+        return -1;
+
+    if (VIR_REALLOC_N(array->data.array.values,
+                      array->data.array.nvalues + 1) < 0)
+        return -1;
+
+    array->data.array.values[array->data.array.nvalues] = value;
+    array->data.array.nvalues++;
+
+    return 0;
+}
+
+int virJSONValueObjectHasKey(virJSONValuePtr object, const char *key)
+{
+    int i;
+
+    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))
+            return 1;
+    }
+
+    return 0;
+}
+
+virJSONValuePtr virJSONValueObjectGet(virJSONValuePtr object, const char *key)
+{
+    int 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;
+}
+
+int virJSONValueArraySize(virJSONValuePtr array)
+{
+    if (array->type != VIR_JSON_TYPE_ARRAY)
+        return -1;
+
+    return array->data.array.nvalues;
+}
+
+
+virJSONValuePtr virJSONValueArrayGet(virJSONValuePtr 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];
+}
+
+char *virJSONValueGetString(virJSONValuePtr string)
+{
+    if (string->type != VIR_JSON_TYPE_STRING)
+        return NULL;
+
+    return string->data.string;
+}
+
+
+int virJSONValueGetNumberInt(virJSONValuePtr number, int *value)
+{
+    if (number->type != VIR_JSON_TYPE_NUMBER)
+        return -1;
+
+    return virStrToLong_i(number->data.number, NULL, 10, value);
+}
+
+int virJSONValueGetNumberUint(virJSONValuePtr number, unsigned int *value)
+{
+    if (number->type != VIR_JSON_TYPE_NUMBER)
+        return -1;
+
+    return virStrToLong_ui(number->data.number, NULL, 10, value);
+}
+
+int virJSONValueGetNumberLong(virJSONValuePtr number, long long *value)
+{
+    if (number->type != VIR_JSON_TYPE_NUMBER)
+        return -1;
+
+    return virStrToLong_ll(number->data.number, NULL, 10, value);
+}
+
+int virJSONValueGetNumberUlong(virJSONValuePtr 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(virJSONValuePtr number, double *value)
+{
+    if (number->type != VIR_JSON_TYPE_NUMBER)
+        return -1;
+
+    return virStrToDouble(number->data.number, NULL, value);
+}
+
+
+int virJSONValueGetBoolean(virJSONValuePtr val)
+{
+    if (val->type != VIR_JSON_TYPE_NUMBER)
+        return -1;
+
+    return val->data.boolean;
+}
+
+
+int virJSONValueIsNull(virJSONValuePtr val)
+{
+    if (val->type != VIR_JSON_TYPE_NULL)
+        return 0;
+
+    return 1;
+}
+
+
+char *virJSONValueObjectGetString(virJSONValuePtr object, const char *key)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return NULL;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return NULL;
+
+    return virJSONValueGetString(val);
+}
+
+
+int virJSONValueObjectGetNumberInt(virJSONValuePtr object, const char *key, int *value)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return -1;
+
+    return virJSONValueGetNumberInt(val, value);
+}
+
+
+int virJSONValueObjectGetNumberUint(virJSONValuePtr object, const char *key, unsigned int *value)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return -1;
+
+    return virJSONValueGetNumberUint(val, value);
+}
+
+
+int virJSONValueObjectGetNumberLong(virJSONValuePtr object, const char *key, long long *value)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return -1;
+
+    return virJSONValueGetNumberLong(val, value);
+}
+
+
+int virJSONValueObjectGetNumberUlong(virJSONValuePtr object, const char *key, unsigned long long *value)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return -1;
+
+    return virJSONValueGetNumberUlong(val, value);
+}
+
+
+int virJSONValueObjectGetNumberDouble(virJSONValuePtr object, const char *key, double *value)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return -1;
+
+    return virJSONValueGetNumberDouble(val, value);
+}
+
+
+int virJSONValueObjectGetBoolean(virJSONValuePtr object, const char *key)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return -1;
+
+    return virJSONValueGetBoolean(val);
+}
+
+
+int virJSONValueObjectIsNull(virJSONValuePtr object, const char *key)
+{
+    virJSONValuePtr val;
+    if (object->type != VIR_JSON_TYPE_OBJECT)
+        return -1;
+
+    val = virJSONValueObjectGet(object, key);
+    if (!val)
+        return -1;
+
+    return virJSONValueIsNull(val);
+}
+
+
+#if HAVE_YAJL
+static int virJSONParserInsertValue(virJSONParserPtr parser,
+                                    virJSONValuePtr value)
+{
+    if (!parser->head) {
+        parser->head = value;
+    } else {
+        virJSONParserStatePtr state;
+        if (!parser->nstate) {
+            VIR_DEBUG0("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_DEBUG0("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_DEBUG0("unexpected key when inserting array value");
+                return -1;
+            }
+
+            if (virJSONValueArrayAppend(state->value,
+                                        value) < 0)
+                return -1;
+        }   break;
+
+        default:
+            VIR_DEBUG0("unexpected value type, not a container");
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int virJSONParserHandleNull(void * ctx)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONValuePtr value = virJSONValueNewNull();
+
+    VIR_DEBUG("parser=%p", parser);
+
+    if (!value)
+        return 0;
+
+    if (virJSONParserInsertValue(parser, value) < 0) {
+        virJSONValueFree(value);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int virJSONParserHandleBoolean(void * ctx, int boolean)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONValuePtr value = virJSONValueNewBoolean(boolean);
+
+    VIR_DEBUG("parser=%p boolean=%d", parser, boolean);
+
+    if (!value)
+        return 0;
+
+    if (virJSONParserInsertValue(parser, value) < 0) {
+        virJSONValueFree(value);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int virJSONParserHandleNumber(void * ctx,
+                                     const char * s,
+                                     unsigned int l)
+{
+    virJSONParserPtr parser = ctx;
+    char *str = strndup(s, l);
+    virJSONValuePtr value;
+
+    if (!str)
+        return -1;
+    value = virJSONValueNewNumber(str);
+    VIR_FREE(str);
+
+    VIR_DEBUG("parser=%p str=%s", parser, str);
+
+    if (!value)
+        return 0;
+
+    if (virJSONParserInsertValue(parser, value) < 0) {
+        virJSONValueFree(value);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int virJSONParserHandleString(void * ctx,
+                                     const unsigned char * stringVal,
+                                     unsigned int stringLen)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONValuePtr value = virJSONValueNewStringLen((const char *)stringVal,
+                                                     stringLen);
+
+    VIR_DEBUG("parser=%p str=%p", parser, (const char *)stringVal);
+
+    if (!value)
+        return 0;
+
+    if (virJSONParserInsertValue(parser, value) < 0) {
+        virJSONValueFree(value);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int virJSONParserHandleMapKey(void * ctx,
+                                     const unsigned char * stringVal,
+                                     unsigned int stringLen)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONParserStatePtr 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 = strndup((const char *)stringVal, stringLen);
+    if (!state->key)
+        return 0;
+    return 1;
+}
+
+static int virJSONParserHandleStartMap(void * ctx)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONValuePtr value = virJSONValueNewObject();
+
+    VIR_DEBUG("parser=%p", parser);
+
+    if (!value)
+        return 0;
+
+    if (virJSONParserInsertValue(parser, value) < 0) {
+        virJSONValueFree(value);
+        return 0;
+    }
+
+    if (VIR_REALLOC_N(parser->state,
+                      parser->nstate + 1) < 0)
+        return 0;
+
+    parser->state[parser->nstate].value = value;
+    parser->state[parser->nstate].key = NULL;
+    parser->nstate++;
+
+    return 1;
+}
+
+
+static int virJSONParserHandleEndMap(void * ctx)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONParserStatePtr 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;
+    }
+
+    if (VIR_REALLOC_N(parser->state,
+                      parser->nstate - 1) < 0)
+        return 0;
+    parser->nstate--;
+
+    return 1;
+}
+
+static int virJSONParserHandleStartArray(void * ctx)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONValuePtr value = virJSONValueNewArray();
+
+    VIR_DEBUG("parser=%p", parser);
+
+    if (!value)
+        return 0;
+
+    if (virJSONParserInsertValue(parser, value) < 0) {
+        virJSONValueFree(value);
+        return 0;
+    }
+
+    if (VIR_REALLOC_N(parser->state,
+                      parser->nstate + 1) < 0)
+        return 0;
+
+    parser->state[parser->nstate].value = value;
+    parser->state[parser->nstate].key = NULL;
+    parser->nstate++;
+
+    return 1;
+}
+
+static int virJSONParserHandleEndArray(void * ctx)
+{
+    virJSONParserPtr parser = ctx;
+    virJSONParserStatePtr 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;
+    }
+
+    if (VIR_REALLOC_N(parser->state,
+                      parser->nstate - 1) < 0)
+        return 0;
+    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 */
+virJSONValuePtr virJSONValueFromString(const char *jsonstring)
+{
+    yajl_parser_config cfg = { 1, 1 };
+    yajl_handle hand;
+    virJSONParser parser = { NULL, NULL, 0 };
+
+    VIR_DEBUG("string=%s", jsonstring);
+
+    hand = yajl_alloc(&parserCallbacks, &cfg, NULL, &parser);
+
+    if (yajl_parse(hand,
+                   (const unsigned char *)jsonstring,
+                   strlen(jsonstring)) != yajl_status_ok) {
+        unsigned char *errstr = yajl_get_error(hand, 1,
+                                               (const unsigned char*)jsonstring,
+                                               strlen(jsonstring));
+
+        ReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                    _("cannot parse json %s: %s"),
+                    jsonstring, (const char*) errstr);
+        VIR_FREE(errstr);
+        virJSONValueFree(parser.head);
+        goto cleanup;
+    }
+
+cleanup:
+    yajl_free(hand);
+
+    if (parser.nstate) {
+        int i;
+        VIR_WARN("cleanup state %d", parser.nstate);
+        for (i = 0 ; i < parser.nstate ; i++) {
+            VIR_FREE(parser.state[i].key);
+        }
+    }
+
+    VIR_DEBUG("result=%p", parser.head);
+
+    return parser.head;
+}
+
+
+static int virJSONValueToStringOne(virJSONValuePtr object,
+                                   yajl_gen g)
+{
+    int 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;
+}
+
+char *virJSONValueToString(virJSONValuePtr object)
+{
+    yajl_gen_config conf = { 0, " " }; /* Turns off pretty printing since QEMU can't cope */
+    yajl_gen g;
+    const unsigned char *str;
+    char *ret = NULL;
+    unsigned int len;
+
+    VIR_DEBUG("object=%p", object);
+
+    g = yajl_gen_alloc(&conf, NULL);
+
+    if (virJSONValueToStringOne(object, g) < 0) {
+        virReportOOMError(NULL);
+        goto cleanup;
+    }
+
+    if (yajl_gen_get_buf(g, &str, &len) != yajl_gen_status_ok) {
+        virReportOOMError(NULL);
+        goto cleanup;
+    }
+
+    if (!(ret = strdup((const char *)str)))
+        virReportOOMError(NULL);
+
+cleanup:
+    yajl_gen_free(g);
+
+    VIR_DEBUG("result=%s", NULLSTR(ret));
+
+    return ret;
+}
+
+
+#else
+virJSONValuePtr virJSONValueFromString(const char *jsonstring ATTRIBUTE_UNUSED)
+{
+    ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s",
+                _("No JSON parser implementation is available"));
+    return NULL;
+}
+char *virJSONValueToString(virJSONValuePtr object)
+{
+    ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s",
+                _("No JSON parser implementation is available"));
+    return NULL;
+}
+#endif
diff --git a/src/util/json.h b/src/util/json.h
new file mode 100644
index 0000000..bab93cf
--- /dev/null
+++ b/src/util/json.h
@@ -0,0 +1,131 @@
+/*
+ * json.h: JSON object parsing/formatting
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ */
+
+
+#ifndef __VIR_JSON_H_
+#define __VIR_JSON_H_
+
+#include "internal.h"
+
+
+enum {
+    VIR_JSON_TYPE_OBJECT,
+    VIR_JSON_TYPE_ARRAY,
+    VIR_JSON_TYPE_STRING,
+    VIR_JSON_TYPE_NUMBER,
+    VIR_JSON_TYPE_BOOLEAN,
+    VIR_JSON_TYPE_NULL,
+};
+
+typedef struct _virJSONValue virJSONValue;
+typedef virJSONValue *virJSONValuePtr;
+
+typedef struct _virJSONObject virJSONObject;
+typedef virJSONObject *virJSONObjectPtr;
+
+typedef struct _virJSONObjectPair virJSONObjectPair;
+typedef virJSONObjectPair *virJSONObjectPairPtr;
+
+typedef struct _virJSONArray virJSONArray;
+typedef virJSONArray *virJSONArrayPtr;
+
+
+struct _virJSONObjectPair {
+    char *key;
+    virJSONValuePtr value;
+};
+
+struct _virJSONObject {
+    unsigned int npairs;
+    virJSONObjectPairPtr pairs;
+};
+
+struct _virJSONArray {
+    unsigned int nvalues;
+    virJSONValuePtr *values;
+};
+
+struct _virJSONValue {
+    int type;
+
+    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;
+};
+
+void virJSONValueFree(virJSONValuePtr value);
+
+virJSONValuePtr virJSONValueNewString(const char *data);
+virJSONValuePtr virJSONValueNewStringLen(const char *data, size_t length);
+virJSONValuePtr virJSONValueNewNumberInt(int data);
+virJSONValuePtr virJSONValueNewNumberUint(unsigned int data);
+virJSONValuePtr virJSONValueNewNumberLong(long long data);
+virJSONValuePtr virJSONValueNewNumberUlong(unsigned long long data);
+virJSONValuePtr virJSONValueNewNumberDouble(double data);
+virJSONValuePtr virJSONValueNewBoolean(int boolean);
+virJSONValuePtr virJSONValueNewNull(void);
+virJSONValuePtr virJSONValueNewArray(void);
+virJSONValuePtr virJSONValueNewObject(void);
+
+int virJSONValueObjectAppend(virJSONValuePtr object, const char *key, virJSONValuePtr value);
+int virJSONValueArrayAppend(virJSONValuePtr object, virJSONValuePtr value);
+
+int virJSONValueObjectHasKey(virJSONValuePtr object, const char *key);
+virJSONValuePtr virJSONValueObjectGet(virJSONValuePtr object, const char *key);
+
+int virJSONValueArraySize(virJSONValuePtr object);
+virJSONValuePtr virJSONValueArrayGet(virJSONValuePtr object, unsigned int element);
+
+char *virJSONValueGetString(virJSONValuePtr object);
+int virJSONValueGetNumberInt(virJSONValuePtr object, int *value);
+int virJSONValueGetNumberUint(virJSONValuePtr object, unsigned int *value);
+int virJSONValueGetNumberLong(virJSONValuePtr object, long long *value);
+int virJSONValueGetNumberUlong(virJSONValuePtr object, unsigned long long *value);
+int virJSONValueGetNumberDouble(virJSONValuePtr object, double *value);
+int virJSONValueGetBoolean(virJSONValuePtr object);
+int virJSONValueIsNull(virJSONValuePtr object);
+
+char *virJSONValueObjectGetString(virJSONValuePtr object, const char *key);
+int virJSONValueObjectGetNumberInt(virJSONValuePtr object, const char *key, int *value);
+int virJSONValueObjectGetNumberUint(virJSONValuePtr object, const char *key, unsigned int *value);
+int virJSONValueObjectGetNumberLong(virJSONValuePtr object, const char *key, long long *value);
+int virJSONValueObjectGetNumberUlong(virJSONValuePtr object, const char *key, unsigned long long *value);
+int virJSONValueObjectGetNumberDouble(virJSONValuePtr object, const char *key, double *value);
+int virJSONValueObjectGetBoolean(virJSONValuePtr object, const char *key);
+int virJSONValueObjectIsNull(virJSONValuePtr object, const char *key);
+
+int virJSONValueObjectAppendString(virJSONValuePtr object, const char *key, const char *value);
+int virJSONValueObjectAppendNumberInt(virJSONValuePtr object, const char *key, int number);
+int virJSONValueObjectAppendNumberUint(virJSONValuePtr object, const char *key, unsigned int number);
+int virJSONValueObjectAppendNumberLong(virJSONValuePtr object, const char *key, long long number);
+int virJSONValueObjectAppendNumberUlong(virJSONValuePtr object, const char *key, unsigned long long number);
+int virJSONValueObjectAppendNumberDouble(virJSONValuePtr object, const char *key, double number);
+int virJSONValueObjectAppendBoolean(virJSONValuePtr object, const char *key, int boolean);
+int virJSONValueObjectAppendNull(virJSONValuePtr object, const char *key);
+
+virJSONValuePtr virJSONValueFromString(const char *jsonstring);
+char *virJSONValueToString(virJSONValuePtr object);
+
+#endif /* __VIR_JSON_H_ */
-- 
1.6.5.2




More information about the libvir-list mailing list