summaryrefslogtreecommitdiff
path: root/src/libtracker-sparql/tracker-resource.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libtracker-sparql/tracker-resource.c')
-rw-r--r--src/libtracker-sparql/tracker-resource.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/src/libtracker-sparql/tracker-resource.c b/src/libtracker-sparql/tracker-resource.c
index 694f5ac87..2270900fc 100644
--- a/src/libtracker-sparql/tracker-resource.c
+++ b/src/libtracker-sparql/tracker-resource.c
@@ -18,6 +18,7 @@
*/
#include <glib.h>
+#include <json-glib/json-glib.h>
#include <string.h>
@@ -1542,3 +1543,177 @@ tracker_resource_print_sparql_update (TrackerResource *resource,
return g_string_free (context.string, FALSE);
}
+
+typedef struct {
+ TrackerNamespaceManager *all_namespaces, *our_namespaces;
+ JsonBuilder *builder;
+ GList *done_list;
+} GenerateJsonldData;
+
+static void generate_jsonld_foreach (gpointer key, gpointer value_ptr, gpointer user_data);
+
+static void
+tracker_resource_generate_jsonld (TrackerResource *self,
+ GenerateJsonldData *data)
+{
+ TrackerResourcePrivate *priv = GET_PRIVATE (self);
+ JsonBuilder *builder = data->builder;
+
+ /* The JSON-LD spec says it is "important that nodes have an identifier", but
+ * doesn't mandate one. I think it's better to omit the ID for blank nodes
+ * (where the caller passed NULL as an identifier) than to emit something
+ * SPARQL-specific like '_:123'.
+ */
+ if (strncmp (priv->identifier, "_:", 2) != 0) {
+ json_builder_set_member_name (builder, "@id");
+ json_builder_add_string_value (builder, priv->identifier);
+ }
+
+ g_hash_table_foreach (priv->properties, generate_jsonld_foreach, data);
+};
+
+static void
+generate_jsonld_value (const GValue *value,
+ GenerateJsonldData *data)
+{
+ JsonNode *node;
+
+ if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+ TrackerResource *resource;
+
+ resource = TRACKER_RESOURCE (g_value_get_object (value));
+
+ if (g_list_find_custom (data->done_list, resource, (GCompareFunc) tracker_resource_compare) == NULL) {
+ json_builder_begin_object (data->builder);
+
+ tracker_resource_generate_jsonld (resource, data);
+
+ json_builder_end_object (data->builder);
+
+ data->done_list = g_list_prepend (data->done_list, resource);
+ } else {
+ json_builder_add_string_value (data->builder, tracker_resource_get_identifier(resource));
+ }
+ } else if (G_VALUE_HOLDS (value, TRACKER_TYPE_URI)) {
+ /* URIs can be treated the same as strings in JSON-LD provided the @context
+ * sets the type of that property correctly. However, json_node_set_value()
+ * will reject a GValue holding TRACKER_TYPE_URI, so we have to extract the
+ * string manually here.
+ */
+ const char *uri = g_value_get_string (value);
+ maybe_intern_prefix_of_compact_uri (data->all_namespaces, data->our_namespaces, uri);
+ node = json_node_new (JSON_NODE_VALUE);
+ json_node_set_string (node, uri);
+ json_builder_add_value (data->builder, node);
+ } else {
+ node = json_node_new (JSON_NODE_VALUE);
+ json_node_set_value (node, value);
+ json_builder_add_value (data->builder, node);
+ }
+}
+
+static void
+generate_jsonld_foreach (gpointer key,
+ gpointer value_ptr,
+ gpointer user_data)
+{
+ const char *property = key;
+ const GValue *value = value_ptr;
+ GenerateJsonldData *data = user_data;
+ JsonBuilder *builder = data->builder;
+
+ if (strcmp (property, "rdf:type") == 0) {
+ property = "@type";
+ } else {
+ maybe_intern_prefix_of_compact_uri (data->all_namespaces, data->our_namespaces, property);
+ }
+
+ json_builder_set_member_name (builder, property);
+
+ if (G_VALUE_HOLDS (value, G_TYPE_PTR_ARRAY)) {
+ json_builder_begin_array (builder);
+ g_ptr_array_foreach (g_value_get_boxed (value), (GFunc) generate_jsonld_value, data);
+ json_builder_end_array (builder);
+ } else {
+ generate_jsonld_value (value, data);
+ }
+}
+
+static void
+generate_jsonld_namespace_mapping_foreach (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GenerateJsonldData *data = user_data;
+
+ json_builder_set_member_name (data->builder, key);
+ json_builder_add_string_value (data->builder, value);
+}
+
+
+/**
+ * tracker_resource_print_jsonld:
+ * @self: a #TrackerResource
+ * @namespaces: (allow-none): a set of prefixed URLs, or %NULL to use the
+ * default set
+ *
+ * Serialize all the information in @resource as a JSON-LD document.
+ *
+ * See <http://www.jsonld.org/> for more information on the JSON-LD
+ * serialization format.
+ *
+ * The @namespaces object is used to expand any compact URI values. In most
+ * cases you should pass %NULL, which means the set of namespaces returned by
+ * tracker_namespace_manager_get_default() will be used. This defines the
+ * usual prefixes for all of the ontologies that Tracker ships with by default.
+ *
+ * Returns: a newly-allocated string containing JSON-LD data.
+ *
+ * Since: 2.0.5
+ */
+char *
+tracker_resource_print_jsonld (TrackerResource *self,
+ TrackerNamespaceManager *namespaces)
+{
+ GenerateJsonldData context;
+ JsonNode *json_root_node;
+ JsonGenerator *generator;
+ char *result;
+
+ if (namespaces == NULL) {
+ namespaces = tracker_namespace_manager_get_default ();
+ }
+
+ context.all_namespaces = namespaces;
+ context.our_namespaces = tracker_namespace_manager_new ();
+ context.builder = json_builder_new ();
+ context.done_list = NULL;
+
+ maybe_intern_prefix_of_compact_uri (context.all_namespaces, context.our_namespaces, tracker_resource_get_identifier (self));
+
+ json_builder_begin_object (context.builder);
+
+ tracker_resource_generate_jsonld (self, &context);
+
+ json_builder_set_member_name (context.builder, "@context");
+ json_builder_begin_object (context.builder);
+ tracker_namespace_manager_foreach (context.our_namespaces, generate_jsonld_namespace_mapping_foreach, &context);
+ json_builder_end_object (context.builder);
+
+ json_builder_end_object (context.builder);
+
+ json_root_node = json_builder_get_root (context.builder);
+
+ generator = json_generator_new ();
+ json_generator_set_root (generator, json_root_node);
+ json_generator_set_pretty (generator, TRUE);
+
+ result = json_generator_to_data (generator, NULL);
+
+ g_list_free (context.done_list);
+ json_node_free (json_root_node);
+ g_object_unref (context.builder);
+ g_object_unref (generator);
+
+ return result;
+}