diff options
author | Emmanuele Bassi <ebassi@openedhand.com> | 2007-11-13 10:51:58 +0000 |
---|---|---|
committer | Emmanuele Bassi <ebassi@openedhand.com> | 2007-11-13 10:51:58 +0000 |
commit | b83a2bfa96885837ca48bacb6492fd68a2b5b564 (patch) | |
tree | f1bbc56d1fb13fd508112e018e5e9ebc5aca0c58 /json-glib/json-gobject.c | |
parent | 1f9b3e50282f8aa4a421c83ad596f6186ef82ec9 (diff) | |
parent | 198ed839424dc7791d22dede152f4d7abc16a8b2 (diff) | |
download | json-glib-b83a2bfa96885837ca48bacb6492fd68a2b5b564.tar.gz |
Merge branch 'gobject-deserialize' into work
Diffstat (limited to 'json-glib/json-gobject.c')
-rw-r--r-- | json-glib/json-gobject.c | 283 |
1 files changed, 262 insertions, 21 deletions
diff --git a/json-glib/json-gobject.c b/json-glib/json-gobject.c index f6f8b57..3357910 100644 --- a/json-glib/json-gobject.c +++ b/json-glib/json-gobject.c @@ -129,6 +129,92 @@ json_serializable_deserialize_property (JsonSerializable *serializable, property_node); } +static gboolean +json_deserialize_pspec (GValue *value, + GParamSpec *pspec, + JsonNode *node) +{ + GValue node_value = { 0, }; + gboolean retval = FALSE; + + 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 (&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: + 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; +} + static JsonNode * json_serialize_pspec (const GValue *real_value, GParamSpec *pspec) @@ -136,13 +222,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 +235,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 +281,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: @@ -224,6 +323,139 @@ json_serialize_pspec (const GValue *real_value, } /** + * 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; + gboolean deserialize_property; + 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); + deserialize_property = (iface->deserialize_property != NULL); + } + else + deserialize_property = FALSE; + + 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 = FALSE; + + 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 (deserialize_property) + { + res = iface->deserialize_property (serializable, pspec->name, + &value, + pspec, + val); + } + + if (!res) + 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 * @length: return value for the length of the buffer, or %NULL @@ -240,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; @@ -250,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 (); @@ -263,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)) @@ -272,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) @@ -291,11 +528,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; } |