diff options
author | Emmanuele Bassi <ebassi@linux.intel.com> | 2009-10-27 17:53:34 +0000 |
---|---|---|
committer | Emmanuele Bassi <ebassi@linux.intel.com> | 2009-10-27 17:53:34 +0000 |
commit | ff986ee5b8df45255f4f5ab01be0bbad893bc55e (patch) | |
tree | 16d565e41225266c2d762f174d1f238c7c72d2d6 /json-glib/json-gobject.c | |
parent | 7f6a73a0964b66b15e8b5a9858b9bc76b010f67b (diff) | |
download | json-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.c | 264 |
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: |