[tracker/wip/carlosg/resources-as-cursors: 3/11] libtracker-sparql: Add JSON-LD serializer




commit 92e590acdc8aee13cc8a67fb0045afc74a55da2f
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Jul 11 20:25:12 2022 +0200

    libtracker-sparql: Add JSON-LD serializer
    
    This object converts a RDF TrackerSparqlCursor into a document
    in the JSON-LD format. Since this RDF data may not have a tidy
    order, the prettiness of the JSON-LD produced leaves a bit to
    desire, but this is still valid JSON-LD.

 src/libtracker-sparql/meson.build                  |   1 +
 src/libtracker-sparql/tracker-serializer-json-ld.c | 289 +++++++++++++++++++++
 src/libtracker-sparql/tracker-serializer-json-ld.h |  36 +++
 3 files changed, 326 insertions(+)
---
diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build
index 176c94f69..5c957e521 100644
--- a/src/libtracker-sparql/meson.build
+++ b/src/libtracker-sparql/meson.build
@@ -41,6 +41,7 @@ libtracker_sparql_c_sources = files(
     'tracker-statement.c',
     'tracker-serializer.c',
     'tracker-serializer-json.c',
+    'tracker-serializer-json-ld.c',
     'tracker-serializer-trig.c',
     'tracker-serializer-turtle.c',
     'tracker-serializer-xml.c',
diff --git a/src/libtracker-sparql/tracker-serializer-json-ld.c 
b/src/libtracker-sparql/tracker-serializer-json-ld.c
new file mode 100644
index 000000000..4dc10c52c
--- /dev/null
+++ b/src/libtracker-sparql/tracker-serializer-json-ld.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2020, Red Hat, Inc
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+/* Serialization of cursors to the JSON-LD format defined at:
+ *  https://www.w3.org/TR/json-ld/
+ */
+
+#include "config.h"
+
+#include "tracker-serializer-json-ld.h"
+
+#include <json-glib/json-glib.h>
+
+struct _TrackerSerializerJsonLD
+{
+       TrackerSerializer parent_instance;
+       JsonGenerator *generator;
+       JsonBuilder *builder;
+       GString *data;
+       GPtrArray *vars;
+       gsize current_pos;
+       gchar *cur_graph;
+       gchar *cur_subject;
+       guint stream_closed : 1;
+       guint cursor_started : 1;
+       guint cursor_finished : 1;
+       guint context_printed : 1;
+};
+
+G_DEFINE_TYPE (TrackerSerializerJsonLD, tracker_serializer_json_ld,
+               TRACKER_TYPE_SERIALIZER)
+
+static void
+tracker_serializer_json_ld_finalize (GObject *object)
+{
+       TrackerSerializerJsonLD *serializer_json_ld =
+               TRACKER_SERIALIZER_JSON_LD (object);
+
+       g_input_stream_close (G_INPUT_STREAM (object), NULL, NULL);
+       g_clear_pointer (&serializer_json_ld->cur_graph, g_free);
+       g_clear_pointer (&serializer_json_ld->cur_subject, g_free);
+
+       G_OBJECT_CLASS (tracker_serializer_json_ld_parent_class)->finalize (object);
+}
+
+static void
+generate_namespace_foreach (gpointer key,
+                            gpointer value,
+                            gpointer user_data)
+{
+       JsonBuilder *builder = user_data;;
+
+       json_builder_set_member_name (builder, key);
+       json_builder_add_string_value (builder, value);
+}
+
+static void
+finish_current_object (TrackerSerializerJsonLD *serializer_json_ld)
+{
+       JsonNode *node;
+
+       json_builder_end_object (serializer_json_ld->builder);
+
+       node = json_builder_get_root (serializer_json_ld->builder);
+       json_generator_set_root (serializer_json_ld->generator, node);
+       json_generator_to_gstring (serializer_json_ld->generator,
+                                  serializer_json_ld->data);
+       json_node_unref (node);
+}
+
+static gboolean
+serialize_up_to_position (TrackerSerializerJsonLD  *serializer_json_ld,
+                          gsize                     pos,
+                          GCancellable             *cancellable,
+                          GError                  **error)
+{
+       TrackerSparqlCursor *cursor;
+       TrackerNamespaceManager *namespaces;
+       GError *inner_error = NULL;
+       JsonNode *node;
+       gboolean check_finish = FALSE;
+
+       if (!serializer_json_ld->data)
+               serializer_json_ld->data = g_string_new (NULL);
+       if (!serializer_json_ld->generator)
+               serializer_json_ld->generator = json_generator_new ();
+       if (!serializer_json_ld->builder)
+               serializer_json_ld->builder = json_builder_new ();
+       if (!serializer_json_ld->vars)
+               serializer_json_ld->vars = g_ptr_array_new_with_free_func (g_free);
+
+       cursor = tracker_serializer_get_cursor (TRACKER_SERIALIZER (serializer_json_ld));
+       namespaces = tracker_serializer_get_namespaces (TRACKER_SERIALIZER (serializer_json_ld));
+
+       if (!serializer_json_ld->cursor_started)
+               g_string_append (serializer_json_ld->data, "[");
+
+       while (!serializer_json_ld->cursor_finished) {
+               const gchar *graph, *subject, *predicate, *object;
+               gboolean graph_changed, subject_changed;
+
+               if (!tracker_sparql_cursor_next (cursor, cancellable, &inner_error)) {
+                       if (inner_error) {
+                               g_propagate_error (error, inner_error);
+                               return FALSE;
+                       } else {
+                               finish_current_object (serializer_json_ld);
+                               serializer_json_ld->cursor_finished = TRUE;
+                               g_string_append (serializer_json_ld->data, "]}]");
+                               return TRUE;
+                       }
+               }
+
+               subject = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+               predicate = tracker_sparql_cursor_get_string (cursor, 1, NULL);
+               object = tracker_sparql_cursor_get_string (cursor, 2, NULL);
+               graph = tracker_sparql_cursor_get_string (cursor, 3, NULL);
+
+               graph_changed = g_strcmp0 (graph, serializer_json_ld->cur_graph) != 0;
+               subject_changed = g_strcmp0 (subject, serializer_json_ld->cur_subject) != 0;
+
+               if (!serializer_json_ld->cursor_started || graph_changed) {
+                       /* New/different graph */
+                       if (!serializer_json_ld->cursor_started) {
+                               g_string_append (serializer_json_ld->data, "{");
+                       } else {
+                               finish_current_object (serializer_json_ld);
+                               g_string_append (serializer_json_ld->data, "]},{");
+                       }
+
+                       json_builder_reset (serializer_json_ld->builder);
+                       json_builder_begin_object (serializer_json_ld->builder);
+                       tracker_namespace_manager_foreach (namespaces,
+                                                          generate_namespace_foreach,
+                                                          serializer_json_ld->builder);
+                       json_builder_end_object (serializer_json_ld->builder);
+
+                       node = json_builder_get_root (serializer_json_ld->builder);
+                       json_generator_set_root (serializer_json_ld->generator, node);
+
+                       g_string_append (serializer_json_ld->data, "\"@context\":");
+                       json_generator_to_gstring (serializer_json_ld->generator,
+                                                  serializer_json_ld->data);
+                       json_node_unref (node);
+
+                       if (graph) {
+                               g_string_append_printf (serializer_json_ld->data,
+                                                       ",\"@id\":\"%s\"",
+                                                       graph);
+                       }
+
+                       g_string_append (serializer_json_ld->data,
+                                        ",\"@graph\":[");
+
+                       g_clear_pointer (&serializer_json_ld->cur_graph, g_free);
+                       serializer_json_ld->cur_graph = g_strdup (graph);
+               }
+
+               if (subject_changed || graph_changed) {
+                       /* New/different subject */
+                       if (serializer_json_ld->cursor_started && subject_changed) {
+                               if (!graph_changed) {
+                                       finish_current_object (serializer_json_ld);
+                                       g_string_append (serializer_json_ld->data, ",");
+                               }
+
+                               check_finish = TRUE;
+                       }
+
+                       json_builder_reset (serializer_json_ld->builder);
+                       json_builder_begin_object (serializer_json_ld->builder);
+
+                       json_builder_set_member_name (serializer_json_ld->builder, "@id");
+                       json_builder_add_string_value (serializer_json_ld->builder, subject);
+
+                       g_clear_pointer (&serializer_json_ld->cur_subject, g_free);
+                       serializer_json_ld->cur_subject = g_strdup (subject);
+               }
+
+               if (g_strcmp0 (predicate, TRACKER_PREFIX_RDF "type") == 0) {
+                       json_builder_set_member_name (serializer_json_ld->builder, "@type");
+               } else {
+                       TrackerNamespaceManager *namespaces;
+                       gchar *compressed;
+
+                       namespaces = tracker_serializer_get_namespaces (TRACKER_SERIALIZER 
(serializer_json_ld));
+                       compressed = tracker_namespace_manager_compress_uri (namespaces, predicate);
+                       json_builder_set_member_name (serializer_json_ld->builder, compressed);
+                       g_free (compressed);
+               }
+
+               json_builder_add_string_value (serializer_json_ld->builder, object);
+
+               serializer_json_ld->cursor_started = TRUE;
+
+               if (check_finish && serializer_json_ld->data->len > pos)
+                       break;
+       }
+
+       return TRUE;
+}
+
+static gssize
+tracker_serializer_json_ld_read (GInputStream  *istream,
+                                 gpointer       buffer,
+                                 gsize          count,
+                                 GCancellable  *cancellable,
+                                 GError       **error)
+{
+       TrackerSerializerJsonLD *serializer_json_ld = TRACKER_SERIALIZER_JSON_LD (istream);
+       gsize bytes_unflushed, bytes_copied;
+
+       if (serializer_json_ld->stream_closed ||
+           (serializer_json_ld->cursor_finished &&
+            serializer_json_ld->current_pos == serializer_json_ld->data->len))
+               return 0;
+
+       if (!serialize_up_to_position (serializer_json_ld,
+                                      serializer_json_ld->current_pos + count,
+                                      cancellable,
+                                      error))
+               return -1;
+
+       bytes_unflushed =
+               serializer_json_ld->data->len - serializer_json_ld->current_pos;
+       bytes_copied = MIN (count, bytes_unflushed);
+
+       memcpy (buffer,
+               &serializer_json_ld->data->str[serializer_json_ld->current_pos],
+               bytes_copied);
+       serializer_json_ld->current_pos += bytes_copied;
+
+       return bytes_copied;
+}
+
+static gboolean
+tracker_serializer_json_ld_close (GInputStream  *istream,
+                                  GCancellable  *cancellable,
+                                  GError       **error)
+{
+       TrackerSerializerJsonLD *serializer_json_ld = TRACKER_SERIALIZER_JSON_LD (istream);
+
+       if (serializer_json_ld->data) {
+               g_string_free (serializer_json_ld->data, TRUE);
+               serializer_json_ld->data = NULL;
+       }
+
+       g_clear_object (&serializer_json_ld->generator);
+       g_clear_object (&serializer_json_ld->builder);
+       serializer_json_ld->stream_closed = TRUE;
+       g_clear_pointer (&serializer_json_ld->vars, g_ptr_array_unref);
+
+       return TRUE;
+}
+
+static void
+tracker_serializer_json_ld_class_init (TrackerSerializerJsonLDClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);
+
+       object_class->finalize = tracker_serializer_json_ld_finalize;
+
+       istream_class->read_fn = tracker_serializer_json_ld_read;
+       istream_class->close_fn = tracker_serializer_json_ld_close;
+}
+
+static void
+tracker_serializer_json_ld_init (TrackerSerializerJsonLD *serializer)
+{
+}
diff --git a/src/libtracker-sparql/tracker-serializer-json-ld.h 
b/src/libtracker-sparql/tracker-serializer-json-ld.h
new file mode 100644
index 000000000..13325e00f
--- /dev/null
+++ b/src/libtracker-sparql/tracker-serializer-json-ld.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022, Red Hat, Inc
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#ifndef TRACKER_SERIALIZER_JSON_LD_H
+#define TRACKER_SERIALIZER_JSON_LD_H
+
+#include <libtracker-sparql/tracker-sparql.h>
+#include <libtracker-sparql/tracker-private.h>
+#include <libtracker-sparql/tracker-serializer.h>
+
+#define TRACKER_TYPE_SERIALIZER_JSON_LD (tracker_serializer_json_ld_get_type())
+
+G_DECLARE_FINAL_TYPE (TrackerSerializerJsonLD,
+                      tracker_serializer_json_ld,
+                      TRACKER, SERIALIZER_JSON_LD,
+                      TrackerSerializer)
+
+#endif /* TRACKER_SERIALIZER_JSON_LD_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]