/* json-gboxed.c - JSON GBoxed integration * * This file is part of JSON-GLib * * Copyright (C) 2007 OpenedHand Ltd. * Copyright (C) 2009 Intel Corp. * * 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.1 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: * Emmanuele Bassi */ /** * SECTION:json-gboxed * @short_description: Serialize and deserialize GBoxed types * * GLib's #GBoxed type is a generic wrapper for arbitrary C structures. * * JSON-GLib allows serialization and deserialization of a #GBoxed type * by registering functions mapping a #JsonNodeType to a specific * #GType. * * When registering a #GBoxed type you should also register the * corresponding transformation functions, e.g.: * * |[ * GType * my_struct_get_type (void) * { * static GType boxed_type = 0; * * if (boxed_type == 0) * { * boxed_type = * g_boxed_type_register_static (g_intern_static_string ("MyStruct"), * (GBoxedCopyFunc) my_struct_copy, * (GBoxedFreeFunc) my_struct_free); * * json_boxed_register_serialize_func (boxed_type, JSON_NODE_OBJECT, * my_struct_serialize); * json_boxed_register_deserialize_func (boxed_type, JSON_NODE_OBJECT, * my_struct_deserialize); * } * * return boxed_type; * } * ]| * * The serialization function will be invoked by json_boxed_serialize(): * it will be passed a pointer to the C structure and it must return a * #JsonNode. The deserialization function will be invoked by * json_boxed_deserialize(): it will be passed a #JsonNode for the * declared type and it must return a newly allocated C structure. * * It is possible to check whether a #GBoxed type can be deserialized * from a specific #JsonNodeType, and whether a #GBoxed can be serialized * and to which specific #JsonNodeType. */ #include "config.h" #include #include #include "json-types-private.h" #include "json-gobject.h" typedef struct _BoxedTransform BoxedTransform; struct _BoxedTransform { GType boxed_type; gint node_type; JsonBoxedSerializeFunc serialize; JsonBoxedDeserializeFunc deserialize; }; G_LOCK_DEFINE_STATIC (boxed_serialize); static GSList *boxed_serialize = NULL; G_LOCK_DEFINE_STATIC (boxed_deserialize); static GSList *boxed_deserialize = NULL; static gint boxed_transforms_cmp (gconstpointer a, gconstpointer b) { const BoxedTransform *ta = a; const BoxedTransform *tb = b; return tb->boxed_type - ta->boxed_type; } static gint boxed_transforms_find (gconstpointer a, gconstpointer b) { const BoxedTransform *haystack = a; const BoxedTransform *needle = b; if (needle->node_type != -1) return (haystack->boxed_type == needle->boxed_type && haystack->node_type == needle->node_type) ? 0 : 1; else return (haystack->boxed_type == needle->boxed_type) ? 0 : 1; } static BoxedTransform * lookup_boxed_transform (GSList *transforms, GType gboxed_type, JsonNodeType node_type) { BoxedTransform lookup; GSList *t; lookup.boxed_type = gboxed_type; lookup.node_type = node_type; t = g_slist_find_custom (transforms, &lookup, boxed_transforms_find); if (t == NULL) return NULL; return t->data; } /** * json_boxed_register_serialize_func: (skip) * @gboxed_type: a boxed type * @node_type: a node type * @serialize_func: serialization function for @boxed_type into * a #JsonNode of type @node_type * * Registers a serialization function for a #GBoxed of type @gboxed_type * to a #JsonNode of type @node_type * * Since: 0.10 */ void json_boxed_register_serialize_func (GType gboxed_type, JsonNodeType node_type, JsonBoxedSerializeFunc serialize_func) { BoxedTransform *t; g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type)); g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE); G_LOCK (boxed_serialize); t = lookup_boxed_transform (boxed_serialize, gboxed_type, node_type); if (t == NULL) { t = g_slice_new (BoxedTransform); t->boxed_type = gboxed_type; t->node_type = node_type; t->serialize = serialize_func; boxed_serialize = g_slist_insert_sorted (boxed_serialize, t, boxed_transforms_cmp); } else g_warning ("A serialization function for the boxed type %s into " "JSON nodes of type %s already exists", g_type_name (gboxed_type), json_node_type_get_name (node_type)); G_UNLOCK (boxed_serialize); } /** * json_boxed_register_deserialize_func: (skip) * @gboxed_type: a boxed type * @node_type: a node type * @deserialize_func: deserialization function for @boxed_type from * a #JsonNode of type @node_type * * Registers a deserialization function for a #GBoxed of type @gboxed_type * from a #JsonNode of type @node_type * * Since: 0.10 */ void json_boxed_register_deserialize_func (GType gboxed_type, JsonNodeType node_type, JsonBoxedDeserializeFunc deserialize_func) { BoxedTransform *t; g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type)); g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE); G_LOCK (boxed_deserialize); t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type); if (t == NULL) { t = g_slice_new (BoxedTransform); t->boxed_type = gboxed_type; t->node_type = node_type; t->deserialize = deserialize_func; boxed_deserialize = g_slist_insert_sorted (boxed_deserialize, t, boxed_transforms_cmp); } else g_warning ("A deserialization function for the boxed type %s from " "JSON nodes of type %s already exists", g_type_name (gboxed_type), json_node_type_get_name (node_type)); G_UNLOCK (boxed_deserialize); } /** * json_boxed_can_serialize: * @gboxed_type: a boxed type * @node_type: (out): the #JsonNode type to which the boxed type can be * serialized into * * Checks whether it is possible to serialize a #GBoxed of * type @gboxed_type into a #JsonNode. The type of the * #JsonNode is placed inside @node_type if the function * returns %TRUE and it's undefined otherwise. * * Return value: %TRUE if the type can be serialized, * and %FALSE otherwise. * * Since: 0.10 */ gboolean json_boxed_can_serialize (GType gboxed_type, JsonNodeType *node_type) { BoxedTransform *t; g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE); g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE); t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1); if (t != NULL) { if (node_type) *node_type = t->node_type; return TRUE; } return FALSE; } /** * json_boxed_can_deserialize: * @gboxed_type: a boxed type * @node_type: a #JsonNode type * * Checks whether it is possible to deserialize a #GBoxed of * type @gboxed_type from a #JsonNode of type @node_type * * Return value: %TRUE if the type can be deserialized, %FALSE otherwise * * Since: 0.10 */ gboolean json_boxed_can_deserialize (GType gboxed_type, JsonNodeType node_type) { BoxedTransform *t; g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE); g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE); t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type); if (t != NULL) return TRUE; return FALSE; } /** * json_boxed_serialize: * @gboxed_type: a boxed type * @boxed: a pointer to a #GBoxed of type @gboxed_type * * Serializes @boxed, a pointer to a #GBoxed of type @gboxed_type, * into a #JsonNode * * Return value: (nullable) (transfer full): a #JsonNode with the serialization of * the boxed type, or %NULL if serialization either failed or was not possible * * Since: 0.10 */ JsonNode * json_boxed_serialize (GType gboxed_type, gconstpointer boxed) { BoxedTransform *t; g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL); g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL); g_return_val_if_fail (boxed != NULL, NULL); t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1); if (t != NULL && t->serialize != NULL) return t->serialize (boxed); return NULL; } /** * json_boxed_deserialize: * @gboxed_type: a boxed type * @node: a #JsonNode * * Deserializes @node into a #GBoxed of @gboxed_type * * Return value: (transfer full): the newly allocated #GBoxed. Use * g_boxed_free() to release the resources allocated by this * function * * Since: 0.10 */ gpointer json_boxed_deserialize (GType gboxed_type, JsonNode *node) { JsonNodeType node_type; BoxedTransform *t; g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL); g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL); g_return_val_if_fail (node != NULL, NULL); node_type = json_node_get_node_type (node); t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type); if (t != NULL && t->deserialize != NULL) return t->deserialize (node); return NULL; }