From 87a36b8a5e23f3cf80730627f9b89061934a5bbf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Oct 2007 17:40:06 +0100 Subject: Add the JsonSerializable interface The JsonSerializable interface allows implementations to override the GObject-to-JSON serialization process, by providing two virtual methods to control the (de)serialization of GObject properties. This way it's possible to serialize GObjects with properties holding complex data types. --- json-glib/json-gobject.c | 188 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 31 deletions(-) (limited to 'json-glib/json-gobject.c') diff --git a/json-glib/json-gobject.c b/json-glib/json-gobject.c index ee652aa..d02a7bf 100644 --- a/json-glib/json-gobject.c +++ b/json-glib/json-gobject.c @@ -24,10 +24,12 @@ * JSON-GLib provides API for serializing and deserializing #GObjects * to and from JSON data streams. * - * The simplest form to serialize a #GObject class is calling the - * json_serialize_gobject() function on a #GObject instance: every - * property using a fundamental type (or a type that can be coherced - * into a fundamental type) will be converted into a JSON type. + * Simple #GObject classes can be (de)serialized into JSON objects, if the + * properties have compatible types with the native JSON types (integers, + * booleans, strings, string vectors). If the class to be (de)serialized has + * complex data types for properties (like boxed types or other objects) + * then the class should implement the provided #JsonSerializable interface + * and its virtual functions. */ #include "config.h" @@ -39,47 +41,151 @@ #include "json-parser.h" #include "json-generator.h" +GType +json_serializable_get_type (void) +{ + static GType iface_type = 0; + + return iface_type; +} + +/** + * json_serializable_serialize_property: + * @serializable: a #JsonSerializable object + * @property_name: the name of the property + * @value: the value of the property + * @pspec: a #GParamSpec + * + * Asks a #JsonSerializable implementation to serialize a #GObject + * property into a #JsonNode object. + * + * Return value: a #JsonNode containing the serialize property + */ +JsonNode * +json_serializable_serialize_property (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec) +{ + JsonSerializableIface *iface; + + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL); + g_return_val_if_fail (property_name != NULL, NULL); + g_return_val_if_fail (value != NULL, NULL); + g_return_val_if_fail (pspec != NULL, NULL); + + iface = JSON_SERIALIZABLE_GET_IFACE (serializable); + if (!iface->serialize_property) + return json_node_new (JSON_NODE_NULL); + + return iface->serialize_property (serializable, property_name, value, pspec); +} + +/** + * json_serializable_deserialize_property: + * @serializable: a #JsonSerializable + * @property_name: the name of the property + * @value: a pointer to an uninitialized #GValue + * @pspec: a #GParamSpec + * @property_node: a #JsonNode containing the serialized property + * + * Asks a #JsonSerializable implementation to deserialize the + * property contained inside @property_node into @value. + * + * Return value: %TRUE if the property was successfully deserialized. + */ +gboolean +json_serializable_deserialize_property (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node) +{ + JsonSerializableIface *iface; + + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), FALSE); + g_return_val_if_fail (property_name != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (pspec != NULL, FALSE); + g_return_val_if_fail (property_node != NULL, FALSE); + + iface = JSON_SERIALIZABLE_GET_IFACE (serializable); + if (!iface->deserialize_property) + { + g_param_value_defaults (pspec, value); + return TRUE; + } + + return iface->deserialize_property (serializable, + property_name, + value, + pspec, + property_node); +} + static JsonNode * -json_serialize_pspec (GObject *gobject, - GParamSpec *pspec) +json_serialize_pspec (const GValue *real_value, + GParamSpec *pspec) { JsonNode *retval = NULL; - GValue real_value = { 0, }; GValue value = { 0, }; - g_value_init (&real_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - g_object_get_property (gobject, pspec->name, &real_value); - - if (!G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (&real_value))) + if (!G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (real_value))) { g_warning ("Complex types are not supported."); return NULL; } - switch (G_VALUE_TYPE (&real_value)) + switch (G_VALUE_TYPE (real_value)) { case G_TYPE_INT: case G_TYPE_BOOLEAN: - case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: /* JSON native types */ retval = json_node_new (JSON_NODE_VALUE); - g_value_init (&value, G_VALUE_TYPE (&real_value)); - g_value_copy (&real_value, &value); + g_value_init (&value, G_VALUE_TYPE (real_value)); + g_value_copy (real_value, &value); json_node_set_value (retval, &value); g_value_unset (&value); break; case G_TYPE_STRING: /* strings might be NULL */ - if (!g_value_get_string (&real_value)) + if (!g_value_get_string (real_value)) retval = json_node_new (JSON_NODE_NULL); else { retval = json_node_new (JSON_NODE_VALUE); - g_value_init (&value, G_TYPE_STRING); - g_value_set_string (&value, g_value_get_string (&real_value)); - json_node_set_value (retval, &value); - g_value_unset (&value); + json_node_set_string (retval, g_value_get_string (real_value)); + break; + } + break; + + case G_TYPE_BOXED: + if (G_VALUE_HOLDS (real_value, G_TYPE_STRV)) + { + gchar **strv = g_value_get_boxed (real_value); + gint i, strv_len; + JsonArray *array; + + strv_len = g_strv_length (strv); + array = json_array_sized_new (strv_len); + + for (i = 0; i < strv_len; i++) + { + JsonNode *str = json_node_new (JSON_NODE_VALUE); + + json_node_set_string (str, strv[i]); + json_array_add_element (array, str); + } + + retval = json_node_new (JSON_NODE_ARRAY); + json_node_take_array (retval, array); + } + else + { + g_warning ("Unsupported type `%s'", + g_type_name (G_VALUE_TYPE (real_value))); } break; @@ -93,7 +199,7 @@ json_serialize_pspec (GObject *gobject, /* these should fit into an int */ retval = json_node_new (JSON_NODE_VALUE); g_value_init (&value, G_TYPE_INT); - g_value_copy (&real_value, &value); + g_value_copy (real_value, &value); json_node_set_value (retval, &value); g_value_unset (&value); break; @@ -104,12 +210,10 @@ json_serialize_pspec (GObject *gobject, default: g_warning ("Unsupported type `%s'", - g_type_name (G_VALUE_TYPE (&real_value))); + g_type_name (G_VALUE_TYPE (real_value))); break; } - g_value_unset (&real_value); - return retval; } @@ -118,7 +222,10 @@ json_serialize_pspec (GObject *gobject, * @gobject: a #GObject * @length: return value for the length of the buffer, or %NULL * - * Serializes a #GObject into a JSON data stream. + * Serializes a #GObject into a JSON data stream. If @gobject implements + * the #JsonSerializableIface interface, it will be responsible to + * serizalize all its properties; otherwise, the default implementation + * will be use to translate the compatible types into JSON native types. * * Return value: a JSON data stream representing the passed #GObject */ @@ -126,6 +233,7 @@ gchar * json_serialize_gobject (GObject *gobject, gsize *length) { + JsonSerializableIface *iface = NULL; JsonGenerator *gen; JsonNode *root; JsonObject *object; @@ -133,7 +241,10 @@ json_serialize_gobject (GObject *gobject, guint n_pspecs, i; gchar *data; - g_return_val_if_fail (G_IS_OBJECT (gobject), NULL); + g_return_val_if_fail (G_OBJECT (gobject), NULL); + + if (JSON_IS_SERIALIZABLE (gobject)) + iface = JSON_SERIALIZABLE_GET_IFACE (gobject); object = json_object_new (); @@ -142,19 +253,34 @@ json_serialize_gobject (GObject *gobject, pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (gobject), &n_pspecs); - for (i = 0; i < n_pspecs; i++) { GParamSpec *pspec = pspecs[i]; - JsonNode *value; + GValue value = { 0, }; + JsonNode *node; /* read only what we can */ if (!(pspec->flags & G_PARAM_READABLE)) continue; - value = json_serialize_pspec (gobject, pspec); - if (value) - json_object_add_member (object, pspec->name, value); + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + g_object_get_property (gobject, pspec->name, &value); + + if (iface && iface->serialize_property) + { + JsonSerializable *serializable = JSON_SERIALIZABLE (gobject); + + node = iface->serialize_property (serializable, pspec->name, + &value, + pspec); + } + else + node = json_serialize_pspec (&value, pspec); + + if (node) + json_object_add_member (object, pspec->name, node); + + g_value_unset (&value); } g_free (pspecs); -- cgit v1.2.1