diff options
-rw-r--r-- | doc/reference/json-glib-sections.txt | 15 | ||||
-rw-r--r-- | json-glib/json-gobject.c | 188 | ||||
-rw-r--r-- | json-glib/json-gobject.h | 51 | ||||
-rw-r--r-- | json-glib/json-types.h | 1 |
4 files changed, 224 insertions, 31 deletions
diff --git a/doc/reference/json-glib-sections.txt b/doc/reference/json-glib-sections.txt index 176e877..46d78ae 100644 --- a/doc/reference/json-glib-sections.txt +++ b/doc/reference/json-glib-sections.txt @@ -143,7 +143,22 @@ json_generator_get_type <SECTION> <FILE>json-gobject</FILE> <TITLE>GObject integration</TITLE> +JsonSerializableIface +json_serializable_serialize_property +json_serializable_deserialize_property + +<SUBSECTION> json_serialize_gobject + +<SUBSECTION Standard> +JSON_TYPE_SERIALIZABLE +JSON_SERIALIZABLE +JSON_IS_SERIALIZABLE +JSON_SERIALIZABLE_GET_IFACE + +<SUBSECTION Private> +JsonSerializable +json_serializable_get_type </SECTION> <SECTION> 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 #GObject<!-- -->s * 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); diff --git a/json-glib/json-gobject.h b/json-glib/json-gobject.h index 5f3b8b4..abd2f95 100644 --- a/json-glib/json-gobject.h +++ b/json-glib/json-gobject.h @@ -25,6 +25,57 @@ G_BEGIN_DECLS +#define JSON_TYPE_SERIALIZABLE (json_serializable_get_type ()) +#define JSON_SERIALIZABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_SERIALIZABLE, JsonSerializable)) +#define JSON_IS_SERIALIZABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_SERIALIZABLE)) +#define JSON_SERIALIZABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), JSON_TYPE_SERIALIZABLE, JsonSerializableIface)) + +typedef struct _JsonSerializable JsonSerializable; /* dummy */ +typedef struct _JsonSerializableIface JsonSerializableIface; + +/** + * JsonSerializableIface: + * @serialize_property: virtual function for serializing a #GObject property + * into a #JsonNode + * @deserialize_property: virtual function for deserializing a #JsonNode + * into a #GObject property + * + * Interface that allows serializing and deserializing #GObject<!-- -->s + * with properties storing complex data types. The json_serialize_gobject() + * function will check if the passed #GObject implements this interface, + * so it can also be used to override the default property serialization + * sequence. + */ +struct _JsonSerializableIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + JsonNode *(* serialize_property) (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec); + gboolean (* deserialize_property) (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node); +}; + +GType json_serializable_get_type (void) G_GNUC_CONST; + +JsonNode *json_serializable_serialize_property (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec); +gboolean json_serializable_deserialize_property (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node); + + gchar *json_serialize_gobject (GObject *gobject, gsize *length); diff --git a/json-glib/json-types.h b/json-glib/json-types.h index 323bf87..2b4ba14 100644 --- a/json-glib/json-types.h +++ b/json-glib/json-types.h @@ -70,6 +70,7 @@ typedef enum { /** * JsonNode: + * @type: the type of node * * A generic container of JSON data types. The contents of the #JsonNode * structure are private and should only be accessed via the provided |