1 /* json-gboxed.c - JSON GBoxed integration
2 *
3 * This file is part of JSON-GLib
4 *
5 * Copyright (C) 2007 OpenedHand Ltd.
6 * Copyright (C) 2009 Intel Corp.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * Author:
19 * Emmanuele Bassi <ebassi@linux.intel.com>
20 */
21
22 /**
23 * SECTION:json-gboxed
24 * @short_description: Serialize and deserialize GBoxed types
25 *
26 * GLib's #GBoxed type is a generic wrapper for arbitrary C structures.
27 *
28 * JSON-GLib allows serialization and deserialization of a #GBoxed type
29 * by registering functions mapping a #JsonNodeType to a specific
30 * #GType.
31 *
32 * When registering a #GBoxed type you should also register the
33 * corresponding transformation functions, e.g.:
34 *
35 * |[<!-- language="C" -->
36 * GType
37 * my_struct_get_type (void)
38 * {
39 * static GType boxed_type = 0;
40 *
41 * if (boxed_type == 0)
42 * {
43 * boxed_type =
44 * g_boxed_type_register_static (g_intern_static_string ("MyStruct"),
45 * (GBoxedCopyFunc) my_struct_copy,
46 * (GBoxedFreeFunc) my_struct_free);
47 *
48 * json_boxed_register_serialize_func (boxed_type, JSON_NODE_OBJECT,
49 * my_struct_serialize);
50 * json_boxed_register_deserialize_func (boxed_type, JSON_NODE_OBJECT,
51 * my_struct_deserialize);
52 * }
53 *
54 * return boxed_type;
55 * }
56 * ]|
57 *
58 * The serialization function will be invoked by json_boxed_serialize():
59 * it will be passed a pointer to the C structure and it must return a
60 * #JsonNode. The deserialization function will be invoked by
61 * json_boxed_deserialize(): it will be passed a #JsonNode for the
62 * declared type and it must return a newly allocated C structure.
63 *
64 * It is possible to check whether a #GBoxed type can be deserialized
65 * from a specific #JsonNodeType, and whether a #GBoxed can be serialized
66 * and to which specific #JsonNodeType.
67 */
68
69 #include "config.h"
70
71 #include <string.h>
72 #include <stdlib.h>
73
74 #include "json-types-private.h"
75 #include "json-gobject.h"
76
77 typedef struct _BoxedTransform BoxedTransform;
78
79 struct _BoxedTransform
80 {
81 GType boxed_type;
82 gint node_type;
83
84 JsonBoxedSerializeFunc serialize;
85 JsonBoxedDeserializeFunc deserialize;
86 };
87
88 G_LOCK_DEFINE_STATIC (boxed_serialize);
89 static GSList *boxed_serialize = NULL;
90
91 G_LOCK_DEFINE_STATIC (boxed_deserialize);
92 static GSList *boxed_deserialize = NULL;
93
94 static gint
boxed_transforms_cmp(gconstpointer a,gconstpointer b)95 boxed_transforms_cmp (gconstpointer a,
96 gconstpointer b)
97 {
98 const BoxedTransform *ta = a;
99 const BoxedTransform *tb = b;
100
101 return tb->boxed_type - ta->boxed_type;
102 }
103
104 static gint
boxed_transforms_find(gconstpointer a,gconstpointer b)105 boxed_transforms_find (gconstpointer a,
106 gconstpointer b)
107 {
108 const BoxedTransform *haystack = a;
109 const BoxedTransform *needle = b;
110
111 if (needle->node_type != -1)
112 return (haystack->boxed_type == needle->boxed_type &&
113 haystack->node_type == needle->node_type) ? 0 : 1;
114 else
115 return (haystack->boxed_type == needle->boxed_type) ? 0 : 1;
116 }
117
118 static BoxedTransform *
lookup_boxed_transform(GSList * transforms,GType gboxed_type,JsonNodeType node_type)119 lookup_boxed_transform (GSList *transforms,
120 GType gboxed_type,
121 JsonNodeType node_type)
122 {
123 BoxedTransform lookup;
124 GSList *t;
125
126 lookup.boxed_type = gboxed_type;
127 lookup.node_type = node_type;
128
129 t = g_slist_find_custom (transforms, &lookup, boxed_transforms_find);
130 if (t == NULL)
131 return NULL;
132
133 return t->data;
134 }
135
136 /**
137 * json_boxed_register_serialize_func: (skip)
138 * @gboxed_type: a boxed type
139 * @node_type: a node type
140 * @serialize_func: serialization function for @boxed_type into
141 * a #JsonNode of type @node_type
142 *
143 * Registers a serialization function for a #GBoxed of type @gboxed_type
144 * to a #JsonNode of type @node_type
145 *
146 * Since: 0.10
147 */
148 void
json_boxed_register_serialize_func(GType gboxed_type,JsonNodeType node_type,JsonBoxedSerializeFunc serialize_func)149 json_boxed_register_serialize_func (GType gboxed_type,
150 JsonNodeType node_type,
151 JsonBoxedSerializeFunc serialize_func)
152 {
153 BoxedTransform *t;
154
155 g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type));
156 g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE);
157
158 G_LOCK (boxed_serialize);
159
160 t = lookup_boxed_transform (boxed_serialize, gboxed_type, node_type);
161 if (t == NULL)
162 {
163 t = g_slice_new (BoxedTransform);
164
165 t->boxed_type = gboxed_type;
166 t->node_type = node_type;
167 t->serialize = serialize_func;
168
169 boxed_serialize = g_slist_insert_sorted (boxed_serialize, t,
170 boxed_transforms_cmp);
171 }
172 else
173 g_warning ("A serialization function for the boxed type %s into "
174 "JSON nodes of type %s already exists",
175 g_type_name (gboxed_type),
176 json_node_type_get_name (node_type));
177
178 G_UNLOCK (boxed_serialize);
179 }
180
181 /**
182 * json_boxed_register_deserialize_func: (skip)
183 * @gboxed_type: a boxed type
184 * @node_type: a node type
185 * @deserialize_func: deserialization function for @boxed_type from
186 * a #JsonNode of type @node_type
187 *
188 * Registers a deserialization function for a #GBoxed of type @gboxed_type
189 * from a #JsonNode of type @node_type
190 *
191 * Since: 0.10
192 */
193 void
json_boxed_register_deserialize_func(GType gboxed_type,JsonNodeType node_type,JsonBoxedDeserializeFunc deserialize_func)194 json_boxed_register_deserialize_func (GType gboxed_type,
195 JsonNodeType node_type,
196 JsonBoxedDeserializeFunc deserialize_func)
197 {
198 BoxedTransform *t;
199
200 g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type));
201 g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE);
202
203 G_LOCK (boxed_deserialize);
204
205 t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type);
206 if (t == NULL)
207 {
208 t = g_slice_new (BoxedTransform);
209
210 t->boxed_type = gboxed_type;
211 t->node_type = node_type;
212 t->deserialize = deserialize_func;
213
214 boxed_deserialize = g_slist_insert_sorted (boxed_deserialize, t,
215 boxed_transforms_cmp);
216 }
217 else
218 g_warning ("A deserialization function for the boxed type %s from "
219 "JSON nodes of type %s already exists",
220 g_type_name (gboxed_type),
221 json_node_type_get_name (node_type));
222
223 G_UNLOCK (boxed_deserialize);
224 }
225
226 /**
227 * json_boxed_can_serialize:
228 * @gboxed_type: a boxed type
229 * @node_type: (out): the #JsonNode type to which the boxed type can be
230 * serialized into
231 *
232 * Checks whether it is possible to serialize a #GBoxed of
233 * type @gboxed_type into a #JsonNode. The type of the
234 * #JsonNode is placed inside @node_type if the function
235 * returns %TRUE and it's undefined otherwise.
236 *
237 * Return value: %TRUE if the type can be serialized,
238 * and %FALSE otherwise.
239 *
240 * Since: 0.10
241 */
242 gboolean
json_boxed_can_serialize(GType gboxed_type,JsonNodeType * node_type)243 json_boxed_can_serialize (GType gboxed_type,
244 JsonNodeType *node_type)
245 {
246 BoxedTransform *t;
247
248 g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE);
249 g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE);
250
251 t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1);
252 if (t != NULL)
253 {
254 if (node_type)
255 *node_type = t->node_type;
256
257 return TRUE;
258 }
259
260 return FALSE;
261 }
262
263 /**
264 * json_boxed_can_deserialize:
265 * @gboxed_type: a boxed type
266 * @node_type: a #JsonNode type
267 *
268 * Checks whether it is possible to deserialize a #GBoxed of
269 * type @gboxed_type from a #JsonNode of type @node_type
270 *
271 * Return value: %TRUE if the type can be deserialized, %FALSE otherwise
272 *
273 * Since: 0.10
274 */
275 gboolean
json_boxed_can_deserialize(GType gboxed_type,JsonNodeType node_type)276 json_boxed_can_deserialize (GType gboxed_type,
277 JsonNodeType node_type)
278 {
279 BoxedTransform *t;
280
281 g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE);
282 g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE);
283
284 t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type);
285 if (t != NULL)
286 return TRUE;
287
288 return FALSE;
289 }
290
291 /**
292 * json_boxed_serialize:
293 * @gboxed_type: a boxed type
294 * @boxed: a pointer to a #GBoxed of type @gboxed_type
295 *
296 * Serializes @boxed, a pointer to a #GBoxed of type @gboxed_type,
297 * into a #JsonNode
298 *
299 * Return value: (nullable) (transfer full): a #JsonNode with the serialization of
300 * the boxed type, or %NULL if serialization either failed or was not possible
301 *
302 * Since: 0.10
303 */
304 JsonNode *
json_boxed_serialize(GType gboxed_type,gconstpointer boxed)305 json_boxed_serialize (GType gboxed_type,
306 gconstpointer boxed)
307 {
308 BoxedTransform *t;
309
310 g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL);
311 g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL);
312 g_return_val_if_fail (boxed != NULL, NULL);
313
314 t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1);
315 if (t != NULL && t->serialize != NULL)
316 return t->serialize (boxed);
317
318 return NULL;
319 }
320
321 /**
322 * json_boxed_deserialize:
323 * @gboxed_type: a boxed type
324 * @node: a #JsonNode
325 *
326 * Deserializes @node into a #GBoxed of @gboxed_type
327 *
328 * Return value: (transfer full): the newly allocated #GBoxed. Use
329 * g_boxed_free() to release the resources allocated by this
330 * function
331 *
332 * Since: 0.10
333 */
334 gpointer
json_boxed_deserialize(GType gboxed_type,JsonNode * node)335 json_boxed_deserialize (GType gboxed_type,
336 JsonNode *node)
337 {
338 JsonNodeType node_type;
339 BoxedTransform *t;
340
341 g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL);
342 g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL);
343 g_return_val_if_fail (node != NULL, NULL);
344
345 node_type = json_node_get_node_type (node);
346
347 t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type);
348 if (t != NULL && t->deserialize != NULL)
349 return t->deserialize (node);
350
351 return NULL;
352 }
353