/src/tinysparql/src/libtinysparql/tracker-deserializer-json.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2022, Red Hat Inc. |
3 | | * |
4 | | * This library is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Lesser General Public |
6 | | * License as published by the Free Software Foundation; either |
7 | | * version 2.1 of the License, or (at your option) any later version. |
8 | | * |
9 | | * This library is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | | * Lesser General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Lesser General Public |
15 | | * License along with this library; if not, write to the |
16 | | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | | * Boston, MA 02110-1301, USA. |
18 | | * |
19 | | * Author: Carlos Garnacho <carlosg@gnome.org> |
20 | | */ |
21 | | |
22 | | /* Deserialization to cursors for the JSON format defined at: |
23 | | * https://www.w3.org/TR/sparql11-results-json/ |
24 | | */ |
25 | | |
26 | | #include "config.h" |
27 | | |
28 | | #include "tracker-deserializer-json.h" |
29 | | |
30 | | #include <json-glib/json-glib.h> |
31 | | |
32 | | typedef struct { |
33 | | TrackerSparqlValueType type; |
34 | | const gchar *str; |
35 | | const gchar *langtag; |
36 | | } ColumnData; |
37 | | |
38 | | struct _TrackerDeserializerJson { |
39 | | TrackerDeserializer parent_instance; |
40 | | GArray *columns; |
41 | | JsonParser *parser; |
42 | | JsonArray *vars; |
43 | | JsonArray *results; |
44 | | JsonObject *current_row; |
45 | | guint idx; |
46 | | gboolean started; |
47 | | GError *init_error; |
48 | | }; |
49 | | |
50 | 0 | G_DEFINE_TYPE (TrackerDeserializerJson, |
51 | 0 | tracker_deserializer_json, |
52 | 0 | TRACKER_TYPE_DESERIALIZER) |
53 | 0 |
|
54 | 0 | static void |
55 | 0 | tracker_deserializer_json_finalize (GObject *object) |
56 | 0 | { |
57 | 0 | TrackerDeserializerJson *deserializer = |
58 | 0 | TRACKER_DESERIALIZER_JSON (object); |
59 | |
|
60 | 0 | g_clear_object (&deserializer->parser); |
61 | 0 | g_array_unref (deserializer->columns); |
62 | |
|
63 | 0 | G_OBJECT_CLASS (tracker_deserializer_json_parent_class)->finalize (object); |
64 | 0 | } |
65 | | |
66 | | static void |
67 | | tracker_deserializer_json_constructed (GObject *object) |
68 | 0 | { |
69 | 0 | TrackerDeserializerJson *deserializer = |
70 | 0 | TRACKER_DESERIALIZER_JSON (object); |
71 | 0 | GInputStream *stream; |
72 | 0 | JsonNode *root_node; |
73 | 0 | JsonObject *root, *head, *results; |
74 | |
|
75 | 0 | G_OBJECT_CLASS (tracker_deserializer_json_parent_class)->constructed (object); |
76 | |
|
77 | 0 | stream = tracker_deserializer_get_stream (TRACKER_DESERIALIZER (object)); |
78 | |
|
79 | 0 | if (json_parser_load_from_stream (deserializer->parser, |
80 | 0 | stream, |
81 | 0 | NULL, |
82 | 0 | &deserializer->init_error)) { |
83 | 0 | root_node = json_parser_get_root (deserializer->parser); |
84 | 0 | root = json_node_get_object (root_node); |
85 | |
|
86 | 0 | head = json_object_get_object_member (root, "head"); |
87 | 0 | deserializer->vars = json_object_get_array_member (head, "vars"); |
88 | |
|
89 | 0 | results = json_object_get_object_member (root, "results"); |
90 | 0 | deserializer->results = json_object_get_array_member (results, "bindings"); |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | static gint |
95 | | tracker_deserializer_json_get_n_columns (TrackerSparqlCursor *cursor) |
96 | 0 | { |
97 | 0 | TrackerDeserializerJson *deserializer = |
98 | 0 | TRACKER_DESERIALIZER_JSON (cursor); |
99 | |
|
100 | 0 | return json_array_get_length (deserializer->vars); |
101 | 0 | } |
102 | | |
103 | | static TrackerSparqlValueType |
104 | | tracker_deserializer_json_get_value_type (TrackerSparqlCursor *cursor, |
105 | | gint column) |
106 | 0 | { |
107 | 0 | TrackerDeserializerJson *deserializer = |
108 | 0 | TRACKER_DESERIALIZER_JSON (cursor); |
109 | 0 | ColumnData *col; |
110 | |
|
111 | 0 | if (column < 0 || column >= (gint) deserializer->columns->len) |
112 | 0 | return TRACKER_SPARQL_VALUE_TYPE_UNBOUND; |
113 | | |
114 | 0 | col = &g_array_index (deserializer->columns, ColumnData, column); |
115 | |
|
116 | 0 | return col->type; |
117 | 0 | } |
118 | | |
119 | | static const gchar * |
120 | | tracker_deserializer_json_get_variable_name (TrackerSparqlCursor *cursor, |
121 | | gint column) |
122 | 0 | { |
123 | 0 | TrackerDeserializerJson *deserializer = |
124 | 0 | TRACKER_DESERIALIZER_JSON (cursor); |
125 | |
|
126 | 0 | if (column < 0 || (guint) column >= json_array_get_length (deserializer->vars)) |
127 | 0 | return NULL; |
128 | | |
129 | 0 | return json_array_get_string_element (deserializer->vars, column); |
130 | 0 | } |
131 | | |
132 | | static const gchar * |
133 | | tracker_deserializer_json_get_string (TrackerSparqlCursor *cursor, |
134 | | gint column, |
135 | | const gchar **langtag, |
136 | | glong *length) |
137 | 0 | { |
138 | 0 | TrackerDeserializerJson *deserializer = |
139 | 0 | TRACKER_DESERIALIZER_JSON (cursor); |
140 | 0 | ColumnData *col; |
141 | |
|
142 | 0 | if (length) |
143 | 0 | *length = 0; |
144 | 0 | if (langtag) |
145 | 0 | *langtag = NULL; |
146 | |
|
147 | 0 | if (column < 0 || column >= (gint) deserializer->columns->len) |
148 | 0 | return NULL; |
149 | | |
150 | 0 | col = &g_array_index (deserializer->columns, ColumnData, column); |
151 | |
|
152 | 0 | if (length) |
153 | 0 | *length = strlen (col->str); |
154 | 0 | if (langtag) |
155 | 0 | *langtag = col->langtag; |
156 | |
|
157 | 0 | return col->str; |
158 | 0 | } |
159 | | |
160 | | static gboolean |
161 | | parse_column_type (JsonObject *column, |
162 | | TrackerSparqlValueType *value, |
163 | | GError **error) |
164 | 0 | { |
165 | 0 | const gchar *type; |
166 | |
|
167 | 0 | if (!json_object_has_member (column, "type")) { |
168 | 0 | g_set_error (error, |
169 | 0 | TRACKER_SPARQL_ERROR, |
170 | 0 | TRACKER_SPARQL_ERROR_PARSE, |
171 | 0 | "Column object does not have 'type' member"); |
172 | 0 | return FALSE; |
173 | 0 | } |
174 | | |
175 | 0 | type = json_object_get_string_member (column, "type"); |
176 | |
|
177 | 0 | if (g_str_equal (type, "uri")) { |
178 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_URI; |
179 | 0 | } else if (g_str_equal (type, "bnode")) { |
180 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE; |
181 | 0 | } else if (g_str_equal (type, "literal")) { |
182 | 0 | const gchar *datatype, *suffix; |
183 | |
|
184 | 0 | if (json_object_has_member (column, "datatype")) |
185 | 0 | datatype = json_object_get_string_member (column, "datatype"); |
186 | 0 | else |
187 | 0 | datatype = TRACKER_PREFIX_XSD "string"; |
188 | |
|
189 | 0 | if (!g_str_has_prefix (datatype, TRACKER_PREFIX_XSD)) { |
190 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_STRING; |
191 | 0 | return TRUE; |
192 | 0 | } |
193 | | |
194 | 0 | suffix = &datatype[strlen (TRACKER_PREFIX_XSD)]; |
195 | |
|
196 | 0 | if (g_str_equal (suffix, "byte") || |
197 | 0 | g_str_equal (suffix, "int") || |
198 | 0 | g_str_equal (suffix, "integer") || |
199 | 0 | g_str_equal (suffix, "long")) |
200 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_INTEGER; |
201 | 0 | else if (g_str_equal (suffix, "decimal") || |
202 | 0 | g_str_equal (suffix, "double")) |
203 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_DOUBLE; |
204 | 0 | else if (g_str_equal (suffix, "date") || |
205 | 0 | g_str_equal (suffix, "dateTime")) |
206 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_DATETIME; |
207 | 0 | else if (g_str_equal (suffix, "boolean")) |
208 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_BOOLEAN; |
209 | 0 | else |
210 | 0 | *value = TRACKER_SPARQL_VALUE_TYPE_STRING; |
211 | 0 | } else { |
212 | 0 | g_set_error (error, |
213 | 0 | TRACKER_SPARQL_ERROR, |
214 | 0 | TRACKER_SPARQL_ERROR_PARSE, |
215 | 0 | "Unknown type '%s'", type); |
216 | 0 | return FALSE; |
217 | 0 | } |
218 | | |
219 | 0 | return TRUE; |
220 | 0 | } |
221 | | |
222 | | static gboolean |
223 | | parse_column_string (JsonObject *column, |
224 | | const gchar **str, |
225 | | GError **error) |
226 | 0 | { |
227 | 0 | if (!json_object_has_member (column, "value")) { |
228 | 0 | g_set_error (error, |
229 | 0 | TRACKER_SPARQL_ERROR, |
230 | 0 | TRACKER_SPARQL_ERROR_PARSE, |
231 | 0 | "Column object does not have 'value' member"); |
232 | 0 | return FALSE; |
233 | 0 | } |
234 | | |
235 | 0 | *str = json_object_get_string_member (column, "value"); |
236 | 0 | return TRUE; |
237 | 0 | } |
238 | | |
239 | | static const gchar * |
240 | | parse_column_langtag (JsonObject *column) |
241 | 0 | { |
242 | 0 | if (!json_object_has_member (column, "xml:lang")) |
243 | 0 | return NULL; |
244 | | |
245 | 0 | return json_object_get_string_member (column, "xml:lang"); |
246 | 0 | } |
247 | | |
248 | | static gboolean |
249 | | parse_row (TrackerDeserializerJson *deserializer, |
250 | | GError **error) |
251 | 0 | { |
252 | 0 | TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR (deserializer); |
253 | 0 | const gchar *var_name; |
254 | 0 | JsonObject *column; |
255 | 0 | gint n_columns, i; |
256 | |
|
257 | 0 | g_array_set_size (deserializer->columns, 0); |
258 | 0 | n_columns = tracker_sparql_cursor_get_n_columns (cursor); |
259 | |
|
260 | 0 | for (i = 0; i < n_columns; i++) { |
261 | 0 | ColumnData col = { 0 }; |
262 | |
|
263 | 0 | var_name = tracker_sparql_cursor_get_variable_name (cursor, i); |
264 | |
|
265 | 0 | if (json_object_has_member (deserializer->current_row, var_name)) { |
266 | 0 | column = json_object_get_object_member (deserializer->current_row, |
267 | 0 | var_name); |
268 | 0 | if (column) { |
269 | 0 | if (!parse_column_string (column, &col.str, error) || |
270 | 0 | !parse_column_type (column, &col.type, error)) |
271 | 0 | return FALSE; |
272 | | |
273 | 0 | col.langtag = parse_column_langtag (column); |
274 | 0 | g_array_append_val (deserializer->columns, col); |
275 | 0 | continue; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | 0 | col = (ColumnData) { TRACKER_SPARQL_VALUE_TYPE_UNBOUND, NULL }; |
280 | 0 | g_array_append_val (deserializer->columns, col); |
281 | 0 | } |
282 | | |
283 | 0 | return TRUE; |
284 | 0 | } |
285 | | |
286 | | static gboolean |
287 | | tracker_deserializer_json_next (TrackerSparqlCursor *cursor, |
288 | | GCancellable *cancellable, |
289 | | GError **error) |
290 | 0 | { |
291 | 0 | TrackerDeserializerJson *deserializer = |
292 | 0 | TRACKER_DESERIALIZER_JSON (cursor); |
293 | |
|
294 | 0 | g_array_set_size (deserializer->columns, 0); |
295 | |
|
296 | 0 | if (deserializer->init_error) { |
297 | 0 | GError *init_error; |
298 | |
|
299 | 0 | init_error = g_steal_pointer (&deserializer->init_error); |
300 | 0 | g_propagate_error (error, init_error); |
301 | 0 | return FALSE; |
302 | 0 | } |
303 | | |
304 | 0 | if (deserializer->started) |
305 | 0 | deserializer->idx++; |
306 | |
|
307 | 0 | if (deserializer->idx >= json_array_get_length (deserializer->results)) |
308 | 0 | return FALSE; |
309 | | |
310 | 0 | if (g_cancellable_set_error_if_cancelled (cancellable, error)) |
311 | 0 | return FALSE; |
312 | | |
313 | 0 | deserializer->current_row = |
314 | 0 | json_array_get_object_element (deserializer->results, |
315 | 0 | deserializer->idx); |
316 | 0 | deserializer->started = TRUE; |
317 | |
|
318 | 0 | return parse_row (deserializer, error); |
319 | 0 | } |
320 | | |
321 | | static void |
322 | | tracker_deserializer_json_next_async (TrackerSparqlCursor *cursor, |
323 | | GCancellable *cancellable, |
324 | | GAsyncReadyCallback cb, |
325 | | gpointer user_data) |
326 | 0 | { |
327 | 0 | GError *error = NULL; |
328 | 0 | GTask *task; |
329 | |
|
330 | 0 | task = g_task_new (cursor, cancellable, cb, user_data); |
331 | |
|
332 | 0 | if (tracker_sparql_cursor_next (cursor, cancellable, &error)) |
333 | 0 | g_task_return_boolean (task, TRUE); |
334 | 0 | else if (!error) |
335 | 0 | g_task_return_boolean (task, FALSE); |
336 | 0 | else |
337 | 0 | g_task_return_error (task, error); |
338 | |
|
339 | 0 | g_object_unref (task); |
340 | 0 | } |
341 | | |
342 | | static gboolean |
343 | | tracker_deserializer_json_next_finish (TrackerSparqlCursor *cursor, |
344 | | GAsyncResult *res, |
345 | | GError **error) |
346 | 0 | { |
347 | 0 | return g_task_propagate_boolean (G_TASK (res), error); |
348 | 0 | } |
349 | | |
350 | | gboolean |
351 | | tracker_deserializer_json_get_parser_location (TrackerDeserializer *deserializer, |
352 | | const char **name, |
353 | | goffset *line_no, |
354 | | goffset *column_no) |
355 | 0 | { |
356 | 0 | if (name) |
357 | 0 | *name = tracker_deserializer_get_name (deserializer); |
358 | |
|
359 | 0 | return FALSE; |
360 | 0 | } |
361 | | |
362 | | static void |
363 | | tracker_deserializer_json_class_init (TrackerDeserializerJsonClass *klass) |
364 | 0 | { |
365 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
366 | 0 | TrackerSparqlCursorClass *cursor_class = |
367 | 0 | TRACKER_SPARQL_CURSOR_CLASS (klass); |
368 | 0 | TrackerDeserializerClass *deserializer_class = |
369 | 0 | TRACKER_DESERIALIZER_CLASS (klass); |
370 | |
|
371 | 0 | object_class->finalize = tracker_deserializer_json_finalize; |
372 | 0 | object_class->constructed = tracker_deserializer_json_constructed; |
373 | |
|
374 | 0 | cursor_class->get_n_columns = tracker_deserializer_json_get_n_columns; |
375 | 0 | cursor_class->get_value_type = tracker_deserializer_json_get_value_type; |
376 | 0 | cursor_class->get_variable_name = tracker_deserializer_json_get_variable_name; |
377 | 0 | cursor_class->get_string = tracker_deserializer_json_get_string; |
378 | 0 | cursor_class->next = tracker_deserializer_json_next; |
379 | 0 | cursor_class->next_async = tracker_deserializer_json_next_async; |
380 | 0 | cursor_class->next_finish = tracker_deserializer_json_next_finish; |
381 | |
|
382 | 0 | deserializer_class->get_parser_location = |
383 | 0 | tracker_deserializer_json_get_parser_location; |
384 | 0 | } |
385 | | |
386 | | static void |
387 | | tracker_deserializer_json_init (TrackerDeserializerJson *deserializer) |
388 | 0 | { |
389 | 0 | deserializer->parser = json_parser_new (); |
390 | 0 | deserializer->columns = g_array_new (FALSE, FALSE, sizeof (ColumnData)); |
391 | 0 | } |
392 | | |
393 | | TrackerSparqlCursor * |
394 | | tracker_deserializer_json_new (GInputStream *stream, |
395 | | TrackerNamespaceManager *namespaces) |
396 | 0 | { |
397 | 0 | return g_object_new (TRACKER_TYPE_DESERIALIZER_JSON, |
398 | 0 | "stream", stream, |
399 | 0 | "namespace-manager", namespaces, |
400 | | NULL); |
401 | 0 | } |