Coverage Report

Created: 2026-04-01 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinysparql/src/libtinysparql/tracker-serializer-json.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2020, 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
/* Serialization of cursors to the JSON format defined at:
23
 *  https://www.w3.org/TR/sparql11-results-json/
24
 */
25
26
#include "config.h"
27
28
#include "tracker-serializer-json.h"
29
30
#include <json-glib/json-glib.h>
31
32
struct _TrackerSerializerJson
33
{
34
  TrackerSerializer parent_instance;
35
  JsonGenerator *generator;
36
  GString *data;
37
  GPtrArray *vars;
38
  gsize current_pos;
39
  guint stream_closed : 1;
40
  guint cursor_started : 1;
41
  guint cursor_finished : 1;
42
  guint head_printed : 1;
43
};
44
45
0
G_DEFINE_TYPE (TrackerSerializerJson, tracker_serializer_json,
46
0
               TRACKER_TYPE_SERIALIZER)
47
0
48
0
static void
49
0
tracker_serializer_json_finalize (GObject *object)
50
0
{
51
0
  g_input_stream_close (G_INPUT_STREAM (object), NULL, NULL);
52
53
0
  G_OBJECT_CLASS (tracker_serializer_json_parent_class)->finalize (object);
54
0
}
55
56
static gboolean
57
serialize_up_to_position (TrackerSerializerJson  *serializer_json,
58
                          gsize                   pos,
59
                          GCancellable           *cancellable,
60
                          GError                **error)
61
0
{
62
0
  TrackerSparqlCursor *cursor;
63
0
  GError *inner_error = NULL;
64
0
  JsonBuilder *builder;
65
0
  JsonNode *node;
66
0
  gint i;
67
68
0
  if (!serializer_json->data)
69
0
    serializer_json->data = g_string_new (NULL);
70
0
  if (!serializer_json->generator)
71
0
    serializer_json->generator = json_generator_new ();
72
0
  if (!serializer_json->vars)
73
0
    serializer_json->vars = g_ptr_array_new_with_free_func (g_free);
74
75
0
  cursor = tracker_serializer_get_cursor (TRACKER_SERIALIZER (serializer_json));
76
0
  builder = json_builder_new ();
77
78
0
  if (!serializer_json->head_printed) {
79
0
    json_builder_reset (builder);
80
0
    json_builder_begin_object (builder);
81
0
    json_builder_set_member_name (builder, "vars");
82
0
    json_builder_begin_array (builder);
83
84
0
    for (i = 0; i < tracker_sparql_cursor_get_n_columns (cursor); i++) {
85
0
      const gchar *var;
86
87
0
      var = tracker_sparql_cursor_get_variable_name (cursor, i);
88
89
0
      if (var && *var) {
90
0
        g_ptr_array_add (serializer_json->vars,
91
0
                         g_strdup (var));
92
0
      } else {
93
0
        g_ptr_array_add (serializer_json->vars,
94
0
                         g_strdup_printf ("var%d", i + 1));
95
0
      }
96
97
0
      json_builder_add_string_value (builder,
98
0
                                     g_ptr_array_index (serializer_json->vars, i));
99
0
    }
100
101
0
    json_builder_end_array (builder);
102
0
    json_builder_end_object (builder);
103
104
0
    node = json_builder_get_root (builder);
105
106
0
    g_string_append_printf (serializer_json->data,
107
0
                            "{\"head\":");
108
0
    json_generator_set_root (serializer_json->generator, node);
109
0
    json_generator_to_gstring (serializer_json->generator,
110
0
                               serializer_json->data);
111
0
    g_string_append_printf (serializer_json->data,
112
0
                            ",\"results\":{\"bindings\":[");
113
114
0
    serializer_json->head_printed = TRUE;
115
0
    json_node_free (node);
116
0
  }
117
118
0
  while (!serializer_json->cursor_finished &&
119
0
         serializer_json->data->len < pos) {
120
0
    if (!tracker_sparql_cursor_next (cursor, cancellable, &inner_error)) {
121
0
      if (inner_error) {
122
0
        g_propagate_error (error, inner_error);
123
0
        g_clear_object (&builder);
124
0
        return FALSE;
125
0
      } else {
126
0
        serializer_json->cursor_finished = TRUE;
127
0
        g_string_append (serializer_json->data, "]}}");
128
0
        break;
129
0
      }
130
0
    } else {
131
0
      if (serializer_json->cursor_started)
132
0
        g_string_append_c (serializer_json->data, ',');
133
134
0
      serializer_json->cursor_started = TRUE;
135
0
    }
136
137
0
    json_builder_reset (builder);
138
0
    json_builder_begin_object (builder);
139
140
0
    for (i = 0; i < tracker_sparql_cursor_get_n_columns (cursor); i++) {
141
0
      const gchar *var, *str, *type = NULL, *datatype = NULL, *langtag = NULL;
142
143
0
      switch (tracker_sparql_cursor_get_value_type (cursor, i)) {
144
0
      case TRACKER_SPARQL_VALUE_TYPE_URI:
145
0
        type = "uri";
146
0
        break;
147
0
      case TRACKER_SPARQL_VALUE_TYPE_STRING:
148
0
        type = "literal";
149
0
        datatype = TRACKER_PREFIX_XSD "string";
150
0
        break;
151
0
      case TRACKER_SPARQL_VALUE_TYPE_INTEGER:
152
0
        type = "literal";
153
0
        datatype = TRACKER_PREFIX_XSD "integer";
154
0
        break;
155
0
      case TRACKER_SPARQL_VALUE_TYPE_BOOLEAN:
156
0
        type = "literal";
157
0
        datatype = TRACKER_PREFIX_XSD "boolean";
158
0
        break;
159
0
      case TRACKER_SPARQL_VALUE_TYPE_DOUBLE:
160
0
        type = "literal";
161
0
        datatype = TRACKER_PREFIX_XSD "double";
162
0
        break;
163
0
      case TRACKER_SPARQL_VALUE_TYPE_DATETIME:
164
0
        type = "literal";
165
0
        datatype = TRACKER_PREFIX_XSD "dateTime";
166
0
        break;
167
0
      case TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE:
168
0
        type = "bnode";
169
0
        break;
170
0
      case TRACKER_SPARQL_VALUE_TYPE_UNBOUND:
171
0
                                continue;
172
0
      }
173
174
0
      var = g_ptr_array_index (serializer_json->vars, i);
175
0
      json_builder_set_member_name (builder, var);
176
177
0
      json_builder_begin_object (builder);
178
179
0
      json_builder_set_member_name (builder, "type");
180
0
      json_builder_add_string_value (builder, type);
181
182
0
      str = tracker_sparql_cursor_get_langstring (cursor, i, &langtag, NULL);
183
184
0
      if (langtag) {
185
0
        datatype = TRACKER_PREFIX_RDF "langString";
186
0
        json_builder_set_member_name (builder, "xml:lang");
187
0
        json_builder_add_string_value (builder, langtag);
188
0
      }
189
190
0
      if (datatype) {
191
0
        json_builder_set_member_name (builder, "datatype");
192
0
        json_builder_add_string_value (builder, datatype);
193
0
      }
194
195
0
      if (str) {
196
0
        json_builder_set_member_name (builder, "value");
197
0
        json_builder_add_string_value (builder,
198
0
                                       tracker_sparql_cursor_get_string (cursor, i, NULL));
199
0
        json_builder_end_object (builder);
200
0
      }
201
0
    }
202
203
0
    json_builder_end_object (builder);
204
0
    node = json_builder_get_root (builder);
205
206
0
    json_generator_set_root (serializer_json->generator, node);
207
0
    json_generator_to_gstring (serializer_json->generator,
208
0
                               serializer_json->data);
209
0
    json_node_free (node);
210
0
  }
211
212
0
  g_clear_object (&builder);
213
214
0
  return TRUE;
215
0
}
216
217
static gssize
218
tracker_serializer_json_read (GInputStream  *istream,
219
                              gpointer       buffer,
220
                              gsize          count,
221
                              GCancellable  *cancellable,
222
                              GError       **error)
223
0
{
224
0
  TrackerSerializerJson *serializer_json = TRACKER_SERIALIZER_JSON (istream);
225
0
  gsize bytes_unflushed, bytes_copied;
226
227
0
  if (serializer_json->stream_closed ||
228
0
      (serializer_json->cursor_finished &&
229
0
       serializer_json->current_pos == serializer_json->data->len))
230
0
    return 0;
231
232
0
  if (!serialize_up_to_position (serializer_json,
233
0
                                 serializer_json->current_pos + count,
234
0
                                 cancellable,
235
0
                                 error))
236
0
    return -1;
237
238
0
  bytes_unflushed =
239
0
    serializer_json->data->len - serializer_json->current_pos;
240
0
  bytes_copied = MIN (count, bytes_unflushed);
241
242
0
  memcpy (buffer,
243
0
          &serializer_json->data->str[serializer_json->current_pos],
244
0
          bytes_copied);
245
0
  serializer_json->current_pos += bytes_copied;
246
247
0
  return bytes_copied;
248
0
}
249
250
static gboolean
251
tracker_serializer_json_close (GInputStream  *istream,
252
                               GCancellable  *cancellable,
253
                               GError       **error)
254
0
{
255
0
  TrackerSerializerJson *serializer_json = TRACKER_SERIALIZER_JSON (istream);
256
257
0
  if (serializer_json->data) {
258
0
    g_string_free (serializer_json->data, TRUE);
259
0
    serializer_json->data = NULL;
260
0
  }
261
262
0
  g_clear_object (&serializer_json->generator);
263
0
  serializer_json->stream_closed = TRUE;
264
0
  g_clear_pointer (&serializer_json->vars, g_ptr_array_unref);
265
266
0
  return TRUE;
267
0
}
268
269
static void
270
tracker_serializer_json_class_init (TrackerSerializerJsonClass *klass)
271
0
{
272
0
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
273
0
  GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);
274
275
0
  object_class->finalize = tracker_serializer_json_finalize;
276
277
0
  istream_class->read_fn = tracker_serializer_json_read;
278
0
  istream_class->close_fn = tracker_serializer_json_close;
279
0
}
280
281
static void
282
tracker_serializer_json_init (TrackerSerializerJson *serializer)
283
0
{
284
0
}