[json-glib] gvariant: Adds JSON GVariant integration API, with docs and tests
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [json-glib] gvariant: Adds JSON GVariant integration API, with docs and tests
- Date: Wed, 26 Jan 2011 17:03:14 +0000 (UTC)
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]