[tracker/wip/carlosg/writeback-refactor] libtracker-sparql: Add API to serialize and deserialize a TrackerResource
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker/wip/carlosg/writeback-refactor] libtracker-sparql: Add API to serialize and deserialize a TrackerResource
- Date: Thu, 21 May 2020 14:23:16 +0000 (UTC)
commit 0f8e12e6ec9ffe5166be8e476969052b816804aa
Author: Carlos Garnacho <carlosg gnome org>
Date: Thu May 21 14:30:43 2020 +0200
libtracker-sparql: Add API to serialize and deserialize a TrackerResource
There's contexts in which it may be useful to transfer and handle resources as
a GVariant. Add API to handle both its serialization to a GVariant, and its
deserialization back to a TrackerResource.
Add a test for it too.
.../libtracker-sparql-sections.txt | 3 +
src/libtracker-sparql/tracker-resource.c | 231 +++++++++++++++++++++
src/libtracker-sparql/tracker-resource.h | 6 +
tests/libtracker-sparql/tracker-resource-test.c | 64 ++++++
4 files changed, 304 insertions(+)
---
diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
index 38dfe9d51..ead836986 100644
--- a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
+++ b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
@@ -46,10 +46,13 @@ tracker_resource_get_first_string
tracker_resource_get_first_uri
tracker_resource_get_identifier
tracker_resource_get_values
+tracker_resource_get_properties
tracker_resource_identifier_compare_func
tracker_resource_print_sparql_update
tracker_resource_print_turtle
tracker_resource_print_jsonld
+tracker_resource_serialize
+tracker_resource_deserialize
TRACKER_PREFIX_DC
TRACKER_PREFIX_MFO
TRACKER_PREFIX_NAO
diff --git a/src/libtracker-sparql/tracker-resource.c b/src/libtracker-sparql/tracker-resource.c
index f7d75996f..9f83e440c 100644
--- a/src/libtracker-sparql/tracker-resource.c
+++ b/src/libtracker-sparql/tracker-resource.c
@@ -1772,3 +1772,234 @@ tracker_resource_print_jsonld (TrackerResource *self,
return result;
}
+
+static GVariant *
+tracker_serialize_single_value (TrackerResource *resource,
+ const GValue *value)
+{
+ if (G_VALUE_HOLDS_BOOLEAN (value)) {
+ return g_variant_new_boolean (g_value_get_boolean (value));
+ } else if (G_VALUE_HOLDS_INT (value)) {
+ return g_variant_new_int32 (g_value_get_int (value));
+ } else if (G_VALUE_HOLDS_INT64 (value)) {
+ return g_variant_new_int64 (g_value_get_int64 (value));
+ } else if (G_VALUE_HOLDS_DOUBLE (value)) {
+ return g_variant_new_double (g_value_get_double (value));
+ } else if (G_VALUE_HOLDS (value, TRACKER_TYPE_URI)) {
+ /* Use bytestring for URIs, so they can be distinguised
+ * from plain strings
+ */
+ return g_variant_new_bytestring (g_value_get_string (value));
+ } else if (G_VALUE_HOLDS_STRING (value)) {
+ return g_variant_new_string (g_value_get_string (value));
+ } else if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+ return tracker_resource_serialize (g_value_get_object (value));
+ }
+
+ g_warn_if_reached ();
+
+ return NULL;
+}
+
+/**
+ * tracker_resource_serialize:
+ * @resource: A #TrackerResource
+ *
+ * Serializes a #TrackerResource to a #GVariant in a lossless way.
+ * All child resources are subsequently serialized. It is implied
+ * that both ends use a common #TrackerNamespaceManager.
+ *
+ * Returns: (transfer full): A variant describing the resource,
+ * the reference is floating.
+ *
+ * Since: 3.0
+ **/
+GVariant *
+tracker_resource_serialize (TrackerResource *resource)
+{
+ TrackerResourcePrivate *priv = GET_PRIVATE (resource);
+ GVariantBuilder builder;
+ GHashTableIter iter;
+ GList *properties, *l;
+ const gchar *pred;
+ GValue *value;
+
+ g_return_val_if_fail (TRACKER_IS_RESOURCE (resource), NULL);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+
+ if (priv->identifier &&
+ strncmp (priv->identifier, "_:", 2) != 0) {
+ g_variant_builder_add (&builder, "{sv}", "@id",
+ g_variant_new_string (priv->identifier));
+ }
+
+ g_hash_table_iter_init (&iter, priv->properties);
+
+ /* Use a stable sort, so that GVariants are byte compatible */
+ properties = tracker_resource_get_properties (resource);
+ properties = g_list_sort (properties, g_strcmp0);
+
+ for (l = properties; l; l = l->next) {
+ pred = l->data;
+ value = g_hash_table_lookup (priv->properties, pred);
+
+ if (G_VALUE_HOLDS (value, G_TYPE_PTR_ARRAY)) {
+ GPtrArray *array = g_value_get_boxed (value);
+ GVariantBuilder array_builder;
+ guint i;
+
+ g_variant_builder_init (&array_builder, G_VARIANT_TYPE_ARRAY);
+
+ for (i = 0; i < array->len; i++) {
+ GValue *child = g_ptr_array_index (array, i);
+ GVariant *variant;
+
+ variant = tracker_serialize_single_value (resource, child);
+ if (!variant)
+ return NULL;
+
+ g_variant_builder_add_value (&array_builder, variant);
+ }
+
+ g_variant_builder_add (&builder, "{sv}", pred,
+ g_variant_builder_end (&array_builder));
+ } else {
+ GVariant *variant;
+
+ variant = tracker_serialize_single_value (resource, value);
+ if (!variant)
+ return NULL;
+
+ g_variant_builder_add (&builder, "{sv}", pred, variant);
+ }
+ }
+
+ g_list_free (properties);
+
+ return g_variant_builder_end (&builder);
+}
+
+/**
+ * tracker_resource_deserialize:
+ * @variant: a #GVariant
+ * @error: return location for errors
+ *
+ * Deserializes a #TrackerResource previously serialized with
+ * tracker_resource_serialize(). It is implied that both ends
+ * use a common #TrackerNamespaceManager.
+ *
+ * Returns: (transfer full): A TrackerResource, or %NULL if
+ * deserialization fails.
+ *
+ * Since: 3.0
+ **/
+TrackerResource *
+tracker_resource_deserialize (GVariant *variant)
+{
+ TrackerResource *resource;
+ GVariantIter iter;
+ GVariant *obj;
+ gchar *pred;
+
+ g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
+
+ resource = tracker_resource_new (NULL);
+
+ g_variant_iter_init (&iter, variant);
+
+ while (g_variant_iter_next (&iter, "{sv}", &pred, &obj)) {
+ /* Special case, "@id" for the resource identifier */
+ if (g_strcmp0 (pred, "@id") == 0 &&
+ g_variant_is_of_type (obj, G_VARIANT_TYPE_STRING)) {
+ tracker_resource_set_identifier (resource, g_variant_get_string (obj, NULL));
+ continue;
+ }
+
+ if (g_variant_is_of_type (obj, G_VARIANT_TYPE_STRING)) {
+ tracker_resource_set_string (resource, pred,
+ g_variant_get_string (obj, NULL));
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_BOOLEAN)) {
+ tracker_resource_set_boolean (resource, pred,
+ g_variant_get_boolean (obj));
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_INT16)) {
+ tracker_resource_set_int64 (resource, pred,
+ (gint64) g_variant_get_int16 (obj));
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_INT32)) {
+ tracker_resource_set_int64 (resource, pred,
+ (gint64) g_variant_get_int32 (obj));
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_INT64)) {
+ tracker_resource_set_int64 (resource, pred,
+ (gint64) g_variant_get_int64 (obj));
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_DOUBLE)) {
+ tracker_resource_set_double (resource, pred,
+ g_variant_get_double (obj));
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_BYTESTRING)) {
+ tracker_resource_set_uri (resource, pred,
+ g_variant_get_bytestring (obj));
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_VARDICT)) {
+ TrackerResource *child;
+
+ child = tracker_resource_deserialize (obj);
+ if (!child) {
+ g_object_unref (resource);
+ return NULL;
+ }
+
+ tracker_resource_set_relation (resource, pred, child);
+ } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_ARRAY)) {
+ GVariant *elem;
+ GVariantIter iter2;
+
+ g_variant_iter_init (&iter2, obj);
+
+ /* Other arrays are multi-valued */
+ while ((elem = g_variant_iter_next_value (&iter2)) != NULL) {
+ if (g_variant_is_of_type (elem, G_VARIANT_TYPE_STRING)) {
+ tracker_resource_add_string (resource, pred,
+ g_variant_get_string (elem, NULL));
+ } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_BOOLEAN)) {
+ tracker_resource_add_boolean (resource, pred,
+ g_variant_get_boolean (elem));
+ } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_INT16)) {
+ tracker_resource_add_int64 (resource, pred,
+ (gint64) g_variant_get_int16 (elem));
+ } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_INT32)) {
+ tracker_resource_add_int64 (resource, pred,
+ (gint64) g_variant_get_int32 (elem));
+ } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_INT64)) {
+ tracker_resource_add_int64 (resource, pred,
+ (gint64) g_variant_get_int16 (elem));
+ } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_DOUBLE)) {
+ tracker_resource_add_double (resource, pred,
+ g_variant_get_double (elem));
+ } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_BYTESTRING)) {
+ tracker_resource_add_uri (resource, pred,
+ g_variant_get_bytestring (elem));
+ } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_VARDICT)) {
+ TrackerResource *child;
+
+ child = tracker_resource_deserialize (elem);
+ if (!child) {
+ g_object_unref (resource);
+ return NULL;
+ }
+
+ tracker_resource_add_relation (resource, pred, child);
+ } else {
+ g_warning ("Unhandled GVariant signature '%s'",
+ g_variant_get_type_string (elem));
+ g_object_unref (resource);
+ return NULL;
+ }
+ }
+ } else {
+ g_warning ("Unhandled GVariant signature '%s'",
+ g_variant_get_type_string (obj));
+ g_object_unref (resource);
+ return NULL;
+ }
+ }
+
+ return resource;
+}
diff --git a/src/libtracker-sparql/tracker-resource.h b/src/libtracker-sparql/tracker-resource.h
index fcc36b3ed..171834449 100644
--- a/src/libtracker-sparql/tracker-resource.h
+++ b/src/libtracker-sparql/tracker-resource.h
@@ -108,6 +108,12 @@ char *tracker_resource_print_sparql_update (TrackerResource *self, TrackerNamesp
TRACKER_AVAILABLE_IN_ALL
char *tracker_resource_print_jsonld (TrackerResource *self, TrackerNamespaceManager *namespaces);
+TRACKER_AVAILABLE_IN_ALL
+GVariant * tracker_resource_serialize (TrackerResource *resource);
+
+TRACKER_AVAILABLE_IN_ALL
+TrackerResource * tracker_resource_deserialize (GVariant *variant);
+
G_END_DECLS
#endif /* __LIBTRACKER_RESOURCE_H__ */
diff --git a/tests/libtracker-sparql/tracker-resource-test.c b/tests/libtracker-sparql/tracker-resource-test.c
index 4b616b947..1325eb9bb 100644
--- a/tests/libtracker-sparql/tracker-resource-test.c
+++ b/tests/libtracker-sparql/tracker-resource-test.c
@@ -145,6 +145,68 @@ test_resource_get_set_pointer_validation (void)
g_test_trap_assert_stderr ("*tracker_resource_set_string: NULL is not a valid value.*");
}
+static void
+test_resource_serialization (void)
+{
+ TrackerResource *res, *child, *child2, *copy;
+ GVariant *variant, *variant2;
+
+ /* Create sample data */
+ res = tracker_resource_new (NULL);
+ tracker_resource_set_string (res, "nie:title", "foo");
+ tracker_resource_set_double (res, "nfo:duration", 25.4);
+ tracker_resource_set_boolean (res, "nfo:isBootable", TRUE);
+ tracker_resource_set_int64 (res, "nie:usageCounter", 4);
+ tracker_resource_add_uri (res, "rdf:type", "nfo:Audio");
+ tracker_resource_add_uri (res, "rdf:type", "nfo:Media");
+
+ child = tracker_resource_new ("uriuri");
+ tracker_resource_set_string (child, "nfo:fileName", "bar");
+ tracker_resource_set_take_relation (res, "nie:isStoredAs", child);
+
+ child2 = tracker_resource_new (NULL);
+ tracker_resource_set_string (child2, "nie:title", "baz");
+ tracker_resource_set_take_relation (child, "nfo:belongsToContainer", child2);
+
+ /* Ensure multiple serialize calls produce the same variant */
+ variant = tracker_resource_serialize (res);
+ g_assert_true (variant != NULL);
+ variant2 = tracker_resource_serialize (res);
+ g_assert_true (variant != NULL);
+ g_assert_true (g_variant_equal (variant, variant2));
+ g_variant_unref (variant2);
+
+ /* Deserialize to a copy and check its content */
+ copy = tracker_resource_deserialize (variant);
+ g_assert_true (copy != NULL);
+
+ g_assert_true (tracker_resource_get_first_boolean (copy, "nfo:isBootable"));
+ g_assert_true (strncmp (tracker_resource_get_identifier (copy), "_:", 2) == 0);
+ g_assert_cmpstr (tracker_resource_get_first_string (copy, "nie:title"), ==, "foo");
+ g_assert_cmpint (tracker_resource_get_first_int64 (copy, "nie:usageCounter"), ==, 4);
+
+ child = tracker_resource_get_first_relation (copy, "nie:isStoredAs");
+ g_assert_true (child != NULL);
+ g_assert_cmpstr (tracker_resource_get_identifier (child), ==, "uriuri");
+ g_assert_cmpstr (tracker_resource_get_first_string (child, "nfo:fileName"), ==, "bar");
+
+ child2 = tracker_resource_get_first_relation (child, "nfo:belongsToContainer");
+ g_assert_true (child2 != NULL);
+ g_assert_true (strncmp (tracker_resource_get_identifier (child2), "_:", 2) == 0);
+ g_assert_cmpstr (tracker_resource_get_first_string (child2, "nie:title"), ==, "baz");
+
+ /* Also ensure the copy serializes the same */
+ variant2 = tracker_resource_serialize (copy);
+ g_assert_true (variant2 != NULL);
+
+ g_assert_true (g_variant_equal (variant, variant2));
+ g_variant_unref (variant);
+ g_variant_unref (variant2);
+
+ g_object_unref (res);
+ g_object_unref (copy);
+}
+
int
main (int argc,
char **argv)
@@ -163,6 +225,8 @@ main (int argc,
test_resource_get_set_many);
g_test_add_func ("/libtracker-sparql/tracker-resource/get_set_pointer_validation",
test_resource_get_set_pointer_validation);
+ g_test_add_func ("/libtracker-sparql/tracker-resource/serialization",
+ test_resource_serialization);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]