From 45c697263364a975fd6a54373f999f2932436b6f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sat, 10 Nov 2007 01:26:46 +0000 Subject: Initial implementation of GObject deserialization function The json_construct_gobject() function takes a GType and a JSON data stream and constructs a new instance for the given type. If the type is a JsonSerializable, it will use the JsonSerializable interface for parsing the JsonNodes of each object member. This is the initial implementation of the function: the JsonNode-to-GValue fallback parser is just a stub. --- json-glib/json-gobject.c | 182 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 170 insertions(+), 12 deletions(-) (limited to 'json-glib/json-gobject.c') diff --git a/json-glib/json-gobject.c b/json-glib/json-gobject.c index f6f8b57..ba5ce5b 100644 --- a/json-glib/json-gobject.c +++ b/json-glib/json-gobject.c @@ -129,6 +129,22 @@ json_serializable_deserialize_property (JsonSerializable *serializable, property_node); } +static gboolean +json_deserialize_pspec (GValue *value, + GParamSpec *pspec, + JsonNode *node) +{ + gboolean retval = FALSE; + GValue node_value = { 0, }; + + if (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT) + { + return FALSE; + } + + return retval; +} + static JsonNode * json_serialize_pspec (const GValue *real_value, GParamSpec *pspec) @@ -136,13 +152,7 @@ json_serialize_pspec (const GValue *real_value, JsonNode *retval = NULL; GValue value = { 0, }; - 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_TYPE_FUNDAMENTAL (G_VALUE_TYPE (real_value))) { case G_TYPE_INT: case G_TYPE_BOOLEAN: @@ -155,6 +165,11 @@ json_serialize_pspec (const GValue *real_value, g_value_unset (&value); break; + case G_TYPE_FLOAT: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_double (retval, (gdouble) g_value_get_float (real_value)); + break; + case G_TYPE_STRING: /* strings might be NULL */ if (!g_value_get_string (real_value)) @@ -196,18 +211,32 @@ json_serialize_pspec (const GValue *real_value, break; case G_TYPE_UINT: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, (gint) g_value_get_uint (real_value)); + break; + case G_TYPE_LONG: case G_TYPE_ULONG: + break; + case G_TYPE_CHAR: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, (gint) g_value_get_char (real_value)); + break; + case G_TYPE_UCHAR: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, (gint) g_value_get_uchar (real_value)); + break; + case G_TYPE_ENUM: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_enum (real_value)); + break; + case G_TYPE_FLAGS: - /* 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); - json_node_set_value (retval, &value); - g_value_unset (&value); + json_node_set_int (retval, g_value_get_flags (real_value)); break; case G_TYPE_NONE: @@ -223,6 +252,135 @@ json_serialize_pspec (const GValue *real_value, return retval; } +/** + * json_construct_gobject: + * @gtype: the #GType of object to construct + * @data: a JSON data stream + * @length: length of the data stream, or -1 if it is NUL-terminated + * @error: return location for a #GError, or %NULL + * + * Deserializes a JSON data stream and creates the corresponding + * #GObject class. If @gtype implements the #JsonSerializableIface + * interface, it will be responsible to deserialize all the JSON + * members into the respective properties; otherwise, the default + * implementation will be used to translate the compatible JSON + * native types. + * + * Return value: a #GObject or %NULL + * + * Since: 0.4 + */ +GObject * +json_construct_gobject (GType gtype, + const gchar *data, + gsize length, + GError **error) +{ + JsonSerializableIface *iface = NULL; + JsonSerializable *serializable = NULL; + JsonParser *parser; + JsonNode *root; + JsonObject *object; + GError *parse_error; + GList *members, *l; + guint n_members; + GObjectClass *klass; + GObject *retval; + + g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); + g_return_val_if_fail (data != NULL, NULL); + + if (length < 0) + length = strlen (data); + + parser = json_parser_new (); + + parse_error = NULL; + json_parser_load_from_data (parser, data, length, &parse_error); + if (parse_error) + { + g_propagate_error (error, parse_error); + g_object_unref (parser); + return NULL; + } + + root = json_parser_get_root (parser); + if (JSON_NODE_TYPE (root) != JSON_NODE_OBJECT) + { + g_set_error (error, JSON_PARSER_ERROR, + JSON_PARSER_ERROR_PARSE, + "Expecting a JSON object, but the root node " + "is of type `%s'", + json_node_type_name (root)); + g_object_unref (parser); + return NULL; + } + + klass = g_type_class_ref (gtype); + retval = g_object_new (gtype, NULL); + + if (g_type_is_a (gtype, JSON_TYPE_SERIALIZABLE)) + { + serializable = JSON_SERIALIZABLE (retval); + iface = JSON_SERIALIZABLE_GET_IFACE (serializable); + } + + object = json_node_get_object (root); + + g_object_freeze_notify (retval); + + n_members = json_object_get_size (object); + members = json_object_get_members (object); + + for (l = members; l != NULL; l = l->next) + { + const gchar *member_name = l->data; + GParamSpec *pspec; + JsonNode *val; + GValue value = { 0, }; + gboolean res; + + pspec = g_object_class_find_property (klass, member_name); + if (!pspec) + continue; + + if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY)) + continue; + + if (!(pspec->flags & G_PARAM_WRITABLE)) + continue; + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + val = json_object_get_member (object, member_name); + + if (iface && iface->deserialize_property) + { + + res = iface->deserialize_property (serializable, pspec->name, + &value, + pspec, + val); + } + else + res = json_deserialize_pspec (&value, pspec, val); + + if (res) + g_object_set_property (retval, pspec->name, &value); + + g_value_unset (&value); + } + + g_list_free (members); + + g_object_thaw_notify (retval); + + g_type_class_unref (klass); + g_object_unref (parser); + + return retval; +} + /** * json_serialize_gobject: * @gobject: a #GObject -- cgit v1.2.1 From 4b496c7d906c81dceafe6a5f442b55b6e161f762 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 13 Nov 2007 10:11:00 +0000 Subject: Add node-to-property simple fallback parser for deserialization The fallback parser for json_construct_gobject() is invoked if the GType does not implement the JsonSerializable interface, or if the interface was not handling the property. It will natively convert integers, booleans, strings and double precision floating point values; it also handles string vectors in form of arrays. --- json-glib/json-gobject.c | 94 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 9 deletions(-) (limited to 'json-glib/json-gobject.c') diff --git a/json-glib/json-gobject.c b/json-glib/json-gobject.c index ba5ce5b..9388fef 100644 --- a/json-glib/json-gobject.c +++ b/json-glib/json-gobject.c @@ -134,12 +134,80 @@ json_deserialize_pspec (GValue *value, GParamSpec *pspec, JsonNode *node) { - gboolean retval = FALSE; GValue node_value = { 0, }; + gboolean retval = FALSE; - if (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT) + switch (JSON_NODE_TYPE (node)) { + case JSON_NODE_OBJECT: return FALSE; + + case JSON_NODE_ARRAY: + if (G_VALUE_HOLDS (value, G_TYPE_STRV)) + { + JsonArray *array = json_node_get_array (node); + guint i, array_len = json_array_get_length (array); + GString *buffer = g_string_new (NULL); + gchar **strv = NULL; + + for (i = 0; i < array_len; i++) + { + JsonNode *val = json_array_get_element (array, i); + + if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE) + continue; + + if (json_node_get_string (val) != NULL); + { + g_string_append (buffer, json_node_get_string (val)); + g_string_append_c (buffer, '\v'); + } + } + + strv = g_strsplit (buffer->str, "\v", -1); + g_value_set_boxed (value, strv); + + g_strfreev (strv); + g_string_free (buffer, TRUE); + retval = TRUE; + } + break; + + case JSON_NODE_VALUE: + json_node_get_value (node, &node_value); + + switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value))) + { + case G_TYPE_BOOLEAN: + case G_TYPE_INT: + case G_TYPE_DOUBLE: + case G_TYPE_STRING: + g_value_copy (value, &node_value); + retval = TRUE; + + case G_TYPE_CHAR: + g_value_set_char (value, (gchar) g_value_get_int (&node_value)); + break; + + case G_TYPE_UINT: + g_value_set_uint (value, (gint) g_value_get_int (&node_value)); + retval = TRUE; + break; + + case G_TYPE_UCHAR: + g_value_set_uchar (value, (guchar) g_value_get_int (&node_value)); + retval = TRUE; + break; + + default: + retval = FALSE; + break; + } + break; + + case JSON_NODE_NULL: + retval = FALSE; + break; } return retval; @@ -278,6 +346,7 @@ json_construct_gobject (GType gtype, { JsonSerializableIface *iface = NULL; JsonSerializable *serializable = NULL; + gboolean deserialize_property; JsonParser *parser; JsonNode *root; JsonObject *object; @@ -323,7 +392,10 @@ json_construct_gobject (GType gtype, { serializable = JSON_SERIALIZABLE (retval); iface = JSON_SERIALIZABLE_GET_IFACE (serializable); + deserialize_property = (iface->deserialize_property != NULL); } + else + deserialize_property = FALSE; object = json_node_get_object (root); @@ -338,7 +410,7 @@ json_construct_gobject (GType gtype, GParamSpec *pspec; JsonNode *val; GValue value = { 0, }; - gboolean res; + gboolean res = FALSE; pspec = g_object_class_find_property (klass, member_name); if (!pspec) @@ -354,15 +426,15 @@ json_construct_gobject (GType gtype, val = json_object_get_member (object, member_name); - if (iface && iface->deserialize_property) + if (deserialize_property) { - res = iface->deserialize_property (serializable, pspec->name, &value, pspec, val); } - else + + if (!res) res = json_deserialize_pspec (&value, pspec, val); if (res) @@ -449,11 +521,15 @@ json_serialize_gobject (GObject *gobject, g_free (pspecs); - gen = json_generator_new (); - json_generator_set_root (gen, root); - g_object_set (gen, "pretty", TRUE, NULL); + gen = g_object_new (JSON_TYPE_GENERATOR, + "root", root, + "pretty", TRUE, + "indent", 2, + NULL); + data = json_generator_to_data (gen, length); g_object_unref (gen); + json_node_free (root); return data; } -- cgit v1.2.1 From 7b93db7ad996b29a6c576db33803029dc94e16fc Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 13 Nov 2007 10:45:23 +0000 Subject: Fix a couple of dumb typos in the GObject deserialization code We need to skip if the CONSTRUCT_ONLY flag is set, not unset. We also need to copy the value from the JSON node into the target GValue, not the other way around. --- json-glib/json-gobject.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'json-glib/json-gobject.c') diff --git a/json-glib/json-gobject.c b/json-glib/json-gobject.c index 9388fef..c5dfe36 100644 --- a/json-glib/json-gobject.c +++ b/json-glib/json-gobject.c @@ -182,11 +182,13 @@ json_deserialize_pspec (GValue *value, case G_TYPE_INT: case G_TYPE_DOUBLE: case G_TYPE_STRING: - g_value_copy (value, &node_value); + g_value_copy (&node_value, value); retval = TRUE; + break; case G_TYPE_CHAR: g_value_set_char (value, (gchar) g_value_get_int (&node_value)); + retval = TRUE; break; case G_TYPE_UINT: @@ -416,7 +418,7 @@ json_construct_gobject (GType gtype, if (!pspec) continue; - if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY)) + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) continue; if (!(pspec->flags & G_PARAM_WRITABLE)) -- cgit v1.2.1 From 198ed839424dc7791d22dede152f4d7abc16a8b2 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 13 Nov 2007 10:51:31 +0000 Subject: Use the fallback value-to-node generator even for serializables To avoid reimplementing the same code all over again, if the implementation of the serialize_property virtual function of JsonSerializable returns NULL we will fall back to the simple value-to-node generator we provide for non-serializable classes. --- json-glib/json-gobject.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'json-glib/json-gobject.c') diff --git a/json-glib/json-gobject.c b/json-glib/json-gobject.c index c5dfe36..3357910 100644 --- a/json-glib/json-gobject.c +++ b/json-glib/json-gobject.c @@ -472,6 +472,8 @@ json_serialize_gobject (GObject *gobject, gsize *length) { JsonSerializableIface *iface = NULL; + JsonSerializable *serializable = NULL; + gboolean serialize_property = FALSE; JsonGenerator *gen; JsonNode *root; JsonObject *object; @@ -482,7 +484,11 @@ json_serialize_gobject (GObject *gobject, g_return_val_if_fail (G_OBJECT (gobject), NULL); if (JSON_IS_SERIALIZABLE (gobject)) - iface = JSON_SERIALIZABLE_GET_IFACE (gobject); + { + serializable = JSON_SERIALIZABLE (gobject); + iface = JSON_SERIALIZABLE_GET_IFACE (gobject); + serialize_property = (iface->serialize_property != NULL); + } object = json_object_new (); @@ -495,7 +501,7 @@ json_serialize_gobject (GObject *gobject, { GParamSpec *pspec = pspecs[i]; GValue value = { 0, }; - JsonNode *node; + JsonNode *node = NULL; /* read only what we can */ if (!(pspec->flags & G_PARAM_READABLE)) @@ -504,15 +510,14 @@ json_serialize_gobject (GObject *gobject, g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); g_object_get_property (gobject, pspec->name, &value); - if (iface && iface->serialize_property) + if (serialize_property) { - JsonSerializable *serializable = JSON_SERIALIZABLE (gobject); - node = iface->serialize_property (serializable, pspec->name, &value, pspec); } - else + + if (!node) node = json_serialize_pspec (&value, pspec); if (node) -- cgit v1.2.1