[json-glib] gvariant: Adds JSON GVariant integration API, with docs and tests



commit 212b243c07721242da3dc2c0e6dfe979f73ee5c6
Author: Eduardo Lima Mitev <elima igalia com>
Date:   Tue Nov 9 16:45:30 2010 +0100

    gvariant: Adds JSON GVariant integration API, with docs and tests
    
    https://bugzilla.gnome.org/show_bug.cgi?id=632940

 doc/reference/json-glib-docs.xml     |    1 +
 doc/reference/json-glib-sections.txt |   10 +
 json-glib/Makefile.am                |    2 +
 json-glib/json-glib.h                |    2 +
 json-glib/json-gvariant.c            | 1296 ++++++++++++++++++++++++++++++++++
 json-glib/json-gvariant.h            |   46 ++
 json-glib/tests/Makefile.am          |    4 +
 json-glib/tests/gvariant-test.c      |  233 ++++++
 8 files changed, 1594 insertions(+), 0 deletions(-)
---
diff --git a/doc/reference/json-glib-docs.xml b/doc/reference/json-glib-docs.xml
index 01e0134..92bf7d9 100644
--- a/doc/reference/json-glib-docs.xml
+++ b/doc/reference/json-glib-docs.xml
@@ -119,6 +119,7 @@
     <xi:include href="xml/json-gobject.xml"/>
     <xi:include href="xml/json-serializable.xml"/>
     <xi:include href="xml/json-gboxed.xml"/>
+    <xi:include href="xml/json-gvariant.xml"/>
   </part>
 
   <part id="json-tools">
diff --git a/doc/reference/json-glib-sections.txt b/doc/reference/json-glib-sections.txt
index 9a93e05..4ffeaa6 100644
--- a/doc/reference/json-glib-sections.txt
+++ b/doc/reference/json-glib-sections.txt
@@ -256,6 +256,16 @@ json_serialize_gobject
 </SECTION>
 
 <SECTION>
+<FILE>json-gvariant</FILE>
+
+<SUBSECTION>
+json_gvariant_serialize
+json_gvariant_serialize_data
+json_gvariant_deserialize
+json_gvariant_deserialize_data
+</SECTION>
+
+<SECTION>
 <FILE>json-version</FILE>
 <TITLE>Versioning information</TITLE>
 JSON_MAJOR_VERSION
diff --git a/json-glib/Makefile.am b/json-glib/Makefile.am
index d5da07b..1068807 100644
--- a/json-glib/Makefile.am
+++ b/json-glib/Makefile.am
@@ -36,6 +36,7 @@ source_h = \
 	$(top_srcdir)/json-glib/json-parser.h 		\
 	$(top_srcdir)/json-glib/json-reader.h		\
 	$(top_srcdir)/json-glib/json-types.h 		\
+	$(top_srcdir)/json-glib/json-gvariant.h		\
 	$(NULL)
 
 source_h_private = \
@@ -58,6 +59,7 @@ source_c = \
 	$(srcdir)/json-reader.c		\
 	$(srcdir)/json-scanner.c 	\
 	$(srcdir)/json-serializable.c	\
+	$(srcdir)/json-gvariant.c	\
 	$(NULL)
 
 # glib-mkenums rules
diff --git a/json-glib/json-glib.h b/json-glib/json-glib.h
index 7a4d783..a14416b 100644
--- a/json-glib/json-glib.h
+++ b/json-glib/json-glib.h
@@ -38,6 +38,8 @@
 
 #include <json-glib/json-gobject.h>
 
+#include <json-glib/json-gvariant.h>
+
 #undef __JSON_GLIB_INSIDE__
 
 #endif /* __JSON_GLIB_H__ */
diff --git a/json-glib/json-gvariant.c b/json-glib/json-gvariant.c
new file mode 100644
index 0000000..9952089
--- /dev/null
+++ b/json-glib/json-gvariant.c
@@ -0,0 +1,1296 @@
+/* json-gvariant.c - JSON GVariant integration
+ *
+ * This file is part of JSON-GLib
+ * Copyright (C) 2007  OpenedHand Ltd.
+ *
+ * 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 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.
+ *
+ * Author:
+ *   Eduardo Lima Mitev  <elima igalia com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "json-gvariant.h"
+
+/**
+ * SECTION:json-gvariant
+ * @short_description: Serialize and deserialize GVariant types
+ * @Title: JSON GVariant Integration
+ *
+ * Use json_gvariant_serialize() and json_gvariant_serialize_data() to
+ * convert from any #GVariant value to a #JsonNode tree or its string
+ * representation.
+ *
+ * Use json_gvariant_deserialize() and json_gvariant_deserialize_data() to
+ * obtain the #GVariant value from a #JsonNode tree or directly from a JSON
+ * string.
+ * Since many #GVariant data types cannot be directly represented as
+ * JSON, a #GVariant type string (signature) should be provided to these
+ * methods in order to obtain a correct, type-contrained result.
+ * If no signature is provided, conversion can still be done, but the
+ * resulting #GVariant value will be "guessed" from the JSON data types,
+ * according to the following table:
+ *
+ * <table frame='all'><title>Default JSON to GVariant conversion (without signature constrains)</title>
+ *  <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+ *   <thead>
+ *     <row>
+ *       <entry>JSON</entry>
+ *       <entry>GVariant</entry>
+ *     </row>
+ *   </thead>
+ *   <tfoot>
+ *    <row>
+ *     <entry>string</entry>
+ *     <entry>string (s)</entry>
+ *    </row>
+ *    <row>
+ *     <entry>int64</entry>
+ *     <entry>int64 (x)</entry>
+ *    </row>
+ *    <row>
+ *     <entry>boolean</entry>
+ *     <entry>boolean (b)</entry>
+ *    </row>
+ *    <row>
+ *     <entry>double</entry>
+ *     <entry>double (d)</entry>
+ *    </row>
+ *    <row>
+ *     <entry>array</entry>
+ *     <entry>array of variants (av)</entry>
+ *    </row>
+ *    <row>
+ *     <entry>object</entry>
+ *     <entry>dictionary of string-variant (a{sv})</entry>
+ *    </row>
+ *    <row>
+ *     <entry>null</entry>
+ *     <entry>maybe variant (mv)</entry>
+ *    </row>
+ *   </tfoot>
+ *  </tgroup>
+ * </table>
+ */
+
+#define G_VARIANT_CLASS_DICTIONARY 'c'
+
+typedef void (* GVariantForeachFunc) (GVariant *variant_child,
+                                      gpointer  user_data);
+
+static GVariant * json_to_gvariant_recurse (JsonNode      *json_node,
+                                            const gchar  **signature,
+                                            GError       **error);
+
+/* ========================================================================== */
+/* GVariant to JSON */
+/* ========================================================================== */
+
+static void
+gvariant_foreach (GVariant            *variant,
+                  GVariantForeachFunc  func,
+                  gpointer             user_data)
+{
+  GVariantIter iter;
+  GVariant *variant_child;
+
+  g_variant_iter_init (&iter, variant);
+  while ( (variant_child = g_variant_iter_next_value (&iter)) != NULL)
+    {
+      func (variant_child, user_data);
+      g_variant_unref (variant_child);
+    }
+}
+
+static void
+gvariant_to_json_array_foreach (GVariant *variant_child,
+                                gpointer  user_data)
+{
+  JsonNode *json_child;
+  JsonArray *array = (JsonArray *) user_data;
+
+  json_child = json_gvariant_serialize (variant_child);
+  json_array_add_element (array, json_child);
+}
+
+static JsonNode *
+gvariant_to_json_array (GVariant *variant)
+{
+  JsonArray *array;
+  JsonNode *json_node;
+
+  array = json_array_new ();
+  json_node = json_node_new (JSON_NODE_ARRAY);
+  json_node_set_array (json_node, array);
+  json_array_unref (array);
+
+  gvariant_foreach (variant,
+                    gvariant_to_json_array_foreach,
+                    array);
+
+  return json_node;
+}
+
+static gchar *
+gvariant_simple_to_string (GVariant *variant)
+{
+  GVariantClass class;
+  gchar *str;
+
+  class = g_variant_classify (variant);
+  switch (class)
+    {
+    case G_VARIANT_CLASS_BOOLEAN:
+      if (g_variant_get_boolean (variant))
+        str = g_strdup ("true");
+      else
+        str = g_strdup ("false");
+      break;
+
+    case G_VARIANT_CLASS_BYTE:
+      str = g_strdup_printf ("%u", g_variant_get_byte (variant));
+      break;
+    case G_VARIANT_CLASS_INT16:
+      str = g_strdup_printf ("%d", g_variant_get_int16 (variant));
+      break;
+    case G_VARIANT_CLASS_UINT16:
+      str = g_strdup_printf ("%u", g_variant_get_uint16 (variant));
+      break;
+    case G_VARIANT_CLASS_INT32:
+      str = g_strdup_printf ("%d", g_variant_get_int32 (variant));
+      break;
+    case G_VARIANT_CLASS_UINT32:
+      str = g_strdup_printf ("%u", g_variant_get_uint32 (variant));
+      break;
+    case G_VARIANT_CLASS_INT64:
+      str = g_strdup_printf ("%" G_GINT64_FORMAT,
+                             g_variant_get_int64 (variant));
+      break;
+    case G_VARIANT_CLASS_UINT64:
+      str = g_strdup_printf ("%" G_GUINT64_FORMAT,
+                             g_variant_get_uint64 (variant));
+      break;
+    case G_VARIANT_CLASS_HANDLE:
+      str = g_strdup_printf ("%d", g_variant_get_handle (variant));
+      break;
+
+    case G_VARIANT_CLASS_DOUBLE:
+      {
+        gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+        g_ascii_formatd (buf,
+                         G_ASCII_DTOSTR_BUF_SIZE,
+                         "%f",
+                         g_variant_get_double (variant));
+
+        str = g_strdup (buf);
+        break;
+      }
+
+    case G_VARIANT_CLASS_STRING:
+    case G_VARIANT_CLASS_OBJECT_PATH:
+    case G_VARIANT_CLASS_SIGNATURE:
+      str = g_strdup (g_variant_get_string (variant, NULL));
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return str;
+}
+
+static JsonNode *
+gvariant_dict_entry_to_json (GVariant  *variant, gchar **member_name)
+{
+  GVariant *member;
+  GVariant *value;
+  JsonNode *json_node;
+
+  member = g_variant_get_child_value (variant, 0);
+  *member_name = gvariant_simple_to_string (member);
+
+  value = g_variant_get_child_value (variant, 1);
+  json_node = json_gvariant_serialize (value);
+
+  g_variant_unref (member);
+  g_variant_unref (value);
+
+  return json_node;
+}
+
+static void
+gvariant_to_json_object_foreach (GVariant *variant_child, gpointer  user_data)
+{
+  gchar *member_name;
+  JsonNode *json_child;
+  JsonObject *object = (JsonObject *) user_data;
+
+  json_child = gvariant_dict_entry_to_json (variant_child, &member_name);
+  json_object_set_member (object, member_name, json_child);
+  g_free (member_name);
+}
+
+static JsonNode *
+gvariant_to_json_object (GVariant *variant)
+{
+  JsonNode *json_node;
+  JsonObject *object;
+
+  json_node = json_node_new (JSON_NODE_OBJECT);
+  object = json_object_new ();
+  json_node_set_object (json_node, object);
+  json_object_unref (object);
+
+  gvariant_foreach (variant,
+                    gvariant_to_json_object_foreach,
+                    object);
+
+  return json_node;
+}
+
+/**
+ * json_gvariant_serialize:
+ * @variant: A #GVariant to convert
+ *
+ * Converts @variant to a JSON tree.
+ *
+ * Return value: (transfer full): A #JsonNode representing the root of the
+ *   JSON data structure obtained from @variant
+ *
+ * Since: 0.14
+ */
+JsonNode *
+json_gvariant_serialize (GVariant *variant)
+{
+  JsonNode *json_node = NULL;
+  GVariantClass class;
+
+  g_return_val_if_fail (variant != NULL, NULL);
+
+  class = g_variant_classify (variant);
+
+  if (! g_variant_is_container (variant))
+    {
+      json_node = json_node_new (JSON_NODE_VALUE);
+
+      switch (class)
+        {
+        case G_VARIANT_CLASS_BOOLEAN:
+          json_node_set_boolean (json_node, g_variant_get_boolean (variant));
+          break;
+
+        case G_VARIANT_CLASS_BYTE:
+          json_node_set_int (json_node, g_variant_get_byte (variant));
+          break;
+        case G_VARIANT_CLASS_INT16:
+          json_node_set_int (json_node, g_variant_get_int16 (variant));
+          break;
+        case G_VARIANT_CLASS_UINT16:
+          json_node_set_int (json_node, g_variant_get_uint16 (variant));
+          break;
+        case G_VARIANT_CLASS_INT32:
+          json_node_set_int (json_node, g_variant_get_int32 (variant));
+          break;
+        case G_VARIANT_CLASS_UINT32:
+          json_node_set_int (json_node, g_variant_get_uint32 (variant));
+          break;
+        case G_VARIANT_CLASS_INT64:
+          json_node_set_int (json_node, g_variant_get_int64 (variant));
+          break;
+        case G_VARIANT_CLASS_UINT64:
+          json_node_set_int (json_node, g_variant_get_uint64 (variant));
+          break;
+        case G_VARIANT_CLASS_HANDLE:
+          json_node_set_int (json_node, g_variant_get_handle (variant));
+          break;
+
+        case G_VARIANT_CLASS_DOUBLE:
+          json_node_set_double (json_node, g_variant_get_double (variant));
+          break;
+
+        case G_VARIANT_CLASS_STRING:
+        case G_VARIANT_CLASS_OBJECT_PATH:
+        case G_VARIANT_CLASS_SIGNATURE:
+          json_node_set_string (json_node, g_variant_get_string (variant, NULL));
+          break;
+
+        default:
+          break;
+        }
+    }
+  else
+    {
+      switch (class)
+        {
+        case G_VARIANT_CLASS_MAYBE:
+          {
+            GVariant *value;
+
+            value = g_variant_get_maybe (variant);
+            if (value == NULL)
+              {
+                json_node = json_node_new (JSON_NODE_NULL);
+              }
+            else
+              {
+                json_node = json_gvariant_serialize (value);
+                g_variant_unref (value);
+              }
+
+            break;
+          }
+
+        case G_VARIANT_CLASS_VARIANT:
+          {
+            GVariant *value;
+
+            value = g_variant_get_variant (variant);
+            json_node = json_gvariant_serialize (value);
+            g_variant_unref (value);
+
+            break;
+          }
+
+        case G_VARIANT_CLASS_ARRAY:
+          {
+            const gchar *type;
+
+            type = g_variant_get_type_string (variant);
+
+            if (type[1] == G_VARIANT_CLASS_DICT_ENTRY)
+              /* array of dictionary entries => JsonObject */
+              json_node = gvariant_to_json_object (variant);
+            else
+              /* array of anything else => JsonArray */
+              json_node = gvariant_to_json_array (variant);
+
+            break;
+          }
+
+        case G_VARIANT_CLASS_DICT_ENTRY:
+          {
+            gchar *member_name;
+            JsonObject *object;
+            JsonNode *child;
+
+            /* a single dictionary entry => JsonObject */
+            json_node = json_node_new (JSON_NODE_OBJECT);
+            object = json_object_new ();
+            json_node_set_object (json_node, object);
+            json_object_unref (object);
+
+            child = gvariant_dict_entry_to_json (variant, &member_name);
+
+            json_object_set_member (object, member_name, child);
+            g_free (member_name);
+
+            break;
+          }
+
+        case G_VARIANT_CLASS_TUPLE:
+          json_node = gvariant_to_json_array (variant);
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  return json_node;
+}
+
+/**
+ * json_gvariant_serialize_data:
+ * @variant: A #GVariant to convert
+ * @length: (out) (allow-none): Return location for the length of the returned
+ *   string, or %NULL
+ *
+ * Converts @variant to its JSON encoded string representation. This method
+ * is actually a helper function. It uses json_gvariant_serialize() to obtain the
+ * JSON tree, and then #JsonGenerator to stringify it.
+ *
+ * Return value: (transfer full): The JSON encoded string corresponding to
+ *   @variant
+ *
+ * Since: 0.14
+ */
+gchar *
+json_gvariant_serialize_data (GVariant *variant, gsize *length)
+{
+  JsonNode *json_node;
+  JsonGenerator *generator;
+  gchar *json;
+
+  json_node = json_gvariant_serialize (variant);
+
+  generator = json_generator_new ();
+
+  json_generator_set_root (generator, json_node);
+  json = json_generator_to_data (generator, length);
+
+  g_object_unref (generator);
+
+  json_node_free (json_node);
+
+  return json;
+}
+
+/* ========================================================================== */
+/* JSON to GVariant */
+/* ========================================================================== */
+
+static GVariantClass
+json_to_gvariant_get_next_class (JsonNode     *json_node,
+                                 const gchar **signature)
+{
+  if (signature == NULL)
+    {
+      GVariantClass class = 0;
+
+      switch (json_node_get_node_type (json_node))
+        {
+        case JSON_NODE_VALUE:
+          switch (json_node_get_value_type (json_node))
+            {
+            case G_TYPE_BOOLEAN:
+              class = G_VARIANT_CLASS_BOOLEAN;
+              break;
+
+            case G_TYPE_INT64:
+              class = G_VARIANT_CLASS_INT64;
+              break;
+
+            case G_TYPE_DOUBLE:
+              class = G_VARIANT_CLASS_DOUBLE;
+              break;
+
+            case G_TYPE_STRING:
+              class = G_VARIANT_CLASS_STRING;
+              break;
+            }
+
+          break;
+
+        case JSON_NODE_ARRAY:
+          class = G_VARIANT_CLASS_ARRAY;
+          break;
+
+        case JSON_NODE_OBJECT:
+          class = G_VARIANT_CLASS_DICTIONARY;
+          break;
+
+        case JSON_NODE_NULL:
+          class = G_VARIANT_CLASS_MAYBE;
+          break;
+        }
+
+      return class;
+    }
+  else
+    {
+      if ( (*signature)[0] == G_VARIANT_CLASS_ARRAY
+           && (*signature)[1] == G_VARIANT_CLASS_DICT_ENTRY)
+        return G_VARIANT_CLASS_DICTIONARY;
+      else
+        return (*signature)[0];
+    }
+}
+
+static gboolean
+json_node_assert_type (JsonNode       *json_node,
+                       JsonNodeType    type,
+                       GType           sub_type,
+                       GError        **error)
+{
+  if (JSON_NODE_TYPE (json_node) != type
+      || (type == JSON_NODE_VALUE
+          && (json_node_get_value_type (json_node) != sub_type) ) )
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_DATA,
+                           "Unexpected data-type in JSON node");
+      return FALSE;
+    }
+  else
+    {
+      return TRUE;
+    }
+}
+
+static void
+json_to_gvariant_foreach_add (gpointer data, gpointer user_data)
+{
+  GVariantBuilder *builder = (GVariantBuilder *) user_data;
+  GVariant *child = (GVariant *) data;
+
+  g_variant_builder_add_value (builder, child);
+}
+
+static void
+json_to_gvariant_foreach_free (gpointer data, gpointer user_data)
+{
+  GVariant *child = (GVariant *) data;
+
+  g_variant_unref (child);
+}
+
+static GVariant *
+json_to_gvariant_build_from_glist (GList *list, const gchar *signature)
+{
+  GVariantBuilder *builder;
+  GVariant *result;
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE (signature));
+
+  g_list_foreach (list, json_to_gvariant_foreach_add, builder);
+  result = g_variant_builder_end (builder);
+
+  g_variant_builder_unref (builder);
+
+  return result;
+}
+
+static GVariant *
+json_to_gvariant_tuple (JsonNode     *json_node,
+                        const gchar **signature,
+                        GError      **error)
+{
+  GVariant *variant = NULL;
+  JsonArray *array;
+  gint i;
+  GList *children = NULL;
+  gboolean roll_back = FALSE;
+  const gchar *initial_signature;
+
+  array = json_node_get_array (json_node);
+
+  initial_signature = *signature;
+  (*signature)++;
+  i = 1;
+  while ((*signature)[0] != ')' && (*signature)[0] != '\0')
+    {
+      JsonNode *json_child;
+      GVariant *variant_child;
+
+      if (i - 1 >= json_array_get_length (array))
+        {
+          g_set_error_literal (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_DATA,
+                               "Missing elements in JSON array to conform tuple");
+          roll_back = TRUE;
+          break;
+        }
+
+      json_child = json_array_get_element (array, i - 1);
+
+      variant_child = json_to_gvariant_recurse (json_child, signature, error);
+      if (variant_child != NULL)
+        {
+          children = g_list_append (children, variant_child);
+        }
+      else
+        {
+          roll_back = TRUE;
+          break;
+        }
+
+      i++;
+    }
+
+  if (! roll_back)
+    {
+      if ( (*signature)[0] != ')')
+        {
+          g_set_error_literal (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_DATA,
+                               "Missing closing symbol ')' in GVariant tuple type");
+          roll_back = TRUE;
+        }
+      else if (json_array_get_length (array) >= i)
+        {
+          g_set_error_literal (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_DATA,
+                               "Unexpected extra elements in JSON array");
+          roll_back = TRUE;
+        }
+      else
+        {
+          gchar *tuple_type;
+
+          tuple_type = g_strndup (initial_signature,
+                                  (*signature) - initial_signature + 1);
+
+          variant = json_to_gvariant_build_from_glist (children, tuple_type);
+
+          g_free (tuple_type);
+        }
+    }
+
+  if (roll_back)
+    g_list_foreach (children, json_to_gvariant_foreach_free, NULL);
+
+  g_list_free (children);
+
+  return variant;
+}
+
+static gchar *
+signature_get_next_complete_type (const gchar **signature)
+{
+  GVariantClass class;
+  const gchar *initial_signature;
+  gchar *result;
+
+  /* here it is assumed that 'signature' is a valid type string */
+
+  initial_signature = *signature;
+  class = (*signature)[0];
+
+  if (class == G_VARIANT_CLASS_TUPLE || class == G_VARIANT_CLASS_DICT_ENTRY)
+    {
+      gchar stack[256] = {0};
+      guint stack_len = 0;
+
+      do
+        {
+          if ( (*signature)[0] == G_VARIANT_CLASS_TUPLE)
+            {
+              stack[stack_len] = ')';
+              stack_len++;
+            }
+          else if ( (*signature)[0] == G_VARIANT_CLASS_DICT_ENTRY)
+            {
+              stack[stack_len] = '}';
+              stack_len++;
+            }
+
+          (*signature)++;
+
+          if ( (*signature)[0] == stack[stack_len - 1])
+            stack_len--;
+        }
+      while (stack_len > 0);
+
+      (*signature)++;
+    }
+  else if (class == G_VARIANT_CLASS_ARRAY || class == G_VARIANT_CLASS_MAYBE)
+    {
+      gchar *tmp_sig;
+
+      (*signature)++;
+      tmp_sig = signature_get_next_complete_type (signature);
+      g_free (tmp_sig);
+    }
+  else
+    {
+      (*signature)++;
+    }
+
+  result = g_strndup (initial_signature, (*signature) - initial_signature);
+
+  return result;
+}
+
+static GVariant *
+json_to_gvariant_maybe (JsonNode     *json_node,
+                        const gchar **signature,
+                        GError      **error)
+{
+  GVariant *variant = NULL;
+  GVariant *value;
+  gchar *maybe_signature;
+
+  if (signature)
+    {
+      (*signature)++;
+      maybe_signature = signature_get_next_complete_type (signature);
+    }
+  else
+    {
+      maybe_signature = g_strdup ("v");
+    }
+
+  if (json_node_get_node_type (json_node) == JSON_NODE_NULL)
+    {
+      variant = g_variant_new_maybe (G_VARIANT_TYPE (maybe_signature), NULL);
+    }
+  else
+    {
+      const gchar *tmp_signature;
+
+      tmp_signature = maybe_signature;
+      value = json_to_gvariant_recurse (json_node,
+                                        &tmp_signature,
+                                        error);
+
+      if (value != NULL)
+        variant = g_variant_new_maybe (G_VARIANT_TYPE (maybe_signature), value);
+    }
+
+  g_free (maybe_signature);
+
+  /* compensate the (*signature)++ call at the end of 'recurse()' */
+  if (signature)
+    (*signature)--;
+
+  return variant;
+}
+
+static GVariant *
+json_to_gvariant_array (JsonNode     *json_node,
+                        const gchar **signature,
+                        GError      **error)
+{
+  GVariant *variant = NULL;
+  JsonArray *array;
+  GList *children = NULL;
+  gboolean roll_back = FALSE;
+  const gchar *orig_signature;
+  gchar *child_signature;
+
+  array = json_node_get_array (json_node);
+
+  if (signature != NULL)
+    {
+      orig_signature = *signature;
+
+      (*signature)++;
+      child_signature = signature_get_next_complete_type (signature);
+    }
+  else
+    {
+      child_signature = g_strdup ("v");
+    }
+
+  if (json_array_get_length (array) > 0)
+    {
+      gint i;
+      guint len;
+
+      len = json_array_get_length (array);
+      for (i=0; i<len; i++)
+        {
+          JsonNode *json_child;
+          GVariant *variant_child;
+          const gchar *tmp_signature;
+
+          json_child = json_array_get_element (array, i);
+
+          tmp_signature = child_signature;
+          variant_child = json_to_gvariant_recurse (json_child,
+                                                    &tmp_signature,
+                                                    error);
+          if (variant_child != NULL)
+            {
+              children = g_list_append (children, variant_child);
+            }
+          else
+            {
+              roll_back = TRUE;
+              break;
+            }
+        }
+    }
+
+  if (! roll_back)
+    {
+      gchar *array_signature;
+
+      if (signature)
+        array_signature = g_strndup (orig_signature, (*signature) - orig_signature);
+      else
+        array_signature = g_strdup ("av");
+
+      variant = json_to_gvariant_build_from_glist (children, array_signature);
+
+      g_free (array_signature);
+
+      /* compensate the (*signature)++ call at the end of 'recurse()' */
+      if (signature)
+        (*signature)--;
+    }
+  else
+    {
+      g_list_foreach (children, json_to_gvariant_foreach_free, NULL);
+    }
+
+  g_list_free (children);
+  g_free (child_signature);
+
+  return variant;
+}
+
+static GVariant *
+gvariant_simple_from_string (const gchar    *st,
+                             GVariantClass   class,
+                             GError        **error)
+{
+  GVariant *variant = NULL;
+  gchar *nptr = NULL;
+
+  errno = 0;
+
+  switch (class)
+    {
+    case G_VARIANT_CLASS_BOOLEAN:
+      if (g_strcmp0 (st, "true") == 0)
+        variant = g_variant_new_boolean (TRUE);
+      else if (g_strcmp0 (st, "false") == 0)
+        variant = g_variant_new_boolean (FALSE);
+      else
+        errno = 1;
+      break;
+
+    case G_VARIANT_CLASS_BYTE:
+      variant = g_variant_new_byte (g_ascii_strtoll (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_INT16:
+      variant = g_variant_new_int16 (g_ascii_strtoll (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_UINT16:
+      variant = g_variant_new_uint16 (g_ascii_strtoll (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_INT32:
+      variant = g_variant_new_int32 (g_ascii_strtoll (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_UINT32:
+      variant = g_variant_new_uint32 (g_ascii_strtoull (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_INT64:
+      variant = g_variant_new_int64 (g_ascii_strtoll (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_UINT64:
+      variant = g_variant_new_uint64 (g_ascii_strtoull (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_HANDLE:
+      variant = g_variant_new_handle (strtol (st, &nptr, 10));
+      break;
+
+    case G_VARIANT_CLASS_DOUBLE:
+      variant = g_variant_new_double (g_ascii_strtod (st, &nptr));
+      break;
+
+    case G_VARIANT_CLASS_STRING:
+    case G_VARIANT_CLASS_OBJECT_PATH:
+    case G_VARIANT_CLASS_SIGNATURE:
+      variant = g_variant_new_string (st);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  if (errno != 0 || nptr == st)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_DATA,
+                           "Invalid string value converting to GVariant");
+      if (variant != NULL)
+        {
+          g_variant_unref (variant);
+          variant = NULL;
+        }
+    }
+
+  return variant;
+}
+
+static void
+parse_dict_entry_signature (const gchar **signature,
+                            gchar       **entry_signature,
+                            gchar       **key_signature,
+                            gchar       **value_signature)
+{
+  const gchar *tmp_sig;
+
+  if (signature != NULL)
+    *entry_signature = signature_get_next_complete_type (signature);
+  else
+    *entry_signature = g_strdup ("{sv}");
+
+  tmp_sig = (*entry_signature) + 1;
+  *key_signature = signature_get_next_complete_type (&tmp_sig);
+  *value_signature = signature_get_next_complete_type (&tmp_sig);
+}
+
+static GVariant *
+json_to_gvariant_dict_entry (JsonNode     *json_node,
+                             const gchar **signature,
+                             GError      **error)
+{
+  GVariant *variant = NULL;
+  JsonObject *obj;
+
+  gchar *entry_signature;
+  gchar *key_signature;
+  gchar *value_signature;
+  const gchar *tmp_signature;
+
+  GList *member;
+
+  const gchar *json_member;
+  JsonNode *json_value;
+  GVariant *variant_member;
+  GVariant *variant_value;
+
+  obj = json_node_get_object (json_node);
+
+  if (json_object_get_size (obj) != 1)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_DATA,
+                           "GVariant dictionary entry expects JSON object with exactly one member");
+      return NULL;
+    }
+
+  parse_dict_entry_signature (signature,
+                              &entry_signature,
+                              &key_signature,
+                              &value_signature);
+
+  member = json_object_get_members (obj);
+
+  json_member = (const gchar *) member->data;
+  variant_member = gvariant_simple_from_string (json_member,
+                                                key_signature[0],
+                                                error);
+  if (variant_member != NULL)
+    {
+      json_value = json_object_get_member (obj, json_member);
+
+      tmp_signature = value_signature;
+      variant_value = json_to_gvariant_recurse (json_value,
+                                                &tmp_signature,
+                                                error);
+
+      if (variant_value != NULL)
+        {
+          GVariantBuilder *builder;
+
+          builder = g_variant_builder_new (G_VARIANT_TYPE (entry_signature));
+          g_variant_builder_add_value (builder, variant_member);
+          g_variant_builder_add_value (builder, variant_value);
+          variant = g_variant_builder_end (builder);
+
+          g_variant_builder_unref (builder);
+        }
+    }
+
+  g_list_free (member);
+  g_free (value_signature);
+  g_free (key_signature);
+  g_free (entry_signature);
+
+  /* compensate the (*signature)++ call at the end of 'recurse()' */
+  if (signature)
+    (*signature)--;
+
+  return variant;
+}
+
+static GVariant *
+json_to_gvariant_dictionary (JsonNode     *json_node,
+                             const gchar **signature,
+                             GError      **error)
+{
+  GVariant *variant = NULL;
+  JsonObject *obj;
+  gboolean roll_back = FALSE;
+
+  gchar *dict_signature;
+  gchar *entry_signature;
+  gchar *key_signature;
+  gchar *value_signature;
+  const gchar *tmp_signature;
+
+  GVariantBuilder *builder;
+  GList *members;
+  GList *member;
+
+  obj = json_node_get_object (json_node);
+
+  if (signature != NULL)
+    (*signature)++;
+
+  parse_dict_entry_signature (signature,
+                              &entry_signature,
+                              &key_signature,
+                              &value_signature);
+
+  dict_signature = g_strdup_printf ("a%s", entry_signature);
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE (dict_signature));
+
+  members = json_object_get_members (obj);
+
+  member = members;
+  while (member != NULL)
+    {
+      const gchar *json_member;
+      JsonNode *json_value;
+      GVariant *variant_member;
+      GVariant *variant_value;
+
+      json_member = (const gchar *) member->data;
+      variant_member = gvariant_simple_from_string (json_member,
+                                                    key_signature[0],
+                                                    error);
+      if (variant_member == NULL)
+        {
+          roll_back = TRUE;
+          break;
+        }
+
+      json_value = json_object_get_member (obj, json_member);
+
+      tmp_signature = value_signature;
+      variant_value = json_to_gvariant_recurse (json_value,
+                                                &tmp_signature,
+                                                error);
+
+      if (variant_value != NULL)
+        {
+          g_variant_builder_open (builder, G_VARIANT_TYPE (entry_signature));
+          g_variant_builder_add_value (builder, variant_member);
+          g_variant_builder_add_value (builder, variant_value);
+          g_variant_builder_close (builder);
+        }
+      else
+        {
+          roll_back = TRUE;
+          break;
+        }
+
+      member = member->next;
+    }
+
+  if (! roll_back)
+    variant = g_variant_builder_end (builder);
+
+  g_variant_builder_unref (builder);
+  g_list_free (members);
+  g_free (value_signature);
+  g_free (key_signature);
+  g_free (entry_signature);
+  g_free (dict_signature);
+
+  /* compensate the (*signature)++ call at the end of 'recurse()' */
+  if (signature != NULL)
+    (*signature)--;
+
+  return variant;
+}
+
+static GVariant *
+json_to_gvariant_recurse (JsonNode      *json_node,
+                          const gchar  **signature,
+                          GError       **error)
+{
+  GVariant *variant = NULL;
+  GVariantClass class;
+  gchar class_type[2] = {0, 0};
+
+  class = json_to_gvariant_get_next_class (json_node, signature);
+  class_type[0] = class;
+
+  switch (class)
+    {
+    case G_VARIANT_CLASS_BOOLEAN:
+      if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_BOOLEAN, error))
+        variant = g_variant_new (class_type, json_node_get_boolean (json_node));
+      break;
+
+    case G_VARIANT_CLASS_BYTE:
+    case G_VARIANT_CLASS_INT16:
+    case G_VARIANT_CLASS_UINT16:
+    case G_VARIANT_CLASS_INT32:
+    case G_VARIANT_CLASS_UINT32:
+    case G_VARIANT_CLASS_INT64:
+    case G_VARIANT_CLASS_UINT64:
+    case G_VARIANT_CLASS_HANDLE:
+      if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error))
+        variant = g_variant_new (class_type, json_node_get_int (json_node));
+      break;
+
+    case G_VARIANT_CLASS_DOUBLE:
+      if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_DOUBLE, error))
+        variant = g_variant_new (class_type, json_node_get_double (json_node));
+      break;
+
+    case G_VARIANT_CLASS_STRING:
+    case G_VARIANT_CLASS_OBJECT_PATH:
+    case G_VARIANT_CLASS_SIGNATURE:
+      if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_STRING, error))
+        variant = g_variant_new (class_type, json_node_get_string (json_node));
+      break;
+
+    case G_VARIANT_CLASS_VARIANT:
+      variant = g_variant_new_variant (json_to_gvariant_recurse (json_node,
+                                                                 NULL,
+                                                                 error));
+      break;
+
+    case G_VARIANT_CLASS_MAYBE:
+      variant = json_to_gvariant_maybe (json_node, signature, error);
+      break;
+
+    case G_VARIANT_CLASS_ARRAY:
+      if (json_node_assert_type (json_node, JSON_NODE_ARRAY, 0, error))
+        variant = json_to_gvariant_array (json_node, signature, error);
+      break;
+
+    case G_VARIANT_CLASS_TUPLE:
+      if (json_node_assert_type (json_node, JSON_NODE_ARRAY, 0, error))
+        variant = json_to_gvariant_tuple (json_node, signature, error);
+      break;
+
+    case G_VARIANT_CLASS_DICT_ENTRY:
+      if (json_node_assert_type (json_node, JSON_NODE_OBJECT, 0, error))
+        variant = json_to_gvariant_dict_entry (json_node, signature, error);
+      break;
+
+    case G_VARIANT_CLASS_DICTIONARY:
+      if (json_node_assert_type (json_node, JSON_NODE_OBJECT, 0, error))
+        variant = json_to_gvariant_dictionary (json_node, signature, error);
+      break;
+
+    default:
+      {
+        gchar *err_msg;
+
+        err_msg = g_strdup_printf ("GVariant class '%c' not supported", class);
+        g_set_error_literal (error,
+                             G_IO_ERROR,
+                             G_IO_ERROR_INVALID_DATA,
+                             err_msg);
+        g_free (err_msg);
+        break;
+      }
+    }
+
+  if (signature)
+    (*signature)++;
+
+  return variant;
+}
+
+/**
+ * json_gvariant_deserialize:
+ * @json_node: A #JsonNode to convert
+ * @signature: A valid #GVariant type string, or %NULL
+ * @error: A pointer to a #GError
+ *
+ * Converts a JSON data structure to a GVariant value using @signature to
+ * resolve ambiguous data types. If no error occurs, the resulting #GVariant
+ * is guaranteed to conform to @signature.
+ *
+ * If @signature is not %NULL but does not represent a valid GVariant type
+ * string, %NULL is returned and error is set to %G_IO_ERROR_INVALID_ARGUMENT.
+ * If a @signature is provided but the JSON structure cannot be mapped to it,
+ * %NULL is returned and error is set to %G_IO_ERROR_INVALID_DATA.
+ * If @signature is %NULL, the conversion is done based strictly on the types
+ * in the JSON nodes.
+ *
+ * Return value: (transfer full): A newly created #GVariant compliant with
+ *   @signature, or %NULL on error
+ *
+ * Since: 0.14
+ */
+GVariant *
+json_gvariant_deserialize (JsonNode     *json_node,
+                           const gchar  *signature,
+                           GError      **error)
+{
+  g_return_val_if_fail (json_node != NULL, NULL);
+
+  if (signature != NULL && ! g_variant_type_string_is_valid (signature))
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_ARGUMENT,
+                           "Invalid GVariant type string");
+      return NULL;
+    }
+  else
+    {
+      return json_to_gvariant_recurse (json_node, &signature, error);
+    }
+}
+
+/**
+ * json_gvariant_deserialize_data:
+ * @json: A JSON data string
+ * @length: The length of @json, or -1 if %NULL-terminated
+ * @signature: A valid #GVariant type string, or %NULL
+ * @error: A pointer to a #GError
+ *
+ * Converts a JSON string to a #GVariant value. This method works exactly
+ * like json_gvariant_deserialize(), but takes a JSON encoded string instead.
+ * The string is first converted to a #JsonNode using #JsonParser, and then
+ * json_gvariant_deserialize() is called.
+ *
+ * Returns: (transfer full): A newly created #GVariant compliant with
+ *   @signature, or %NULL on error
+ *
+ * Since: 0.14
+ */
+GVariant *
+json_gvariant_deserialize_data (const gchar  *json,
+                                gssize        length,
+                                const gchar  *signature,
+                                GError      **error)
+{
+  JsonParser *parser;
+  GVariant *variant = NULL;
+  JsonNode *root;
+
+  parser = json_parser_new ();
+
+  if (! json_parser_load_from_data (parser, json, length, error))
+    return NULL;
+
+  root = json_parser_get_root (parser);
+  if (root == NULL)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_DATA,
+                           "JSON data is empty");
+    }
+  else
+    {
+      variant =
+        json_gvariant_deserialize (json_parser_get_root (parser), signature, error);
+    }
+
+  g_object_unref (parser);
+
+  return variant;
+}
diff --git a/json-glib/json-gvariant.h b/json-glib/json-gvariant.h
new file mode 100644
index 0000000..6644b21
--- /dev/null
+++ b/json-glib/json-gvariant.h
@@ -0,0 +1,46 @@
+/* json-gvariant.h - JSON GVariant integration
+ *
+ * This file is part of JSON-GLib
+ * Copyright (C) 2007  OpenedHand Ltd.
+ * Copyright (C) 2009  Intel Corp.
+ *
+ * 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/>.
+ *
+ * Author:
+ *   Eduardo Lima Mitev  <elima igalia com>
+ */
+
+#ifndef __JSON_GVARIANT_H__
+#define __JSON_GVARIANT_H__
+
+#include <glib.h>
+#include <json-glib/json-glib.h>
+
+G_BEGIN_DECLS
+
+JsonNode * json_gvariant_serialize        (GVariant *variant);
+gchar *    json_gvariant_serialize_data   (GVariant *variant,
+                                           gsize    *length);
+
+GVariant * json_gvariant_deserialize      (JsonNode     *json_node,
+                                           const gchar  *signature,
+                                           GError      **error);
+GVariant * json_gvariant_deserialize_data (const gchar  *json,
+                                           gssize        length,
+                                           const gchar  *signature,
+                                           GError      **error);
+
+G_END_DECLS
+
+#endif /* __JSON_GVARIANT_H__ */
diff --git a/json-glib/tests/Makefile.am b/json-glib/tests/Makefile.am
index ad87bcf..defcd2f 100644
--- a/json-glib/tests/Makefile.am
+++ b/json-glib/tests/Makefile.am
@@ -47,4 +47,8 @@ TEST_PROGS             += reader-test
 reader_test_SOURCES     = reader-test.c
 reader_test_LDADD       = $(progs_ldadd)
 
+TEST_PROGS             += gvariant-test
+gvariant_test_SOURCES   = gvariant-test.c
+gvariant_test_LDADD     = $(progs_ldadd)
+
 -include $(top_srcdir)/build/autotools/Makefile.am.gitignore
diff --git a/json-glib/tests/gvariant-test.c b/json-glib/tests/gvariant-test.c
new file mode 100644
index 0000000..e3678cc
--- /dev/null
+++ b/json-glib/tests/gvariant-test.c
@@ -0,0 +1,233 @@
+#include <glib/gtestutils.h>
+#include <json-glib/json-glib.h>
+#include <string.h>
+
+typedef struct
+{
+  gchar *test_name;
+  gchar *signature;
+  gchar *variant_data;
+  gchar *json_data;
+} TestCase;
+
+/* each entry in this list spawns to a GVariant-to-JSON and
+   JSON-to-GVariant test */
+const TestCase test_cases[] =
+  {
+    /* boolean */
+    { "/boolean", "(b)", "(true,)", "[ true ]" },
+
+    /* byte */
+    { "/byte", "(y)", "(byte 0xff,)", "[ 255 ]" },
+
+    /* int16 */
+    { "/int16", "(n)", "(int16 -12345,)", "[ -12345 ]" },
+
+    /* uint16 */
+    { "/uint16", "(q)", "(uint16 40001,)", "[ 40001 ]" },
+
+    /* int32 */
+    { "/int32", "(i)", "(-7654321,)", "[ -7654321 ]" },
+
+    /* uint32 */
+    { "/uint32", "(u)", "(uint32 12345678,)", "[ 12345678 ]" },
+
+    /* int64 */
+    { "/int64", "(x)", "(int64 -666999666999,)", "[ -666999666999 ]" },
+
+    /* uint64 */
+    { "/uint64", "(t)", "(uint64 1999999999999999,)", "[ 1999999999999999 ]" },
+
+    /* handle */
+    { "/handle", "(h)", "(handle 1,)", "[ 1 ]" },
+
+    /* double */
+    { "/double", "(d)", "(1.23,)", "[ 1.23 ]" },
+
+    /* string */
+    { "/string", "(s)", "('hello world!',)", "[ \"hello world!\" ]" },
+
+    /* object-path */
+    { "/object-path", "(o)", "(objectpath '/org/gtk/json_glib',)", "[ \"/org/gtk/json_glib\" ]" },
+
+    /* signature */
+    { "/signature", "(g)", "(signature '(asna{sv}i)',)", "[ \"(asna{sv}i)\" ]" },
+
+    /* maybe - null string */
+    { "/maybe/simple/null", "(ms)", "(@ms nothing,)", "[ null ]" },
+
+    /* maybe - simple string */
+    { "/maybe/simple/string", "(ms)", "(@ms 'maybe string',)", "[ \"maybe string\" ]" },
+
+    /* maybe - null container */
+    { "/maybe/container/null", "(m(sn))", "(@m(sn) nothing,)", "[ null ]" },
+
+    /* maybe - tuple container */
+    { "/maybe/container/tuple", "(m(sn))", "(@m(sn) ('foo', 0),)", "[ [ \"foo\", 0 ] ]" },
+
+    /* maybe - variant boolean */
+    { "/maybe/variant/boolean", "(mv)", "(@mv <true>,)", "[ true ]" },
+
+    /* empty array */
+    { "/array/empty", "as", "@as []", "[ ]" },
+
+    /* array of bytes */
+    { "/array/byte", "ay", "[byte 0x01, 0x0a, 0x03, 0xff]", "[ 1, 10, 3, 255 ]" },
+
+    /* array of strings */
+    { "/array/string", "as", "['a', 'b', 'ab', 'ba']", "[ \"a\", \"b\", \"ab\", \"ba\" ]" },
+
+    /* array of array of int32 */
+    { "/array/array/int32", "aai", "[[1, 2], [3, 4], [5, 6]]", "[ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]" },
+
+    /* array of variants */
+    { "/array/variant", "av", "[<true>, <int64 1>, <'oops'>, <int64 -2>, <0.5>]", "[ true, 1, \"oops\", -2, 0.5 ]" },
+
+    /* tuple */
+    { "/tuple", "(bynqiuxthds)",
+      "(false, byte 0x00, int16 -1, uint16 1, -2, uint32 2, int64 429496729, uint64 3, handle 16, 2.48, 'end')",
+      "[ false, 0, -1, 1, -2, 2, 429496729, 3, 16, 2.48, \"end\" ]" },
+
+    /* empty dictionary */
+    { "/dictionary/empty", "a{sv}", "@a{sv} {}", "{ }" },
+
+    /* single dictionary entry */
+    { "/dictionary/single-entry", "{ss}", "{'hello', 'world'}", "{ \"hello\" : \"world\" }" },
+
+    /* dictionary - string : int32 */
+    { "/dictionary/string-int32", "a{si}", "{'foo': 1, 'bar': 2}", "{ \"foo\" : 1, \"bar\" : 2 }" },
+
+    /* dictionary - string : variant */
+    { "/dictionary/string-variant", "a{sv}", "{'str': <'hi!'>, 'bool': <true>}", "{ \"str\" : \"hi!\", \"bool\" : true }" },
+
+    /* dictionary - int64 : variant */
+    { "/dictionary/int64-variant", "a{xv}",
+      "{int64 -5: <'minus five'>, 10: <'ten'>}", "{ \"-5\" : \"minus five\", \"10\" : \"ten\" }" },
+
+    /* dictionary - uint64 : variant */
+    { "/dictionary/uint64-boolean", "a{tb}",
+      "{uint64 999888777666: true, 0: false}", "{ \"999888777666\" : true, \"0\" : false }" },
+
+    /* dictionary - boolean : variant */
+    { "/dictionary/boolean-variant", "a{by}", "{true: byte 0x01, false: 0x00}", "{ \"true\" : 1, \"false\" : 0 }" },
+
+    /* dictionary - double : string */
+    { "/dictionary/double-string", "a{ds}", "{1.0: 'one point zero'}", "{ \"1.000000\" : \"one point zero\" }" },
+
+    /* variant - string */
+    { "/variant/string", "(v)", "(<'string within variant'>,)", "[ \"string within variant\" ]" },
+
+    /* variant - maybe null  */
+    { "/variant/maybe/null", "(v)", "(<@mv nothing>,)", "[ null ]" },
+
+    /* variant - dictionary */
+    { "/variant/dictionary", "v", "<{'foo': <'bar'>, 'hi': <int64 1024>}>", "{ \"foo\" : \"bar\", \"hi\" : 1024 }" },
+
+    /* variant - variant - array */
+    { "/variant/variant/array", "v", "<[<'any'>, <'thing'>, <int64 0>, <int64 -1>]>", "[ \"any\", \"thing\", 0, -1 ]" },
+
+    /* deep-nesting */
+    { "/deep-nesting",
+      "a(a(a(a(a(a(a(a(a(a(s))))))))))",
+      "[([([([([([([([([([('sorprise',)],)],)],)],)],)],)],)],)],)]",
+      "[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ \"sorprise\" ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ]" },
+
+    /* mixed1 */
+    { "/mixed1",
+      "a{s(syba(od))}",
+      "{'foo': ('bar', byte 0x64, true, [(objectpath '/baz', 1.3), ('/cat', -2.5)])}",
+      "{ \"foo\" : [ \"bar\", 100, true, [ [ \"/baz\", 1.3 ], [ \"/cat\", -2.5 ] ] ] }" },
+
+    /* mixed2 */
+    { "/mixed2",
+      "(a{by}amsvmaba{qm(sg)})",
+      "({true: byte 0x01, false: 0x00}, [ ms 'do', nothing, 'did'], <@av []>, @mab nothing, {uint16 10000: @m(sg) ('yes', 'august'), 0: nothing})",
+      "[ { \"true\" : 1, \"false\" : 0 }, [ \"do\", null, \"did\" ], [ ], null, { \"10000\" : [ \"yes\", \"august\" ], \"0\" : null } ]" },
+  };
+
+static void
+test_gvariant_to_json (gconstpointer test_data)
+{
+  TestCase *test_case = (TestCase *) test_data;
+  GVariant *variant;
+  gchar *json_data;
+  gsize len;
+
+  variant = g_variant_parse (G_VARIANT_TYPE (test_case->signature),
+                             test_case->variant_data,
+                             NULL,
+                             NULL,
+                             NULL);
+
+  json_data = json_gvariant_serialize_data (variant, &len);
+  g_assert (json_data != NULL);
+
+  g_assert_cmpstr (test_case->json_data, ==, json_data);
+
+  g_free (json_data);
+  g_variant_unref (variant);
+}
+
+static void
+test_json_to_gvariant (gconstpointer test_data)
+{
+  TestCase *test_case = (TestCase *) test_data;
+  GVariant *variant;
+  gchar *variant_data;
+  GError *error = NULL;
+
+  variant = json_gvariant_deserialize_data (test_case->json_data,
+                                            -1,
+                                            test_case->signature,
+                                            &error);
+
+  if (variant == NULL)
+    {
+      g_assert_no_error (error);
+      g_error_free (error);
+    }
+  else
+    {
+      variant_data = g_variant_print (variant, TRUE);
+
+      g_assert_cmpstr (test_case->variant_data, ==, variant_data);
+
+      g_free (variant_data);
+      g_variant_unref (variant);
+    }
+}
+
+gint
+main (gint argc, gchar *argv[])
+{
+  gint i;
+  TestCase test_case;
+  gchar *test_name;
+
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  /* GVariant to JSON */
+  for (i = 0; i < sizeof (test_cases) / sizeof (TestCase); i++)
+    {
+      test_case = test_cases[i];
+      test_name = g_strdup_printf ("/gvariant/to-json/%s", test_case.test_name);
+
+      g_test_add_data_func (test_name, &test_cases[i], test_gvariant_to_json);
+
+      g_free (test_name);
+    }
+
+  /* JSON to GVariant */
+  for (i = 0; i < sizeof (test_cases) / sizeof (TestCase); i++)
+    {
+      test_case = test_cases[i];
+      test_name = g_strdup_printf ("/gvariant/from-json/%s", test_case.test_name);
+
+      g_test_add_data_func (test_name, &test_cases[i], test_json_to_gvariant);
+
+      g_free (test_name);
+    }
+
+  return g_test_run ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]