diff options
Diffstat (limited to 'src/libtracker-sparql/tracker-resource.c')
-rw-r--r-- | src/libtracker-sparql/tracker-resource.c | 175 |
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; +} |