summaryrefslogtreecommitdiff
path: root/json-glib/json-gobject.c
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@linux.intel.com>2009-10-27 17:53:34 +0000
committerEmmanuele Bassi <ebassi@linux.intel.com>2009-10-27 17:53:34 +0000
commitff986ee5b8df45255f4f5ab01be0bbad893bc55e (patch)
tree16d565e41225266c2d762f174d1f238c7c72d2d6 /json-glib/json-gobject.c
parent7f6a73a0964b66b15e8b5a9858b9bc76b010f67b (diff)
downloadjson-glib-ff986ee5b8df45255f4f5ab01be0bbad893bc55e.tar.gz
gobject: Add experimental GBoxed<->JSON transformation
Serializing and deserializing GBoxed types is fairly complicated currently. If a GObject implements JsonSerializable it is possible for the class to intercept the JsonNode, parse it manually and then set the value to the property. This leaves a hole opened for: • manual (de)serialization of GBoxed types • (de)serialization of GBoxed properties in classes not implementing JsonSerializable In order to serialize and deserialize a GBoxed JSON-GLib should provide a mechanism similar to the GValue transformation functions: when registering the boxed type the developer should also be able to register a serialization and a deserialization functions pair matching the tuple: (GBoxed type, JSON type) The serialization function would be: JsonNode *(* JsonBoxedSerializeFunc) (gconstpointer boxed); And, conversely, the deserialization function would be: gpointer (* JsonBoxedDeserializeFunc) (JsonNode *node); Obviously, the whole machinery works only for GBoxed types that register the serialization and deserialization functions.
Diffstat (limited to 'json-glib/json-gobject.c')
-rw-r--r--json-glib/json-gobject.c264
1 files changed, 262 insertions, 2 deletions
diff --git a/json-glib/json-gobject.c b/json-glib/json-gobject.c
index 2a8ec94..7c9a04d 100644
--- a/json-glib/json-gobject.c
+++ b/json-glib/json-gobject.c
@@ -45,6 +45,243 @@
#include "json-parser.h"
#include "json-generator.h"
+typedef struct _BoxedTransform BoxedTransform;
+
+struct _BoxedTransform
+{
+ GType boxed_type;
+ gint node_type;
+
+ JsonBoxedSerializeFunc serialize;
+ JsonBoxedDeserializeFunc deserialize;
+};
+
+G_LOCK_DEFINE_STATIC (boxed_transforms);
+static GSList *boxed_transforms = NULL;
+
+static gint
+boxed_transforms_cmp (gconstpointer a,
+ gconstpointer b)
+{
+ const BoxedTransform *ta = a;
+ const BoxedTransform *tb = b;
+
+ return tb->boxed_type - ta->boxed_type;
+}
+
+static gint
+boxed_transforms_find (gconstpointer a,
+ gconstpointer b)
+{
+ const BoxedTransform *haystack = a;
+ const BoxedTransform *needle = b;
+
+ if (needle->node_type != -1)
+ return (haystack->boxed_type == needle->boxed_type &&
+ haystack->node_type == needle->node_type) ? 0 : 1;
+ else
+ return (haystack->boxed_type == needle->boxed_type) ? 0 : 1;
+}
+
+static BoxedTransform *
+lookup_boxed_transform (GType gboxed_type,
+ JsonNodeType node_type)
+{
+ BoxedTransform lookup;
+ GSList *t;
+
+ lookup.boxed_type = gboxed_type;
+ lookup.node_type = node_type;
+
+ t = g_slist_find_custom (boxed_transforms, &lookup, boxed_transforms_find);
+ if (t == NULL)
+ return NULL;
+
+ return t->data;
+}
+
+/**
+ * json_boxed_register_transform_func:
+ * @gboxed_type: a boxed type
+ * @node_type: a node type
+ * @serialize_func: (allow-none): serialization function for @boxed_type
+ * into a #JsonNode of type @node_type; can be %NULL if @deserialize_func
+ * is not %NULL
+ * @deserialize_func: (allow-none): deserialization function for @boxed_type
+ * from a #JsonNode of type @node_type; can be %NULL if @serialize_func
+ * is not %NULL
+ *
+ * Registers a serialization and deserialization functions for a #GBoxed
+ * of type @gboxed_type to and from a #JsonNode of type @node_type
+ *
+ * Since: 0.10
+ */
+void
+json_boxed_register_transform_func (GType gboxed_type,
+ JsonNodeType node_type,
+ JsonBoxedSerializeFunc serialize_func,
+ JsonBoxedDeserializeFunc deserialize_func)
+{
+ BoxedTransform *t;
+
+ g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type));
+ g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE);
+
+ if (serialize_func == NULL)
+ g_return_if_fail (deserialize_func != NULL);
+
+ if (deserialize_func == NULL)
+ g_return_if_fail (serialize_func != NULL);
+
+ G_LOCK (boxed_transforms);
+
+ t = lookup_boxed_transform (gboxed_type, node_type);
+ if (t == NULL)
+ {
+ t = g_slice_new (BoxedTransform);
+
+ t->boxed_type = gboxed_type;
+ t->node_type = node_type;
+ t->serialize = serialize_func;
+ t->deserialize = deserialize_func;
+
+ boxed_transforms = g_slist_insert_sorted (boxed_transforms, t,
+ boxed_transforms_cmp);
+ }
+ else
+ g_warning ("A transformation for the boxed type %s into "
+ "JSON nodes of type %s already exists",
+ g_type_name (gboxed_type),
+ json_node_type_get_name (node_type));
+
+ G_UNLOCK (boxed_transforms);
+}
+
+/**
+ * json_boxed_can_serialize:
+ * @gboxed_type: a boxed type
+ * @node_type: (out): the #JsonNode type to which the boxed type can be
+ * deserialized into
+ *
+ * Checks whether it is possible to serialize a #GBoxed of
+ * type @gboxed_type into a #JsonNode of type @node_type
+ *
+ * Return value: %TRUE if the type can be serialized, %FALSE otherwise
+ *
+ * Since: 0.10
+ */
+gboolean
+json_boxed_can_serialize (GType gboxed_type,
+ JsonNodeType *node_type)
+{
+ BoxedTransform *t;
+
+ g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE);
+ g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE);
+
+ t = lookup_boxed_transform (gboxed_type, -1);
+ if (t != NULL && t->serialize != NULL)
+ {
+ if (node_type)
+ *node_type = t->node_type;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * json_boxed_can_deserialize:
+ * @gboxed_type: a boxed type
+ * @node_type: a #JsonNode type
+ *
+ * Checks whether it is possible to deserialize a #GBoxed of
+ * type @gboxed_type from a #JsonNode of type @node_type
+ *
+ * Return value: %TRUE if the type can be deserialized, %FALSE otherwise
+ *
+ * Since: 0.10
+ */
+gboolean
+json_boxed_can_deserialize (GType gboxed_type,
+ JsonNodeType node_type)
+{
+ BoxedTransform *t;
+
+ g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE);
+ g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE);
+
+ t = lookup_boxed_transform (gboxed_type, node_type);
+ if (t != NULL && t->deserialize != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * json_boxed_serialize:
+ * @gboxed_type: a boxed type
+ * @node_type: a #JsonNode type
+ * @boxed: a pointer to a #GBoxed of type @gboxed_type
+ *
+ * Serializes @boxed, a pointer to a #GBoxed of type @gboxed_type,
+ * into a #JsonNode of type @node_type
+ *
+ * Return value: a #JsonNode with the serialization of the boxed
+ * type, or %NULL if serialization either failed or was not
+ * possible
+ *
+ * Since: 0.10
+ */
+JsonNode *
+json_boxed_serialize (GType gboxed_type,
+ JsonNodeType node_type,
+ gconstpointer boxed)
+{
+ BoxedTransform *t;
+
+ g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL);
+ g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL);
+ g_return_val_if_fail (boxed != NULL, NULL);
+
+ t = lookup_boxed_transform (gboxed_type, node_type);
+ if (t != NULL && t->serialize != NULL)
+ return t->serialize (boxed);
+
+ return NULL;
+}
+
+/**
+ * json_boxed_serialize:
+ * @gboxed_type: a boxed type
+ * @node: a #JsonNode
+ *
+ * Deserializes @node into @boxed, a pointer to a #GBoxed of type
+ * @gboxed_type
+ *
+ * Since: 0.10
+ */
+gpointer
+json_boxed_deserialize (GType gboxed_type,
+ JsonNode *node)
+{
+ JsonNodeType node_type;
+ BoxedTransform *t;
+
+ g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL);
+ g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL);
+ g_return_val_if_fail (node != NULL, NULL);
+
+ node_type = json_node_get_node_type (node);
+
+ t = lookup_boxed_transform (gboxed_type, node_type);
+ if (t != NULL && t->deserialize != NULL)
+ return t->deserialize (node);
+
+ return NULL;
+}
+
/* forward declaration */
static JsonNode *json_serialize_pspec (const GValue *real_value,
GParamSpec *pspec);
@@ -396,6 +633,21 @@ json_deserialize_pspec (GValue *value,
GValue node_value = { 0, };
gboolean retval = FALSE;
+ if (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)) == G_TYPE_BOXED)
+ {
+ JsonNodeType node_type = json_node_get_node_type (node);
+ GType boxed_type = G_VALUE_TYPE (value);
+
+ if (json_boxed_can_deserialize (boxed_type, node_type))
+ {
+ gpointer boxed = json_boxed_deserialize (boxed_type, node);
+
+ g_value_take_boxed (value, boxed);
+
+ return TRUE;
+ }
+ }
+
switch (JSON_NODE_TYPE (node))
{
case JSON_NODE_OBJECT:
@@ -554,6 +806,7 @@ json_serialize_pspec (const GValue *real_value,
{
JsonNode *retval = NULL;
GValue value = { 0, };
+ JsonNodeType node_type;
switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (real_value)))
{
@@ -611,10 +864,17 @@ json_serialize_pspec (const GValue *real_value,
retval = json_node_new (JSON_NODE_ARRAY);
json_node_take_array (retval, array);
}
- else
+ else if (json_boxed_can_serialize (G_VALUE_TYPE (real_value), &node_type))
{
- g_warning ("Unsupported type `%s'", g_type_name (G_VALUE_TYPE (real_value)));
+ gpointer boxed = g_value_get_boxed (real_value);
+
+ retval = json_boxed_serialize (G_VALUE_TYPE (real_value),
+ node_type,
+ boxed);
}
+ else
+ g_warning ("Boxed type '%s' is not handled by JSON-GLib",
+ g_type_name (G_VALUE_TYPE (real_value)));
break;
case G_TYPE_UINT: