/* json-gvariant.c - JSON GVariant integration * * This file is part of JSON-GLib * Copyright (C) 2007 OpenedHand Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Author: * Eduardo Lima Mitev */ #include "config.h" #include #include #include #include #include #include "json-gvariant.h" #include "json-generator.h" #include "json-parser.h" #include "json-types-private.h" /** * SECTION:json-gvariant * @short_description: Serialize and deserialize GVariant types * @Title: JSON GVariant Integration * * Use json_gvariant_serialize() and json_gvariant_serialize_data() to * convert from any #GVariant value to a #JsonNode tree or its string * representation. * * Use json_gvariant_deserialize() and json_gvariant_deserialize_data() to * obtain the #GVariant value from a #JsonNode tree or directly from a JSON * string. * * Since many #GVariant data types cannot be directly represented as * JSON, a #GVariant type string (signature) should be provided to these * methods in order to obtain a correct, type-contrained result. * If no signature is provided, conversion can still be done, but the * resulting #GVariant value will be "guessed" from the JSON data types * using the following rules: * * ## Strings * JSON strings map to GVariant `(s)`. * * ## Integers * JSON integers map to GVariant int64 `(x)`. * * ## Booleans * JSON booleans map to GVariant boolean `(b)`. * * ## Numbers * JSON numbers map to GVariant double `(d)`. * * ## Arrays * JSON arrays map to GVariant arrays of variants `(av)`. * * ## Objects * JSON objects map to GVariant dictionaries of string to variants `(a{sv})`. * * ## Null values * JSON null values map to GVariant maybe variants `(mv)`. */ /* custom extension to the GVariantClass enumeration to differentiate * a single dictionary entry from an array of dictionary entries */ #define JSON_G_VARIANT_CLASS_DICTIONARY 'c' typedef void (* GVariantForeachFunc) (GVariant *variant_child, gpointer user_data); static GVariant * json_to_gvariant_recurse (JsonNode *json_node, const gchar **signature, GError **error); /* ========================================================================== */ /* GVariant to JSON */ /* ========================================================================== */ static void gvariant_foreach (GVariant *variant, GVariantForeachFunc func, gpointer user_data) { GVariantIter iter; GVariant *variant_child; g_variant_iter_init (&iter, variant); while ((variant_child = g_variant_iter_next_value (&iter)) != NULL) { func (variant_child, user_data); g_variant_unref (variant_child); } } static void gvariant_to_json_array_foreach (GVariant *variant_child, gpointer user_data) { JsonArray *array = user_data; JsonNode *json_child; json_child = json_gvariant_serialize (variant_child); json_array_add_element (array, json_child); } static JsonNode * gvariant_to_json_array (GVariant *variant) { JsonArray *array; JsonNode *json_node; array = json_array_new (); json_node = json_node_new (JSON_NODE_ARRAY); json_node_set_array (json_node, array); json_array_unref (array); gvariant_foreach (variant, gvariant_to_json_array_foreach, array); return json_node; } static gchar * gvariant_simple_to_string (GVariant *variant) { GVariantClass class; gchar *str; class = g_variant_classify (variant); switch (class) { case G_VARIANT_CLASS_BOOLEAN: if (g_variant_get_boolean (variant)) str = g_strdup ("true"); else str = g_strdup ("false"); break; case G_VARIANT_CLASS_BYTE: str = g_strdup_printf ("%u", g_variant_get_byte (variant)); break; case G_VARIANT_CLASS_INT16: str = g_strdup_printf ("%d", g_variant_get_int16 (variant)); break; case G_VARIANT_CLASS_UINT16: str = g_strdup_printf ("%u", g_variant_get_uint16 (variant)); break; case G_VARIANT_CLASS_INT32: str = g_strdup_printf ("%d", g_variant_get_int32 (variant)); break; case G_VARIANT_CLASS_UINT32: str = g_strdup_printf ("%u", g_variant_get_uint32 (variant)); break; case G_VARIANT_CLASS_INT64: str = g_strdup_printf ("%" G_GINT64_FORMAT, g_variant_get_int64 (variant)); break; case G_VARIANT_CLASS_UINT64: str = g_strdup_printf ("%" G_GUINT64_FORMAT, g_variant_get_uint64 (variant)); break; case G_VARIANT_CLASS_HANDLE: str = g_strdup_printf ("%d", g_variant_get_handle (variant)); break; case G_VARIANT_CLASS_DOUBLE: { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", g_variant_get_double (variant)); str = g_strdup (buf); break; } case G_VARIANT_CLASS_STRING: case G_VARIANT_CLASS_OBJECT_PATH: case G_VARIANT_CLASS_SIGNATURE: str = g_strdup (g_variant_get_string (variant, NULL)); break; default: g_assert_not_reached (); break; } return str; } static JsonNode * gvariant_dict_entry_to_json (GVariant *variant, gchar **member_name) { GVariant *member; GVariant *value; JsonNode *json_node; member = g_variant_get_child_value (variant, 0); *member_name = gvariant_simple_to_string (member); value = g_variant_get_child_value (variant, 1); json_node = json_gvariant_serialize (value); g_variant_unref (member); g_variant_unref (value); return json_node; } static void gvariant_to_json_object_foreach (GVariant *variant_child, gpointer user_data) { gchar *member_name; JsonNode *json_child; JsonObject *object = (JsonObject *) user_data; json_child = gvariant_dict_entry_to_json (variant_child, &member_name); json_object_set_member (object, member_name, json_child); g_free (member_name); } static JsonNode * gvariant_to_json_object (GVariant *variant) { JsonNode *json_node; JsonObject *object; json_node = json_node_new (JSON_NODE_OBJECT); object = json_object_new (); json_node_set_object (json_node, object); json_object_unref (object); gvariant_foreach (variant, gvariant_to_json_object_foreach, object); return json_node; } /** * json_gvariant_serialize: * @variant: A #GVariant to convert * * Converts @variant to a JSON tree. * * Return value: (transfer full): A #JsonNode representing the root of the * JSON data structure obtained from @variant * * Since: 0.14 */ JsonNode * json_gvariant_serialize (GVariant *variant) { JsonNode *json_node = NULL; GVariantClass class; g_return_val_if_fail (variant != NULL, NULL); class = g_variant_classify (variant); if (! g_variant_is_container (variant)) { json_node = json_node_new (JSON_NODE_VALUE); switch (class) { case G_VARIANT_CLASS_BOOLEAN: json_node_set_boolean (json_node, g_variant_get_boolean (variant)); break; case G_VARIANT_CLASS_BYTE: json_node_set_int (json_node, g_variant_get_byte (variant)); break; case G_VARIANT_CLASS_INT16: json_node_set_int (json_node, g_variant_get_int16 (variant)); break; case G_VARIANT_CLASS_UINT16: json_node_set_int (json_node, g_variant_get_uint16 (variant)); break; case G_VARIANT_CLASS_INT32: json_node_set_int (json_node, g_variant_get_int32 (variant)); break; case G_VARIANT_CLASS_UINT32: json_node_set_int (json_node, g_variant_get_uint32 (variant)); break; case G_VARIANT_CLASS_INT64: json_node_set_int (json_node, g_variant_get_int64 (variant)); break; case G_VARIANT_CLASS_UINT64: json_node_set_int (json_node, g_variant_get_uint64 (variant)); break; case G_VARIANT_CLASS_HANDLE: json_node_set_int (json_node, g_variant_get_handle (variant)); break; case G_VARIANT_CLASS_DOUBLE: json_node_set_double (json_node, g_variant_get_double (variant)); break; case G_VARIANT_CLASS_STRING: case G_VARIANT_CLASS_OBJECT_PATH: case G_VARIANT_CLASS_SIGNATURE: json_node_set_string (json_node, g_variant_get_string (variant, NULL)); break; default: break; } } else { switch (class) { case G_VARIANT_CLASS_MAYBE: { GVariant *value; value = g_variant_get_maybe (variant); if (value == NULL) { json_node = json_node_new (JSON_NODE_NULL); } else { json_node = json_gvariant_serialize (value); g_variant_unref (value); } break; } case G_VARIANT_CLASS_VARIANT: { GVariant *value; value = g_variant_get_variant (variant); json_node = json_gvariant_serialize (value); g_variant_unref (value); break; } case G_VARIANT_CLASS_ARRAY: { const gchar *type; type = g_variant_get_type_string (variant); if (type[1] == G_VARIANT_CLASS_DICT_ENTRY) { /* array of dictionary entries => JsonObject */ json_node = gvariant_to_json_object (variant); } else { /* array of anything else => JsonArray */ json_node = gvariant_to_json_array (variant); } break; } case G_VARIANT_CLASS_DICT_ENTRY: { gchar *member_name; JsonObject *object; JsonNode *child; /* a single dictionary entry => JsonObject */ json_node = json_node_new (JSON_NODE_OBJECT); object = json_object_new (); json_node_set_object (json_node, object); json_object_unref (object); child = gvariant_dict_entry_to_json (variant, &member_name); json_object_set_member (object, member_name, child); g_free (member_name); break; } case G_VARIANT_CLASS_TUPLE: json_node = gvariant_to_json_array (variant); break; default: break; } } return json_node; } /** * json_gvariant_serialize_data: * @variant: A #GVariant to convert * @length: (out) (allow-none): Return location for the length of the returned * string, or %NULL * * Converts @variant to its JSON encoded string representation. This method * is actually a helper function. It uses json_gvariant_serialize() to obtain the * JSON tree, and then #JsonGenerator to stringify it. * * Return value: (transfer full): The JSON encoded string corresponding to * @variant * * Since: 0.14 */ gchar * json_gvariant_serialize_data (GVariant *variant, gsize *length) { JsonNode *json_node; JsonGenerator *generator; gchar *json; json_node = json_gvariant_serialize (variant); generator = json_generator_new (); json_generator_set_root (generator, json_node); json = json_generator_to_data (generator, length); g_object_unref (generator); json_node_unref (json_node); return json; } /* ========================================================================== */ /* JSON to GVariant */ /* ========================================================================== */ static GVariantClass json_to_gvariant_get_next_class (JsonNode *json_node, const gchar **signature) { if (signature == NULL) { GVariantClass class = 0; switch (json_node_get_node_type (json_node)) { case JSON_NODE_VALUE: switch (json_node_get_value_type (json_node)) { case G_TYPE_BOOLEAN: class = G_VARIANT_CLASS_BOOLEAN; break; case G_TYPE_INT64: class = G_VARIANT_CLASS_INT64; break; case G_TYPE_DOUBLE: class = G_VARIANT_CLASS_DOUBLE; break; case G_TYPE_STRING: class = G_VARIANT_CLASS_STRING; break; } break; case JSON_NODE_ARRAY: class = G_VARIANT_CLASS_ARRAY; break; case JSON_NODE_OBJECT: class = JSON_G_VARIANT_CLASS_DICTIONARY; break; case JSON_NODE_NULL: class = G_VARIANT_CLASS_MAYBE; break; } return class; } else { if ((*signature)[0] == G_VARIANT_CLASS_ARRAY && (*signature)[1] == G_VARIANT_CLASS_DICT_ENTRY) return JSON_G_VARIANT_CLASS_DICTIONARY; else return (*signature)[0]; } } static gboolean json_node_assert_type (JsonNode *json_node, JsonNodeType type, GType sub_type, GError **error) { if (JSON_NODE_TYPE (json_node) != type || (type == JSON_NODE_VALUE && (json_node_get_value_type (json_node) != sub_type))) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, /* translators: the '%s' is the type name */ _("Unexpected type “%s” in JSON node"), g_type_name (json_node_get_value_type (json_node))); return FALSE; } else { return TRUE; } } static void json_to_gvariant_foreach_add (gpointer data, gpointer user_data) { GVariantBuilder *builder = (GVariantBuilder *) user_data; GVariant *child = (GVariant *) data; g_variant_builder_add_value (builder, child); } static void json_to_gvariant_foreach_free (gpointer data, gpointer user_data) { GVariant *child = (GVariant *) data; g_variant_unref (child); } static GVariant * json_to_gvariant_build_from_glist (GList *list, const gchar *signature) { GVariantBuilder *builder; GVariant *result; builder = g_variant_builder_new (G_VARIANT_TYPE (signature)); g_list_foreach (list, json_to_gvariant_foreach_add, builder); result = g_variant_builder_end (builder); g_variant_builder_unref (builder); return result; } static GVariant * json_to_gvariant_tuple (JsonNode *json_node, const gchar **signature, GError **error) { GVariant *variant = NULL; JsonArray *array; gint i; GList *children = NULL; gboolean roll_back = FALSE; const gchar *initial_signature; array = json_node_get_array (json_node); initial_signature = *signature; (*signature)++; i = 1; while ((*signature)[0] != ')' && (*signature)[0] != '\0') { JsonNode *json_child; GVariant *variant_child; if (i - 1 >= json_array_get_length (array)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, _("Missing elements in JSON array to conform to a tuple")); roll_back = TRUE; break; } json_child = json_array_get_element (array, i - 1); variant_child = json_to_gvariant_recurse (json_child, signature, error); if (variant_child != NULL) { children = g_list_append (children, variant_child); } else { roll_back = TRUE; break; } i++; } if (! roll_back) { if ( (*signature)[0] != ')') { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, _("Missing closing symbol “)” in the GVariant tuple type")); roll_back = TRUE; } else if (json_array_get_length (array) >= i) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, _("Unexpected extra elements in JSON array")); roll_back = TRUE; } else { gchar *tuple_type; tuple_type = g_strndup (initial_signature, (*signature) - initial_signature + 1); variant = json_to_gvariant_build_from_glist (children, tuple_type); g_free (tuple_type); } } if (roll_back) g_list_foreach (children, json_to_gvariant_foreach_free, NULL); g_list_free (children); return variant; } static gchar * signature_get_next_complete_type (const gchar **signature) { GVariantClass class; const gchar *initial_signature; gchar *result; /* here it is assumed that 'signature' is a valid type string */ initial_signature = *signature; class = (*signature)[0]; if (class == G_VARIANT_CLASS_TUPLE || class == G_VARIANT_CLASS_DICT_ENTRY) { gchar stack[256] = {0}; guint stack_len = 0; do { if ( (*signature)[0] == G_VARIANT_CLASS_TUPLE) { stack[stack_len] = ')'; stack_len++; } else if ( (*signature)[0] == G_VARIANT_CLASS_DICT_ENTRY) { stack[stack_len] = '}'; stack_len++; } (*signature)++; if ( (*signature)[0] == stack[stack_len - 1]) stack_len--; } while (stack_len > 0); (*signature)++; } else if (class == G_VARIANT_CLASS_ARRAY || class == G_VARIANT_CLASS_MAYBE) { gchar *tmp_sig; (*signature)++; tmp_sig = signature_get_next_complete_type (signature); g_free (tmp_sig); } else { (*signature)++; } result = g_strndup (initial_signature, (*signature) - initial_signature); return result; } static GVariant * json_to_gvariant_maybe (JsonNode *json_node, const gchar **signature, GError **error) { GVariant *variant = NULL; GVariant *value; gchar *maybe_signature; if (signature) { (*signature)++; maybe_signature = signature_get_next_complete_type (signature); } else { maybe_signature = g_strdup ("v"); } if (json_node_get_node_type (json_node) == JSON_NODE_NULL) { variant = g_variant_new_maybe (G_VARIANT_TYPE (maybe_signature), NULL); } else { const gchar *tmp_signature; tmp_signature = maybe_signature; value = json_to_gvariant_recurse (json_node, &tmp_signature, error); if (value != NULL) variant = g_variant_new_maybe (G_VARIANT_TYPE (maybe_signature), value); } g_free (maybe_signature); /* compensate the (*signature)++ call at the end of 'recurse()' */ if (signature) (*signature)--; return variant; } static GVariant * json_to_gvariant_array (JsonNode *json_node, const gchar **signature, GError **error) { GVariant *variant = NULL; JsonArray *array; GList *children = NULL; gboolean roll_back = FALSE; const gchar *orig_signature = NULL; gchar *child_signature; array = json_node_get_array (json_node); if (signature != NULL) { orig_signature = *signature; (*signature)++; child_signature = signature_get_next_complete_type (signature); } else child_signature = g_strdup ("v"); if (json_array_get_length (array) > 0) { gint i; guint len; len = json_array_get_length (array); for (i = 0; i < len; i++) { JsonNode *json_child; GVariant *variant_child; const gchar *tmp_signature; json_child = json_array_get_element (array, i); tmp_signature = child_signature; variant_child = json_to_gvariant_recurse (json_child, &tmp_signature, error); if (variant_child != NULL) { children = g_list_append (children, variant_child); } else { roll_back = TRUE; break; } } } if (!roll_back) { gchar *array_signature; if (signature) array_signature = g_strndup (orig_signature, (*signature) - orig_signature); else array_signature = g_strdup ("av"); variant = json_to_gvariant_build_from_glist (children, array_signature); g_free (array_signature); /* compensate the (*signature)++ call at the end of 'recurse()' */ if (signature) (*signature)--; } else g_list_foreach (children, json_to_gvariant_foreach_free, NULL); g_list_free (children); g_free (child_signature); return variant; } static GVariant * gvariant_simple_from_string (const gchar *st, GVariantClass class, GError **error) { GVariant *variant = NULL; gchar *nptr = NULL; errno = 0; switch (class) { case G_VARIANT_CLASS_BOOLEAN: if (g_strcmp0 (st, "true") == 0) variant = g_variant_new_boolean (TRUE); else if (g_strcmp0 (st, "false") == 0) variant = g_variant_new_boolean (FALSE); else errno = 1; break; case G_VARIANT_CLASS_BYTE: variant = g_variant_new_byte (g_ascii_strtoll (st, &nptr, 10)); break; case G_VARIANT_CLASS_INT16: variant = g_variant_new_int16 (g_ascii_strtoll (st, &nptr, 10)); break; case G_VARIANT_CLASS_UINT16: variant = g_variant_new_uint16 (g_ascii_strtoll (st, &nptr, 10)); break; case G_VARIANT_CLASS_INT32: variant = g_variant_new_int32 (g_ascii_strtoll (st, &nptr, 10)); break; case G_VARIANT_CLASS_UINT32: variant = g_variant_new_uint32 (g_ascii_strtoull (st, &nptr, 10)); break; case G_VARIANT_CLASS_INT64: variant = g_variant_new_int64 (g_ascii_strtoll (st, &nptr, 10)); break; case G_VARIANT_CLASS_UINT64: variant = g_variant_new_uint64 (g_ascii_strtoull (st, &nptr, 10)); break; case G_VARIANT_CLASS_HANDLE: variant = g_variant_new_handle (strtol (st, &nptr, 10)); break; case G_VARIANT_CLASS_DOUBLE: variant = g_variant_new_double (g_ascii_strtod (st, &nptr)); break; case G_VARIANT_CLASS_STRING: case G_VARIANT_CLASS_OBJECT_PATH: case G_VARIANT_CLASS_SIGNATURE: variant = g_variant_new_string (st); break; default: g_assert_not_reached (); break; } if (errno != 0 || nptr == st) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, _("Invalid string value converting to GVariant")); if (variant != NULL) { g_variant_unref (variant); variant = NULL; } } return variant; } static void parse_dict_entry_signature (const gchar **signature, gchar **entry_signature, gchar **key_signature, gchar **value_signature) { const gchar *tmp_sig; if (signature != NULL) *entry_signature = signature_get_next_complete_type (signature); else *entry_signature = g_strdup ("{sv}"); tmp_sig = (*entry_signature) + 1; *key_signature = signature_get_next_complete_type (&tmp_sig); *value_signature = signature_get_next_complete_type (&tmp_sig); } static GVariant * json_to_gvariant_dict_entry (JsonNode *json_node, const gchar **signature, GError **error) { GVariant *variant = NULL; JsonObject *obj; gchar *entry_signature; gchar *key_signature; gchar *value_signature; const gchar *tmp_signature; GQueue *members; const gchar *json_member; JsonNode *json_value; GVariant *variant_member; GVariant *variant_value; obj = json_node_get_object (json_node); if (json_object_get_size (obj) != 1) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, _("A GVariant dictionary entry expects a JSON object with exactly one member")); return NULL; } parse_dict_entry_signature (signature, &entry_signature, &key_signature, &value_signature); members = json_object_get_members_internal (obj); json_member = (const gchar *) members->head->data; variant_member = gvariant_simple_from_string (json_member, key_signature[0], error); if (variant_member != NULL) { json_value = json_object_get_member (obj, json_member); tmp_signature = value_signature; variant_value = json_to_gvariant_recurse (json_value, &tmp_signature, error); if (variant_value != NULL) { GVariantBuilder *builder; builder = g_variant_builder_new (G_VARIANT_TYPE (entry_signature)); g_variant_builder_add_value (builder, variant_member); g_variant_builder_add_value (builder, variant_value); variant = g_variant_builder_end (builder); g_variant_builder_unref (builder); } } g_free (value_signature); g_free (key_signature); g_free (entry_signature); /* compensate the (*signature)++ call at the end of 'recurse()' */ if (signature) (*signature)--; return variant; } static GVariant * json_to_gvariant_dictionary (JsonNode *json_node, const gchar **signature, GError **error) { GVariant *variant = NULL; JsonObject *obj; gboolean roll_back = FALSE; gchar *dict_signature; gchar *entry_signature; gchar *key_signature; gchar *value_signature; const gchar *tmp_signature; GVariantBuilder *builder; GQueue *members; GList *member; obj = json_node_get_object (json_node); if (signature != NULL) (*signature)++; parse_dict_entry_signature (signature, &entry_signature, &key_signature, &value_signature); dict_signature = g_strdup_printf ("a%s", entry_signature); builder = g_variant_builder_new (G_VARIANT_TYPE (dict_signature)); members = json_object_get_members_internal (obj); for (member = members->head; member != NULL; member = member->next) { const gchar *json_member; JsonNode *json_value; GVariant *variant_member; GVariant *variant_value; json_member = (const gchar *) member->data; variant_member = gvariant_simple_from_string (json_member, key_signature[0], error); if (variant_member == NULL) { roll_back = TRUE; break; } json_value = json_object_get_member (obj, json_member); tmp_signature = value_signature; variant_value = json_to_gvariant_recurse (json_value, &tmp_signature, error); if (variant_value != NULL) { g_variant_builder_open (builder, G_VARIANT_TYPE (entry_signature)); g_variant_builder_add_value (builder, variant_member); g_variant_builder_add_value (builder, variant_value); g_variant_builder_close (builder); } else { roll_back = TRUE; break; } } if (! roll_back) variant = g_variant_builder_end (builder); g_variant_builder_unref (builder); g_free (value_signature); g_free (key_signature); g_free (entry_signature); g_free (dict_signature); /* compensate the (*signature)++ call at the end of 'recurse()' */ if (signature != NULL) (*signature)--; return variant; } static GVariant * json_to_gvariant_recurse (JsonNode *json_node, const gchar **signature, GError **error) { GVariant *variant = NULL; GVariantClass class; class = json_to_gvariant_get_next_class (json_node, signature); if (class == JSON_G_VARIANT_CLASS_DICTIONARY) { if (json_node_assert_type (json_node, JSON_NODE_OBJECT, 0, error)) variant = json_to_gvariant_dictionary (json_node, signature, error); goto out; } if (JSON_NODE_TYPE (json_node) == JSON_NODE_VALUE && json_node_get_value_type (json_node) == G_TYPE_STRING) { const gchar* str = json_node_get_string (json_node); switch (class) { case G_VARIANT_CLASS_BOOLEAN: case G_VARIANT_CLASS_BYTE: case G_VARIANT_CLASS_INT16: case G_VARIANT_CLASS_UINT16: case G_VARIANT_CLASS_INT32: case G_VARIANT_CLASS_UINT32: case G_VARIANT_CLASS_INT64: case G_VARIANT_CLASS_UINT64: case G_VARIANT_CLASS_HANDLE: case G_VARIANT_CLASS_DOUBLE: case G_VARIANT_CLASS_STRING: variant = gvariant_simple_from_string (str, class, error); goto out; default: break; } } switch (class) { case G_VARIANT_CLASS_BOOLEAN: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_BOOLEAN, error)) variant = g_variant_new_boolean (json_node_get_boolean (json_node)); break; case G_VARIANT_CLASS_BYTE: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_byte (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_INT16: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_int16 (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_UINT16: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_uint16 (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_INT32: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_int32 (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_UINT32: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_uint32 (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_INT64: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_int64 (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_UINT64: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_uint64 (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_HANDLE: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) variant = g_variant_new_handle (json_node_get_int (json_node)); break; case G_VARIANT_CLASS_DOUBLE: /* Doubles can look like ints to the json parser: when they don't have a dot */ if (JSON_NODE_TYPE (json_node) == JSON_NODE_VALUE && json_node_get_value_type (json_node) == G_TYPE_INT64) variant = g_variant_new_double (json_node_get_int (json_node)); else if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_DOUBLE, error)) variant = g_variant_new_double (json_node_get_double (json_node)); break; case G_VARIANT_CLASS_STRING: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_STRING, error)) variant = g_variant_new_string (json_node_get_string (json_node)); break; case G_VARIANT_CLASS_OBJECT_PATH: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_STRING, error)) variant = g_variant_new_object_path (json_node_get_string (json_node)); break; case G_VARIANT_CLASS_SIGNATURE: if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_STRING, error)) variant = g_variant_new_signature (json_node_get_string (json_node)); break; case G_VARIANT_CLASS_VARIANT: variant = g_variant_new_variant (json_to_gvariant_recurse (json_node, NULL, error)); break; case G_VARIANT_CLASS_MAYBE: variant = json_to_gvariant_maybe (json_node, signature, error); break; case G_VARIANT_CLASS_ARRAY: if (json_node_assert_type (json_node, JSON_NODE_ARRAY, 0, error)) variant = json_to_gvariant_array (json_node, signature, error); break; case G_VARIANT_CLASS_TUPLE: if (json_node_assert_type (json_node, JSON_NODE_ARRAY, 0, error)) variant = json_to_gvariant_tuple (json_node, signature, error); break; case G_VARIANT_CLASS_DICT_ENTRY: if (json_node_assert_type (json_node, JSON_NODE_OBJECT, 0, error)) variant = json_to_gvariant_dict_entry (json_node, signature, error); break; default: g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, _("GVariant class “%c” not supported"), class); break; } out: if (signature) (*signature)++; return variant; } /** * json_gvariant_deserialize: * @json_node: A #JsonNode to convert * @signature: (allow-none): A valid #GVariant type string, or %NULL * @error: A pointer to a #GError * * Converts a JSON data structure to a GVariant value using @signature to * resolve ambiguous data types. If no error occurs, the resulting #GVariant * is guaranteed to conform to @signature. * * If @signature is not %NULL but does not represent a valid GVariant type * string, %NULL is returned and error is set to %G_IO_ERROR_INVALID_ARGUMENT. * If a @signature is provided but the JSON structure cannot be mapped to it, * %NULL is returned and error is set to %G_IO_ERROR_INVALID_DATA. * If @signature is %NULL, the conversion is done based strictly on the types * in the JSON nodes. * * The returned variant has a floating reference that will need to be sunk * by the caller code. * * Return value: (transfer none): A newly created, floating #GVariant * compliant with @signature, or %NULL on error * * Since: 0.14 */ GVariant * json_gvariant_deserialize (JsonNode *json_node, const gchar *signature, GError **error) { g_return_val_if_fail (json_node != NULL, NULL); if (signature != NULL && ! g_variant_type_string_is_valid (signature)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, _("Invalid GVariant signature")); return NULL; } return json_to_gvariant_recurse (json_node, signature ? &signature : NULL, error); } /** * json_gvariant_deserialize_data: * @json: A JSON data string * @length: The length of @json, or -1 if %NULL-terminated * @signature: (allow-none): A valid #GVariant type string, or %NULL * @error: A pointer to a #GError * * Converts a JSON string to a #GVariant value. This method works exactly * like json_gvariant_deserialize(), but takes a JSON encoded string instead. * The string is first converted to a #JsonNode using #JsonParser, and then * json_gvariant_deserialize() is called. * * The returned variant has a floating reference that will need to be sunk * by the caller code. * * Returns: (transfer none): A newly created, floating #GVariant compliant * with @signature, or %NULL on error * * Since: 0.14 */ GVariant * json_gvariant_deserialize_data (const gchar *json, gssize length, const gchar *signature, GError **error) { JsonParser *parser; GVariant *variant = NULL; JsonNode *root; parser = json_parser_new (); if (! json_parser_load_from_data (parser, json, length, error)) return NULL; root = json_parser_get_root (parser); if (root == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, _("JSON data is empty")); } else { variant = json_gvariant_deserialize (json_parser_get_root (parser), signature, error); } g_object_unref (parser); return variant; }