Repository: SINTEF/dlite (language: python)

Issue:
Add "$ref" and "relation" to DLiteProperty to support structured collections
Check the implementation in SOFT7


Discussion / hints:

Fix patch:
diff --git a/bindings/python/dlite-entity-python.i b/bindings/python/dlite-entity-python.i
index e68fb4773..6562ff3ee 100644
--- a/bindings/python/dlite-entity-python.i
+++ b/bindings/python/dlite-entity-python.i
@@ -196,7 +196,7 @@ def get_instance(id: "str", metaid: "str"=None, check_storages: "bool"=True) ->
 
   %newobject _c_ptr;
   PyObject *_c_ptr(void) {
-    return PyCapsule_New($self, NULL, NULL);
+    return PyCapsule_New($self, NULL, NULL);  // XXX - Add name and descructor
   }
 
   %pythoncode %{
diff --git a/bindings/python/dlite-python.i b/bindings/python/dlite-python.i
index 14a366b9c..2eea27545 100644
--- a/bindings/python/dlite-python.i
+++ b/bindings/python/dlite-python.i
@@ -13,6 +13,7 @@
 #include "strutils.h"
 #include "dlite.h"
 #include "dlite-python-singletons.h"
+#include "dlite-pyembed.h"
 
 #define DLITE_INSTANCE_CAPSULE_NAME ((char *)"dlite.Instance")
 #define DLITE_DATA_CAPSULE_NAME ((char *)"dlite.data")
@@ -138,6 +139,7 @@ int npy_type(DLiteType type, size_t size)
 
        We therefore fall back to NPY_STRING, which is simple ASCII. */
     return NPY_STRING;
+  case dliteRef:
   case dliteStringPtr:
   case dliteDimension:
   case dliteProperty:
@@ -172,10 +174,10 @@ PyArray_Descr *npy_dtype(DLiteType type, size_t size)
     dtype->elsize = (int)size;
     break;
   case dliteStringPtr:
+  case dliteRef:
   case dliteDimension:
   case dliteProperty:
   case dliteRelation:
-    //assert(dtype->elsize == 0);
     assert(dtype->elsize == 0 || sizeof(void *));
     break;
   }
@@ -272,6 +274,7 @@ obj_t *dlite_swig_get_array(DLiteInstance *inst, int ndims, int *dims,
   switch (type) {
 
   case dliteStringPtr:
+  case dliteRef:
   case dliteDimension:
   case dliteProperty:
   case dliteRelation:
@@ -443,6 +446,7 @@ int dlite_swig_set_array(void *ptr, int ndims, int *dims,
     }
     break;
 
+  case dliteRef:
   case dliteDimension:
   case dliteProperty:
   case dliteRelation:
@@ -507,6 +511,10 @@ void *dlite_swig_copy_array(int ndims, int *dims, DLiteType type,
               size);
     break;
   case dliteStringPtr:
+  case dliteRef:
+  case dliteDimension:
+  case dliteProperty:
+  case dliteRelation:
     if (dlite_swig_set_array(&ptr, ndims, dims, type, size, (obj_t *)arr))
       goto fail;
     break;
@@ -615,10 +623,11 @@ obj_t *dlite_swig_get_scalar(DLiteType type, size_t size, void *data)
     }
     break;
 
-  case dliteRelation:
-    if (!(obj = SWIG_NewPointerObj(SWIG_as_voidptr(data),
-                                   SWIGTYPE_p__Triple, 0)))
-      FAIL("cannot create relation");
+  case dliteRef:
+    {
+      DLiteInstance *inst = *(DLiteInstance **)data;
+      if (!(obj = dlite_pyembed_from_instance(inst->uuid))) goto fail;
+    }
     break;
 
   case dliteDimension:
@@ -633,6 +642,12 @@ obj_t *dlite_swig_get_scalar(DLiteType type, size_t size, void *data)
       FAIL("cannot create property");
     break;
 
+  case dliteRelation:
+    if (!(obj = SWIG_NewPointerObj(SWIG_as_voidptr(data),
+                                   SWIGTYPE_p__Triple, 0)))
+      FAIL("cannot create relation");
+    break;
+
   default:
     FAIL1("converting type \"%s\" to scalar is not yet implemented",
          dlite_type_get_dtypename(type));
@@ -665,25 +680,6 @@ int dlite_swig_set_scalar(void *ptr, DLiteType type, size_t size, obj_t *obj)
       if (n != (int)size)
         FAIL2("cannot read Python blob of size %d into buffer of size %d",
               n, (int)size);
-      // xxx
-      /*
-      size_t n;
-      PyObject *bytes = PyObject_Bytes(obj);
-      if (!bytes) {
-        //xxx
-        FAIL("cannot convert object to bytes");
-      }
-      assert(PyBytes_Check(bytes));
-      n = PyBytes_Size(bytes);
-      if (n > size) {
-        Py_DECREF(bytes);
-        FAIL2("Length of bytearray is %lu. Exceeds size of blob: %lu",
-              (unsigned long)n, (unsigned long)size);
-      }
-      memset(ptr, 0, size);
-      memcpy(ptr, PyBytes_AsString(bytes), n);
-      Py_DECREF(bytes);
-      */
     }
     break;
 
@@ -797,6 +793,14 @@ int dlite_swig_set_scalar(void *ptr, DLiteType type, size_t size, obj_t *obj)
     }
     break;
 
+  case dliteRef:
+    {
+      DLiteInstance **q = ptr;
+      if (*q) dlite_instance_decref(*q);
+      *q = dlite_pyembed_get_instance(obj);
+    }
+    break;
+
   case dliteDimension:
     {
       DLiteDimension *dest = ptr;
diff --git a/src/dlite-codegen.c b/src/dlite-codegen.c
index 1d69d0f08..7646c591e 100644
--- a/src/dlite-codegen.c
+++ b/src/dlite-codegen.c
@@ -202,6 +202,7 @@ static int list_properties_helper(TGenBuf *s, const char *template, int len,
     DLiteProperty *p = m->_properties + i;
     const char *type = dlite_type_get_dtypename(p->type);
     const char *dtype = dlite_type_get_enum_name(p->type);
+    char *ref = (p->ref) ? p->ref : "NULL";
     char *unit = (p->unit) ? p->unit : "";
     char *descr = (p->description) ? p->description : "";
     size_t nref = (p->ndims > 0) ? 1 : 0;
@@ -222,6 +223,7 @@ static int list_properties_helper(TGenBuf *s, const char *template, int len,
     tgen_subs_set(&psubs, "prop.ftype",    ftype,    NULL);
     tgen_subs_set(&psubs, "prop.isoctype", isoctype, NULL);
     tgen_subs_set(&psubs, "prop.cdecl",    pcdecl,   NULL);
+    tgen_subs_set(&psubs, "prop.ref",      ref,      NULL);
     tgen_subs_set(&psubs, "prop.unit",     unit,     NULL);
     tgen_subs_set(&psubs, "prop.descr",    descr,    NULL);
     tgen_subs_set(&psubs, "prop.dims",     NULL,     list_dims);
diff --git a/src/dlite-entity.c b/src/dlite-entity.c
index 5ab28901a..f71c40f69 100644
--- a/src/dlite-entity.c
+++ b/src/dlite-entity.c
@@ -769,7 +769,38 @@ DLiteInstance *dlite_instance_load(const DLiteStorage *s, const char *id)
 }
 
 /*
-  A convinient function that loads an instance given an URL of the form
+  A convenient function for loading an instance with given id from a
+  storage specified with the `driver`, `location` and `options`
+  arguments (see dlite_storage_open()).
+
+  The `id` argument may be NULL if the storage contains only one instance.
+
+  Returns the instance or NULL on error.
+ */
+DLiteInstance *dlite_instance_load_loc(const char *driver, const char *location,
+                                       const char *options, const char *id)
+{
+  DLiteStorage *s=NULL;
+  DLiteInstance *inst=NULL;
+ ErrTry:
+  if (id && *id) inst = _instance_store_get(id);
+ ErrOther:
+  err_clear();
+ ErrEnd;
+
+ if (inst) {
+   dlite_instance_incref(inst);
+  } else {
+    if (!(s = dlite_storage_open(driver, location, options))) goto fail;
+    if (!(inst = dlite_instance_load(s, id))) goto fail;
+  }
+ fail:
+  if (s) dlite_storage_close(s);
+  return inst;
+}
+
+/*
+  A convenient function that loads an instance given an URL of the form
 
       driver://location?options#id
 
@@ -782,12 +813,15 @@ DLiteInstance *dlite_instance_load(const DLiteStorage *s, const char *id)
 DLiteInstance *dlite_instance_load_url(const char *url)
 {
   char *str=NULL, *driver=NULL, *location=NULL, *options=NULL, *id=NULL;
-  DLiteStorage *s=NULL;
   DLiteInstance *inst=NULL;
   assert(url);
   if (!(str = strdup(url))) FAIL("allocation failure");
   if (dlite_split_url(str, &driver, &location, &options, &id)) goto fail;
-
+  inst = dlite_instance_load_loc(driver, location, options, id);
+ fail:
+  free(str);
+  return inst;
+  /*
  ErrTry:
   if (id && *id) inst = _instance_store_get(id);
  ErrOther:
@@ -804,6 +838,7 @@ DLiteInstance *dlite_instance_load_url(const char *url)
   if (s) dlite_storage_close(s);
   if (str) free(str);
   return inst;
+  */
 }
 
 /*
@@ -992,6 +1027,25 @@ int dlite_instance_save(DLiteStorage *s, const DLiteInstance *inst)
   return retval;
 }
 
+/*
+  A convinient function that saves instance `inst` to the storage specified
+  by `driver`, `location` and `options`.
+
+  Returns non-zero on error.
+ */
+int dlite_instance_save_loc(const char *driver, const char *location,
+                            const char *options, const DLiteInstance *inst)
+{
+  int retval=1;
+  DLiteStorage *s=NULL;
+
+  if (!(s = dlite_storage_open(driver, location, options))) goto fail;
+  retval = dlite_instance_save(s, inst);
+ fail:
+  if (s) dlite_storage_close(s);
+  return retval;
+}
+
 /*
   A convinient function that saves instance `inst` to the storage specified
   by `url`, which should be of the form
@@ -1004,14 +1058,10 @@ int dlite_instance_save_url(const char *url, const DLiteInstance *inst)
 {
   int retval=1;
   char *str=NULL, *driver=NULL, *loc=NULL, *options=NULL;
-  DLiteStorage *s=NULL;
-
   if (!(str = strdup(url))) FAIL("allocation failure");
   if (dlite_split_url(str, &driver, &loc, &options, NULL)) goto fail;
-  if (!(s = dlite_storage_open(driver, loc, options))) goto fail;
-  retval = dlite_instance_save(s, inst);
+  retval = dlite_instance_save_loc(driver, loc, options, inst);
  fail:
-  if (s) dlite_storage_close(s);
   if (str) free(str);
   return retval;
 }
@@ -1139,12 +1189,36 @@ int dlite_instance_set_property_by_index(DLiteInstance *inst, size_t i,
   const DLiteMeta *meta = inst->meta;
   DLiteProperty *p = meta->_properties + i;
   void *dest;
+  size_t nmemb=1;
+  int j;
 
+  /* Count members */
+  if (p->ndims)
+    for (j=0; j<p->ndims; j++) nmemb *= DLITE_PROP_DIM(inst, i, j);
+
+  /* Consistency check for dliteRef */
+  if (p->type == dliteRef && p->ref) {
+    if (p->ndims) {
+      size_t n;
+      DLiteInstance **q = (DLiteInstance **)ptr;
+      for (n=0; n<nmemb; n++, q++) {
+        const char *ref = (*q)->meta->uri;
+        if (strcmp(ref, p->ref))
+          return err(-1, "Invalid reference. Expected %s, but got %s",
+                     p->ref, ref);
+      }
+    } else {
+      const char *ref = (*(DLiteInstance **)ptr)->meta->uri;
+      if (strcmp(ref, p->ref))
+        return err(-1, "Invalid reference. Expected %s, but got %s",
+                   p->ref, ref);
+    }
+  }
+
+  /* Copy */
   if (p->ndims > 0) {
-    int j;
-    size_t n, nmemb=1;
+    size_t n;
     dest = *((void **)DLITE_PROP(inst, i));
-    for (j=0; j<p->ndims; j++) nmemb *= DLITE_PROP_DIM(inst, i, j);
     if (dlite_type_is_allocated(p->type)) {
       for (n=0; n<nmemb; n++) {
         void *v = (char *)ptr + n*p->size;
@@ -2653,60 +2727,89 @@ int scanobj(const char *src, const jsmntok_t *item, const char *key,
   case dliteDimension:
     {
       DLiteDimension *dim = ptr;
-      if (item->type != JSMN_STRING)
-        return err(-1, "expected JSON string, got \"%.*s\"", len, val);
-
-      if (dim->name) free(dim->name);
-      if (dim->description) free(dim->description);
-      memset(dim, 0, sizeof(DLiteDimension));
-
-      dim->name = strndup(key, keylen);
-      dim->description = strndup(val, len);
+      if (item->type == JSMN_STRING) {
+        if (dim->name) free(dim->name);
+        if (dim->description) free(dim->description);
+        memset(dim, 0, sizeof(DLiteDimension));
+        dim->name = strndup(key, keylen);
+        dim->description = strndup(val, len);
+      } else if (item->type == JSMN_OBJECT) {
+        int i;
+        for (i=0; i < item->size; i++, dim++) {
+          const jsmntok_t *k = item + 2*i + 1;
+          const jsmntok_t *v = item + 2*i + 2;
+          if (dim->name) free(dim->name);
+          if (dim->description) free(dim->description);
+          memset(dim, 0, sizeof(DLiteDimension));
+          dim->name = strndup(src + k->start, k->end - k->start);
+          dim->description = strndup(src + v->start, v->end - v->start);
+        }
+      } else
+        return err(-1,
+                   "expected JSON string or object, got \"%.*s\"", len, val);
     }
     break;
 
   case dliteProperty:
     {
       DLiteProperty *prop = ptr;
-      const jsmntok_t *t, *d;
-      int i;
-
+      const jsmntok_t *v = item + 1;
+      int i, j;
       if (item->type != JSMN_OBJECT)
         return err(-1, "expected JSON object, got \"%.*s\"", len, val);
 
-      if (prop->name) free(prop->name);
-      if (prop->dims) free(prop->dims);
-      if (prop->unit) free(prop->unit);
-      if (prop->description) free(prop->description);
-      memset(prop, 0, sizeof(DLiteProperty));
-
-      prop->name = strndup(key, keylen);
-
-      if (!(t = jsmn_item(src, item, "type")))
-        return errx(-1, "missing property type: '%.*s'", len, val);
-      if (dlite_type_set_dtype_and_size(val, &prop->type, &prop->size))
-        return -1;
-
-      if ((t = jsmn_item(src, item, "dims"))) {
-        if (t->type != JSMN_ARRAY)
-          return errx(-1, "property \"%.*s\": dims should be an array",
-                      keylen, key);
-        prop->ndims = t->size;
-        prop->dims = calloc(prop->ndims, sizeof(char *));
-        for (i=0; i < prop->ndims; i++) {
-          if (!(d = jsmn_element(src, t, i)))
-            return err(-1, "error parsing dimensions \"%.*s\" of property "
-                       "\"%.*s\"", t->end - t->start, src + t->start,
-                       keylen, key);
-          prop->dims[i] = strndup(src + d->start, d->end - d->start);
+      for (i=0; i < item->size; i++, prop++) {
+        const jsmntok_t *t, *d;
+        if (prop->name) free(prop->name);
+        if (prop->ref)  free(prop->ref);
+        if (prop->dims) free(prop->dims);
+        if (prop->unit) free(prop->unit);
+        if (prop->description) free(prop->description);
+        memset(prop, 0, sizeof(DLiteProperty));
+
+        assert(v->type == JSMN_STRING);  // key must be a string
+        prop->name = strndup(src + v->start, v->end - v->start);
+        v++;
+
+        if (v->type != JSMN_OBJECT)
+          return err(-1, "expected JSON object, got \"%.*s\"",
+                     v->end - v->start, src + v->start);
+
+        if (!(t = jsmn_item(src, v, "type")))
+          return errx(-1, "missing property type: '%.*s'", len, val);
+        if (dlite_type_set_dtype_and_size(src + t->start,
+                                          &prop->type, &prop->size))
+          return -1;
+
+        if ((t = jsmn_item(src, v, "$ref")))
+          prop->ref = strndup(src + t->start, t->end - t->start);
+        else if (prop->type == dliteRef && (t = jsmn_item(src, v, "type")))
+          prop->ref = strndup(src + t->start, t->end - t->start);
+
+        if ((t = jsmn_item(src, v, "dims")) ||
+            (t = jsmn_item(src, v, "shape"))) {
+          if (t->type != JSMN_ARRAY)
+            return errx(-1, "property \"%.*s\": dims should be an array",
+                        keylen, key);
+          prop->ndims = t->size;
+          prop->dims = calloc(prop->ndims, sizeof(char *));
+          for (j=0; j < prop->ndims; j++) {
+            if (!(d = jsmn_element(src, t, j)))
+              return err(-1, "error parsing dimensions \"%.*s\" of property "
+                         "\"%.*s\"", t->end - t->start, src + t->start,
+                         keylen, key);
+            prop->dims[j] = strndup(src + d->start, d->end - d->start);
+          }
         }
-      }
 
-      if ((t = jsmn_item(src, item, "unit")))
-        prop->unit = strndup(src + t->start, t->end - t->start);
+        if ((t = jsmn_item(src, v, "unit")))
+          prop->unit = strndup(src + t->start, t->end - t->start);
+
+        if ((t = jsmn_item(src, v, "description")))
+          prop->description = strndup(src + t->start, t->end - t->start);
 
-      if ((t = jsmn_item(src, item, "description")))
-        prop->description = strndup(src + t->start, t->end - t->start);
+        v += jsmn_count(v) + 1;
+      }
     }
     break;
 
@@ -2789,8 +2892,7 @@ int dlite_property_jscan(const char *src, const jsmntok_t *item,
       if (scandim(0, src, &q, p, dims, flags, &t)) return -1;
       break;
     case JSMN_OBJECT:
-      if (scanobj(src, item, key, ptr, p)) return -1;
-      break;
+      return scanobj(src, item, key, ptr, p);
     default:
       return errx(-1,
                  "property \"%s\" has %d dimensions, but got a scalar %.*s",
diff --git a/src/dlite-entity.h b/src/dlite-entity.h
index 8930ab70d..5d6b40e29 100644
--- a/src/dlite-entity.h
+++ b/src/dlite-entity.h
@@ -267,15 +267,12 @@ struct _DLiteDimension {
 
 /**
   DLite property
-
-  E.g. if we have dimensions ["M", "N"] and dims is [1, 1, 0], it
-  means that the data described by this property has dimensions
-  ["N", "N", "M"].
 */
 struct _DLiteProperty {
   char *name;         /*!< Name of this property. */
   DLiteType type;     /*!< Type of the described data. */
   size_t size;        /*!< Size of one data element. */
+  char *ref;          /*!< Reference to metadata URI for type=dliteRef. */
   int ndims;          /*!< Number of dimension of the described
                            data.  Zero if scalar. */
   char **dims;        /*!< Array of dimension strings.  May be NULL. */
@@ -501,7 +498,7 @@ DLiteInstance *dlite_instance_has(const char *id, bool check_storages);
   storage plugin path (initiated from the DLITE_STORAGES environment
   variable).
 
-  It is an error message if the instance cannot be found.
+  It is an error if the instance cannot be found.
 */
 DLiteInstance *dlite_instance_get(const char *id);
 
@@ -525,6 +522,18 @@ DLiteInstance *dlite_instance_get_casted(const char *id, const char *metaid);
  */
 DLiteInstance *dlite_instance_load(const DLiteStorage *s, const char *id);
 
+/**
+  A convenient function for loading an instance with given id from a
+  storage specified with the `driver`, `location` and `options`
+  arguments (see dlite_storage_open()).
+
+  The `id` argument may be NULL if the storage contains only one instance.
+
+  Returns the instance or NULL on error.
+ */
+DLiteInstance *dlite_instance_load_loc(const char *driver, const char *location,
+                                       const char *options, const char *id);
+
 /**
   A convinient function that loads an instance given an URL of the form
 
@@ -561,6 +570,14 @@ DLiteInstance *dlite_instance_load_casted(const DLiteStorage *s,
  */
 int dlite_instance_save(DLiteStorage *s, const DLiteInstance *inst);
 
+/**
+  A convinient function that saves instance `inst` to the storage specified
+  by `driver`, `location` and `options`.
+
+  Returns non-zero on error.
+ */
+int dlite_instance_save_loc(const char *driver, const char *location,
+                            const char *options, const DLiteInstance *inst);
 
 /**
   A convinient function that saves instance `inst` to the storage specified
diff --git a/src/dlite-json.c b/src/dlite-json.c
index fdc39f2e3..e93059577 100644
--- a/src/dlite-json.c
+++ b/src/dlite-json.c
@@ -139,6 +139,8 @@ int dlite_json_sprint(char *dest, size_t size, const DLiteInstance *inst,
       PRINT1("%s    {\n", in);
       PRINT2("%s      \"name\": \"%s\",\n", in, p->name);
       PRINT2("%s      \"type\": \"%s\"", in, typename);
+      if (p->ref)
+        PRINT2(",\n%s      \"$ref\": \"%s\"", in, p->ref);
       if (p->ndims) {
         PRINT1(",\n%s      \"dims\": [", in);
         for (j=0; j < p->ndims; j++) {
@@ -188,8 +190,10 @@ int dlite_json_sprint(char *dest, size_t size, const DLiteInstance *inst,
       dlite_type_set_typename(p->type, p->size, typename, sizeof(typename));
       PRINT2("%s    \"%s\": {\n", in, p->name);
       PRINT2("%s      \"type\": \"%s\"", in, typename);
+      if (p->ref)
+        PRINT2(",\n%s      \"$ref\": \"%s\"", in, p->ref);
       if (p->ndims) {
-        PRINT1(",\n%s      \"dims\": [", in);
+        PRINT1(",\n%s      \"shape\": [", in);
         for (j=0; j < p->ndims; j++) {
           char *cc = (j < p->ndims - 1) ? ", " : "";
           PRINT2("\"%s\"%s", p->dims[j], cc);
@@ -499,53 +503,53 @@ static DLiteInstance *parse_instance(const char *src, jsmntok_t *obj,
     FAIL("allocation failure");
 
   /* Parse dimensions */
-  if (meta->_ndimensions > 0) {
-    if (!(item = jsmn_item(src, obj, "dimensions")))
-      FAIL1("no \"dimensions\" in object %s", id);
-    dimtype = item->type;
-
-    if (item->type == JSMN_OBJECT) {
-      if (item->size != (int)meta->_ndimensions)
-        FAIL3("expected %d dimensions, got %d in instance %s",
-              (int)meta->_ndimensions, item->size, id);
-      for (i=0; i < meta->_ndimensions; i++) {
-        DLiteDimension *d = meta->_dimensions + i;
-        if (!(t = jsmn_item(src, item, d->name)))
-          FAIL2("missing dimension \"%s\" in %s", d->name, id);
-        if (dlite_meta_is_metameta(meta)) {
-          const jsmntok_t *tt;
-          if (t->type != JSMN_STRING)
-            FAIL1("expected dimension description, got: %.6s...",
-                  src + t->start);
-          if (d->name[0] != 'n')
-            FAIL1("meta-metadata dimension names should start with \"n\": %s",
-                  d->name);
-          if (!(tt = jsmn_item(src, obj, d->name+1)))
-            FAIL1("no metadata array named %s", d->name+1);
-          dims[i] = tt->size;
-        } else {
-          if (t->type == JSMN_PRIMITIVE)
-            dims[i] = atoi(src + t->start);
-          else
-            FAIL3("value '%.*s' of dimension should be an integer: %s",
-                  t->end-t->start, src+t->start, id);
+  if (dlite_meta_is_metameta(meta)) {
+    /* For metadata, dimension sizes are inferred from the size of
+       "dimensions", "propertis" and "relations". */
+    size_t n=0;
+    if ((t = jsmn_item(src, obj, "dimensions"))) dims[n++] = t->size;
+    if ((t = jsmn_item(src, obj, "properties"))) dims[n++] = t->size;
+    if ((t = jsmn_item(src, obj, "relations")))  dims[n++] = t->size;
+    if (n != meta->_ndimensions)
+      FAIL3("expected %d dimensions, got %d: %s",
+            (int)meta->_ndimensions, (int)n, id);
+
+  } else {
+    if (meta->_ndimensions > 0) {
+      if (!(item = jsmn_item(src, obj, "dimensions")))
+        FAIL1("no \"dimensions\" in object %s", id);
+      dimtype = item->type;
+
+      if (item->type == JSMN_OBJECT) {
+        if (item->size != (int)meta->_ndimensions)
+          FAIL3("expected %d dimensions, got %d in instance %s",
+                (int)meta->_ndimensions, item->size, id);
+        for (i=0; i < meta->_ndimensions; i++) {
+          DLiteDimension *d = meta->_dimensions + i;
+          if (!(t = jsmn_item(src, item, d->name)))
+            FAIL2("missing dimension \"%s\" in %s", d->name, id);
+          if (dlite_meta_is_metameta(meta)) {
+            const jsmntok_t *tt;
+            if (t->type != JSMN_STRING)
+              FAIL1("expected dimension description, got: %.6s...",
+                    src + t->start);
+            if (d->name[0] != 'n')
+              FAIL1("meta-metadata dimension names should start with \"n\": %s",
+                    d->name);
+            if (!(tt = jsmn_item(src, obj, d->name+1)))
+              FAIL1("no metadata array named %s", d->name+1);
+            dims[i] = tt->size;
+          } else {
+            if (t->type == JSMN_PRIMITIVE)
+              dims[i] = atoi(src + t->start);
+            else
+              FAIL3("value '%.*s' of dimension should be an integer: %s",
+                    t->end-t->start, src+t->start, id);
+          }
         }
+      } else {
+        FAIL1("\"dimensions\" of data instances must be a json object: %s", id);
       }
-    } else if (item->type == JSMN_ARRAY) {
-      size_t n=0;
-      if (!dlite_meta_is_metameta(meta))
-        FAIL1("only metadata can have array dimensions: %s", id);
-      if (meta->_ndimensions > n)
-        dims[n++] = item->size;
-      if (meta->_ndimensions > n && (t = jsmn_item(src, obj, "properties")))
-        dims[n++] = t->size;
-      if (meta->_ndimensions > n && (t = jsmn_item(src, obj, "relations")))
-        dims[n++] = t->size;
-      if (n != meta->_ndimensions)
-        FAIL3("expected %d dimensions, got %d: %s",
-              (int)meta->_ndimensions, (int)n, id);
-    } else {
-      FAIL1("\"dimensions\" must be object or array: %s", id);
     }
   }
 
@@ -561,12 +565,13 @@ static DLiteInstance *parse_instance(const char *src, jsmntok_t *obj,
       FAIL1("\"properties\" must have same type as \"dimensions\": %s", id);
 
     /* -- `base` is the base object to read properties from */
-    if (item->type == JSMN_OBJECT)
-      base = item;
-    else if (item->type == JSMN_ARRAY)
-      base = obj;
-    else
-      FAIL1("\"properties\" must be object or array: %s", id);
+    base = (dlite_instance_is_data(inst)) ? item : obj;
+    //if (item->type == JSMN_OBJECT)
+    //  base = item;
+    //else if (item->type == JSMN_ARRAY)
+    //  base = obj;
+    //else
+    //  FAIL1("\"properties\" must be object or array: %s", id);
     assert(base->type == JSMN_OBJECT);
 
     /* -- infer name, version and namespace */
@@ -597,11 +602,11 @@ static DLiteInstance *parse_instance(const char *src, jsmntok_t *obj,
           if (dlite_property_jscan(src, t, NULL, ptr, p, pdims, 0) < 0)
             goto fail;
         } else if (t->type == JSMN_OBJECT) {
-          const jsmntok_t *v;
-          if (!(v = jsmn_item(src, t, p->name)))
+          if (dlite_instance_is_meta(inst)) {
+            if (dlite_property_jscan(src, t, p->name, ptr, p, pdims, 0) < 0)
+              goto fail;
+          } else
             FAIL1("missing key \"%s\" in JSON object", p->name);
-          if (dlite_property_jscan(src, v, p->name, ptr, p, pdims, 0) < 0)
-            goto fail;
         } else {
           if (dlite_type_scan(src+t->start, t->end - t->start, ptr, p->type,
                               p->size, 0) < 0)
@@ -615,17 +620,15 @@ static DLiteInstance *parse_instance(const char *src, jsmntok_t *obj,
           if (dlite_property_scan(version, ptr, p, pdims, 0) < 0) goto fail;
         } else if (strcmp(p->name, "namespace") == 0) {
           if (dlite_property_scan(namespace, ptr, p, pdims, 0) < 0) goto fail;
-        } else if (strcmp(p->name, "description") == 0) {
-          // pass
         } else
-          FAIL2("missing property \"%s\" in %s", p->name, id);
+          warnx("missing property \"%s\" in %s", p->name, id);
       } else
         FAIL2("missing property \"%s\" in %s", p->name, id);
+
       if (meta->_loadprop) meta->_loadprop(inst, i);
     }
   }
   if (dlite_instance_is_meta(inst)) dlite_meta_init((DLiteMeta *)inst);
-
   ok = 1;
  fail:
   if (name) free(name);
diff --git a/src/dlite-schemas.c b/src/dlite-schemas.c
index 92bde93db..2db2517c7 100644
--- a/src/dlite-schemas.c
+++ b/src/dlite-schemas.c
@@ -23,6 +23,7 @@ static DLiteProperty basic_metadata_schema_properties[] = {
    "name",                                    /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -32,6 +33,7 @@ static DLiteProperty basic_metadata_schema_properties[] = {
    "version",                                 /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -41,6 +43,7 @@ static DLiteProperty basic_metadata_schema_properties[] = {
    "namespace",                               /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -50,6 +53,7 @@ static DLiteProperty basic_metadata_schema_properties[] = {
    "description",                             /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -59,6 +63,7 @@ static DLiteProperty basic_metadata_schema_properties[] = {
    "dimensions",                              /* name */
    dliteDimension,                            /* type */
    sizeof(DLiteDimension),                    /* size */
+   NULL,                                      /* ref */
    1,                                         /* ndims */
    basic_metadata_schema_prop_dimensions_dims,/* dims */
    NULL,                                      /* unit */
@@ -68,6 +73,7 @@ static DLiteProperty basic_metadata_schema_properties[] = {
    "properties",                              /* name */
    dliteProperty,                             /* type */
    sizeof(DLiteProperty),                     /* size */
+   NULL,                                      /* ref */
    1,                                         /* ndims */
    basic_metadata_schema_prop_properties_dims,/* dims */
    NULL,                                      /* unit */
@@ -77,6 +83,7 @@ static DLiteProperty basic_metadata_schema_properties[] = {
    "relations",                               /* name */
    dliteRelation,                             /* type */
    sizeof(DLiteRelation),                     /* size */
+   NULL,                                      /* ref */
    1,                                         /* ndims */
    basic_metadata_schema_prop_relations_dims, /* dims */
    NULL,                                      /* unit */
@@ -181,6 +188,7 @@ static DLiteProperty entity_schema_properties[] = {
    "name",                                    /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -190,6 +198,7 @@ static DLiteProperty entity_schema_properties[] = {
    "version",                                 /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -199,6 +208,7 @@ static DLiteProperty entity_schema_properties[] = {
    "namespace",                               /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -208,6 +218,7 @@ static DLiteProperty entity_schema_properties[] = {
    "description",                             /* name */
    dliteStringPtr,                            /* type */
    sizeof(char *),                            /* size */
+   NULL,                                      /* ref */
    0,                                         /* ndims */
    NULL,                                      /* dims */
    NULL,                                      /* unit */
@@ -217,6 +228,7 @@ static DLiteProperty entity_schema_properties[] = {
    "dimensions",                              /* name */
    dliteDimension,                            /* type */
    sizeof(DLiteDimension),                    /* size */
+   NULL,                                      /* ref */
    1,                                         /* ndims */
    entity_schema_prop_dimensions_dims,        /* dims */
    NULL,                                      /* unit */
@@ -226,6 +238,7 @@ static DLiteProperty entity_schema_properties[] = {
    "properties",                              /* name */
    dliteProperty,                             /* type */
    sizeof(DLiteProperty),                     /* size */
+   NULL,                                      /* ref */
    1,                                         /* ndims */
    entity_schema_prop_properties_dims,        /* dims */
    NULL,                                      /* unit */
@@ -321,13 +334,14 @@ static DLiteDimension collection_entity_dimensions[] = {
 static char *collection_entity_prop_relations_dims[] = {"nrelations"};
 static DLiteProperty collection_entity_properties[] = {
   {
-  "relations",                               /* name */
-  dliteRelation,                             /* type */
-  sizeof(DLiteRelation),                     /* size */
-  1,                                         /* ndims */
-  collection_entity_prop_relations_dims,     /* dims */
-  NULL,                                      /* unit */
-  "Array of relations (s-p-o triples)."      /* description */
+    "relations",                               /* name */
+    dliteRelation,                             /* type */
+    sizeof(DLiteRelation),                     /* size */
+    NULL,                                      /* ref */
+    1,                                         /* ndims */
+    collection_entity_prop_relations_dims,     /* dims */
+    NULL,                                      /* unit */
+    "Array of relations (s-p-o triples)."      /* description */
   }
 };
 static struct _CollectionEntity {
diff --git a/src/dlite-type.c b/src/dlite-type.c
index 012ebfd6a..743f428f5 100644
--- a/src/dlite-type.c
+++ b/src/dlite-type.c
@@ -16,6 +16,7 @@
 #include "utils/strtob.h"
 #include "utils/strutils.h"
 #include "utils/jsmnx.h"
+#include "utils/uuid.h"
 
 #include "dlite-entity.h"
 #include "dlite-macros.h"
@@ -31,7 +32,7 @@ static char *dtype_names[] = {
   "float",
   "fixstring",
   "string",
-
+  "ref",
   "dimension",
   "property",
   "relation",
@@ -46,7 +47,7 @@ static char *dtype_enum_names[] = {
   "dliteFloat",
   "dliteFixString",
   "dliteStringPtr",
-
+  "dliteRef",
   "dliteDimension",
   "dliteProperty",
   "dliteRelation",
@@ -86,7 +87,7 @@ static struct _TypeDescr {
   {"float128", dliteFloat,     16,                    alignof(float128_t)},
 #endif
   {"string",   dliteStringPtr, sizeof(char *),        alignof(char *)},
-
+  {"ref",      dliteRef,       sizeof(DLiteInstance*),alignof(DLiteInstance*)},
   {"dimension",dliteDimension, sizeof(DLiteDimension),alignof(DLiteDimension)},
   {"property", dliteProperty,  sizeof(DLiteProperty), alignof(DLiteProperty)},
   {"relation", dliteRelation,  sizeof(DLiteRelation), alignof(DLiteRelation)},
@@ -95,6 +96,20 @@ static struct _TypeDescr {
 };
 
 
+/*
+  Help function that return non-zero if `dtypename` is "ref" or
+  corresponds to an valid metadata URL or UUID.
+*/
+static int is_metaref(const char *dtypename)
+{
+  if ((strncmp(dtypename, "http://", 7) == 0 ||
+       strncmp(dtypename, "https://", 8) == 0 ||
+       strncmp(dtypename, "ftp://", 6) == 0) &&
+      dlite_split_meta_uri(dtypename, NULL, NULL, NULL) == 0) return 1;
+  if (uuid_from_string(NULL, dtypename, 0) == 0) return 1;
+  return 0;
+}
+
 
 /*
   Returns descriptive name for `dtype` or NULL on error.
@@ -124,6 +139,10 @@ DLiteType dlite_type_get_dtype(const char *dtypename)
   int i, N=sizeof(dtype_names) / sizeof(char *);
   for (i=0; i<N; i++)
     if (strcmp(dtypename, dtype_names[i]) == 0) return i;
+
+  /* dliteRef is a special case... */
+  if (is_metaref(dtypename)) return dliteRef;
+
   return -1;
 }
 
@@ -162,6 +181,12 @@ int dlite_type_set_typename(DLiteType dtype, size_t size,
                   (unsigned long)sizeof(char *), (unsigned long)size);
     snprintf(typename, n, "string");
     break;
+  case dliteRef:
+    if (size != sizeof(DLiteInstance *))
+      return errx(1, "string should have size %lu, but %lu was provided",
+                  (unsigned long)sizeof(DLiteInstance *), (unsigned long)size);
+    snprintf(typename, n, "ref");
+    break;
   case dliteDimension:
     snprintf(typename, n, "dimension");
     break;
@@ -209,6 +234,9 @@ int dlite_type_set_ftype(DLiteType dtype, size_t size,
   case dliteStringPtr:
     snprintf(ftype, n, "character(*)");
     break;
+  case dliteRef:
+    snprintf(ftype, n, "type(DLiteInstance)");
+    break;
   case dliteDimension:
     snprintf(ftype, n, "type(DLiteDimension)");
     break;
@@ -306,6 +334,9 @@ int dlite_type_set_isoctype(DLiteType dtype, size_t size,
   case dliteStringPtr:
     snprintf(isoctype, n, "character(kind=c_char)");
     break;
+  case dliteRef:
+    snprintf(isoctype, n, "type(c_ptr)");
+    break;
   case dliteDimension:
     snprintf(isoctype, n, "type(c_ptr)");
     break;
@@ -388,6 +419,12 @@ int dlite_type_set_cdecl(DLiteType dtype, size_t size, const char *name,
                   (unsigned long)sizeof(char *), (unsigned long)size);
     m = snprintf(pcdecl, n, "char *%s%s", ref, name);
     break;
+  case dliteRef:
+    if (size != sizeof(DLiteInstance *))
+      return errx(-1, "DLiteRef should have size %lu, but %lu was provided",
+                  (unsigned long)sizeof(DLiteInstance *), (unsigned long)size);
+    m = snprintf(pcdecl, n, "DLiteInstance *%s%s", ref, name);
+    break;
   case dliteDimension:
     if (size != sizeof(DLiteDimension))
       return errx(-1, "DLiteDimension must have size %lu, got %lu",
@@ -438,6 +475,13 @@ int dlite_type_set_dtype_and_size(const char *typename,
   size_t len=0, namelen, typesize;
   char *endptr;
 
+  /* Handle dliteRef especially... */
+  if (is_metaref(typename)) {
+    *dtype = dliteRef;
+    *size = sizeof(DLiteInstance *);
+    return 0;
+  }
+
   while (isalpha(typename[len])) len++;
   namelen = len;
   while (isdigit(typename[len])) len++;
@@ -456,8 +500,13 @@ int dlite_type_set_dtype_and_size(const char *typename,
   }
 
   /* Type is not in the type table - it must have a explicit size */
-  if (len == namelen)
-    return errx(1, "explicit length is expected for type name: %s", typename);
+  if (len == namelen) {
+    if (strncmp(typename, "blob", namelen) == 0)
+      return errx(1, "explicit length is expected for type name: %s",
+                  typename);
+    else
+      return errx(1, "unknown type: %s", typename);
+  }
 
   /* extract size from `typename` */
   typesize = strtol(typename + namelen, &endptr, 10);
@@ -486,6 +535,7 @@ int dlite_type_is_allocated(DLiteType dtype)
   case dliteUInt:
   case dliteFloat:
   case dliteFixString:
+  case dliteRef:
     return 0;
   case dliteStringPtr:
   case dliteDimension:
@@ -511,6 +561,7 @@ void *dlite_type_copy(void *dest, const void *src, DLiteType dtype, size_t size)
   case dliteUInt:
   case dliteFloat:
   case dliteFixString:
+  case dliteRef:
     memcpy(dest, src, size);
     break;
   case dliteStringPtr:
@@ -590,6 +641,9 @@ void *dlite_type_clear(void *p, DLiteType dtype, size_t size)
   case dliteStringPtr:
     free(*((char **)p));
     break;
+  case dliteRef:
+    if (*(DLiteInstance **)p) dlite_instance_decref(*(DLiteInstance **)p);
+    break;
   case dliteDimension:
     free(((DLiteDimension *)p)->name);
     free(((DLiteDimension *)p)->description);
@@ -621,8 +675,9 @@ void *dlite_type_clear(void *p, DLiteType dtype, size_t size)
 /*
   Return a StrquoteFlags corresponding to `flags`.
  */
-static StrquoteFlags as_qflags(DLiteTypeFlag flags)
+static StrquoteFlags as_qflags(DLiteType dtype, DLiteTypeFlag flags)
 {
+  UNUSED(dtype);
   int flg=0;
   if (flags == dliteFlagDefault) return strquoteRaw;
   if (flags & dliteFlagRaw)    flg |= strquoteRaw;
@@ -655,7 +710,7 @@ int dlite_type_print(char *dest, size_t n, const void *p, DLiteType dtype,
 {
   int m=0, w=width, r=prec;
   size_t i;
-  StrquoteFlags qflags = as_qflags(flags);
+  StrquoteFlags qflags = as_qflags(dtype, flags);
   switch (dtype) {
 
   case dliteBlob:
@@ -752,6 +807,17 @@ int dlite_type_print(char *dest, size_t n, const void *p, DLiteType dtype,
     }
     break;
 
+  case dliteRef:
+    {
+      DLiteInstance *inst = *(DLiteInstance **)p;
+      if (inst) {
+        const char *id = (inst->uri) ? inst->uri : inst->uuid;
+        m = strnquote(dest, n, id, -1, qflags);
+      } else
+        m = snprintf(dest, n, "%*.*s", w, r, "null");
+    }
+    break;
+
   case dliteDimension:
     m = snprintf(dest, n, "{\"name\": \"%s\", \"description\": \"%s\"}",
                  ((DLiteDimension *)p)->name,
@@ -840,7 +906,7 @@ int dlite_type_aprint(char **dest, size_t *n, size_t pos, const void *p,
 
 /* Maximum number of jsmn tokens in a dimension, property and relation */
 #define MAX_DIMENSION_TOKENS  5
-#define MAX_PROPERTY_TOKENS  64  // this supports up to 53 dimensions...
+#define MAX_PROPERTY_TOKENS  64  // this supports at least 52 dimensions...
 #define MAX_RELATION_TOKENS   9
 
 /*
@@ -859,7 +925,7 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
   size_t i;
   int m=0, v;
   char *endptr;
-  StrquoteFlags qflags = as_qflags(flags);
+  StrquoteFlags qflags = as_qflags(dtype, flags);
   switch(dtype) {
 
   case dliteBlob:
@@ -987,6 +1053,31 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
     }
     break;
 
+  case dliteRef:
+    {
+      char *q=NULL;
+      int n, n2;
+      DLiteInstance *inst=NULL;
+      n = strspn(src, " \t\n");
+      if (strncmp(src+n, "null", 4) == 0) {
+        m += n+4;
+      } else {
+        switch((n = strnunquote(NULL, 0, src, len, &m, qflags))) {
+        case -1: return errx(-1, "expected initial double quote around ref");
+        case -2: return errx(-1, "expected final double quote around ref");
+        }
+        assert(n >= 0);
+        if (!(q = malloc(n+1))) return err(-1, "allocation failure");
+        n2 = strnunquote(q, n+1, src, m, NULL, qflags);
+        assert(n2 == n);
+        inst = dlite_instance_get(q);
+        free(q);
+        if (!inst) return -1;
+      }
+      *(DLiteInstance **)p = inst;
+    }
+    break;
+
   case dliteDimension:
     {
       DLiteDimension *dim = p;
@@ -1142,11 +1233,19 @@ int dlite_type_update_sha3(sha3_context *c, const void *ptr,
     }
     break;
 
+  case dliteRef:
+    {
+      DLiteInstance *inst = *((DLiteInstance **)ptr);
+      if (inst) sha3_Update(c, inst->uuid, DLITE_UUID_LENGTH);
+    }
+    break;
+
   case dliteDimension:
     {
       const DLiteDimension *d = ptr;
       sha3_Update(c, d->name, strlen(d->name));
-      //if (d->description) sha3_Update(c, d->description);
+      //if (d->description)
+      //  sha3_Update(c, p->description, strlen(d->description));
     }
     break;
 
diff --git a/src/dlite-type.h b/src/dlite-type.h
index f0c0ab839..9c9a3edd3 100644
--- a/src/dlite-type.h
+++ b/src/dlite-type.h
@@ -16,18 +16,19 @@
   The properties can have most of the basic types found in C, with
   some additions, as summarised in the table below:
 
-  type      | dtype          | sizes                  | pointer                       | description                      | examples names
-  ----      | -----          | -----                  | -------                       | -----------                      | --------------
-  blob      | dliteBlob      | any                    | uint8_t *                     | binary blob, sequence of bytes   | blob32, blob128
-  bool      | dliteBool      | sizeof(bool)           | bool *                        | boolean                          | bool
-  int       | dliteInt       | 1, 2, 4, {8}           | int8_t *, int16_t *, ...      | signed integer                   | (int), int8, int16, int32, {int64}
-  uint      | dliteUInt      | 1, 2, 4, {8}           | uint8_t *, uint16_t *, ...    | unsigned integer                 | (uint), uint8, uint16, uint32, {uint64}
-  float     | dliteFloat     | 4, 8, {10, 16}         | float32_t *, float64_t *, ... | floating point                   | (float), (double), float32, float64, {float80, float128}
-  fixstring | dliteFixString | any                    | char *                        | fix-sized NUL-terminated string  | string20
-  string    | dliteStringPtr | sizeof(char *)         | char **                       | pointer to NUL-terminated string | string
-  relation  | dliteRelation  | sizeof(DLiteRelation)  | DLiteRelation *               | subject-predicate-object triple  | relation
-  dimension | dliteDimension | sizeof(DLiteDimension) | DLiteDimension *              | only intended for metadata       | dimension
-  property  | dliteProperty  | sizeof(DLiteProperty)  | DLiteProperty *               | only intended for metadata       | property
+  type      | dtype          | sizes                   | pointer                       | description                      | examples names
+  ----      | -----          | -----                   | -------                       | -----------                      | --------------
+  blob      | dliteBlob      | any                     | uint8_t *                     | binary blob, sequence of bytes   | blob32, blob128
+  bool      | dliteBool      | sizeof(bool)            | bool *                        | boolean                          | bool
+  int       | dliteInt       | 1, 2, 4, {8}            | int8_t *, int16_t *, ...      | signed integer                   | (int), int8, int16, int32, {int64}
+  uint      | dliteUInt      | 1, 2, 4, {8}            | uint8_t *, uint16_t *, ...    | unsigned integer                 | (uint), uint8, uint16, uint32, {uint64}
+  float     | dliteFloat     | 4, 8, {10, 16}          | float32_t *, float64_t *, ... | floating point                   | (float), (double), float32, float64, {float80, float128}
+  fixstring | dliteFixString | any                     | char *                        | fix-sized NUL-terminated string  | string20
+  string    | dliteStringPtr | sizeof(char *)          | char **                       | pointer to NUL-terminated string | string
+  ref       | dliteRef       | sizeof(DLiteInstance *) | DLiteInstance **              | UUID to another instance         | http://onto-ns.com/meta/0.1/Person
+  dimension | dliteDimension | sizeof(DLiteDimension)  | DLiteDimension *              | only intended for metadata       | dimension
+  property  | dliteProperty  | sizeof(DLiteProperty)   | DLiteProperty *               | only intended for metadata       | property
+  relation  | dliteRelation  | sizeof(DLiteRelation)   | DLiteRelation *               | subject-predicate-object triple  | relation
 
   The column "pointer" shows the C type of the `ptr` argument for
   functions like dlite_instance_get_property() and
@@ -96,7 +97,7 @@ typedef enum _DLiteType {
   dliteFloat,            /*!< Floating point */
   dliteFixString,        /*!< Fix-sized NUL-terminated string */
   dliteStringPtr,        /*!< Pointer to NUL-terminated string */
-
+  dliteRef,              /*!< Reference to instance */
   dliteDimension,        /*!< Dimension, for entities */
   dliteProperty,         /*!< Property, for entities */
   dliteRelation          /*!< Subject-predicate-object relation */
@@ -108,7 +109,7 @@ typedef enum _DLiteTypeFlag {
   dliteFlagDefault = 0,  /*!< Default (json) */
   dliteFlagRaw=1,        /*!< Raw unquoted input/output */
   dliteFlagQuoted=2,     /*!< Quoted input/output */
-  dliteFlagStrip=4       /*!< Strip initial and final spaces */
+  dliteFlagStrip=8       /*!< Strip initial and final spaces */
 } DLiteTypeFlag;
 
 
@@ -150,16 +151,17 @@ DLiteType dlite_type_get_dtype(const char *dtypename);
 int dlite_type_set_typename(DLiteType dtype, size_t size,
                             char *typename, size_t n);
 
-/*
+/**
   Writes the fortran type name corresponding to `dtype` and `size` to
   `typename`, which must be of size `n`.  Returns non-zero on error.
 */
 int dlite_type_set_ftype(DLiteType dtype, size_t size,
                          char *ftype, size_t n);
 
-/*
+/**
   Writes the Fortran ISO_C_BINDING type name corresponding to `dtype` and
-  `size` to `isoctype`, which must be of size `n`.  Returns non-zero on error.
+  `size` to `isoctype`, which must be of size `n`.
+  Returns non-zero on error.
 */
 int dlite_type_set_isoctype(DLiteType dtype, size_t size,
                             char *isoctype, size_t n);
diff --git a/tools/templates/c-source.txt b/tools/templates/c-source.txt
index 1762af2fe..75ee2ef31 100644
--- a/tools/templates/c-source.txt
+++ b/tools/templates/c-source.txt
@@ -95,6 +95,7 @@ static DLiteProperty {name%u}_properties[] = {{
     "{prop.name}", {@40}/* name */
     {prop.dtype},  {@40}/* type */
     {prop.size},   {@40}/* size */
+    {prop.ref},    {@40}/* ref */
     {prop.ndims},  {@40}/* ndims */
 {@if:{prop.ndims}=0}\
     NULL,          {@40}/* dims */


Test patch:
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index ee28fe6de..d55126cc0 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -15,6 +15,7 @@ set(tests
   test_collection
   test_schemas
   test_arrays
+  test_ref
   )
 
 list(APPEND tests test_json_entity)
diff --git a/src/tests/minunit/minunit.h b/src/tests/minunit/minunit.h
index 74116e66a..7569c4f3d 100644
--- a/src/tests/minunit/minunit.h
+++ b/src/tests/minunit/minunit.h
@@ -221,8 +221,8 @@ static void (*minunit_teardown)(void) = NULL;
 )
 
 #define mu_assert_string_eq(expected, result) MU__SAFE_BLOCK(\
-	const char* minunit_tmp_e = expected;\
-	const char* minunit_tmp_r = result;\
+	const char *minunit_tmp_e = expected;\
+	const char *minunit_tmp_r = result;\
 	minunit_assert++;\
 	if (!minunit_tmp_e) {\
 		minunit_tmp_e = "<null pointer>";\
@@ -240,8 +240,8 @@ static void (*minunit_teardown)(void) = NULL;
 )
 
 #define mu_assert_strn_eq(expected, result, size) MU__SAFE_BLOCK(       \
-	const char* minunit_tmp_e = expected;                           \
-	const char* minunit_tmp_r = result;                             \
+	const char *minunit_tmp_e = expected;                           \
+	const char *minunit_tmp_r = result;                             \
         int minunit_tmp_s = size;                                       \
 	minunit_assert++;                                               \
 	if (!minunit_tmp_e) {                                           \
@@ -262,6 +262,21 @@ static void (*minunit_teardown)(void) = NULL;
 	}                                                               \
 )
 
+#define mu_assert_ptr_eq(expected, result) MU__SAFE_BLOCK(\
+	const void *minunit_tmp_e;\
+	const void *minunit_tmp_r;\
+	minunit_assert++;\
+	minunit_tmp_e = (expected);\
+	minunit_tmp_r = (result);\
+	if (minunit_tmp_e != minunit_tmp_r) {\
+		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %p expected but was %p", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\
+		minunit_status = 1;\
+		return;\
+	} else {\
+		printf(".");\
+	}\
+)
+
 /*
  * The following two functions were written by David Robert Nadeau
  * from http://NadeauSoftware.com/ and distributed under the
diff --git a/src/tests/test_entity.c b/src/tests/test_entity.c
index 46bf9eca5..d973eee00 100644
--- a/src/tests/test_entity.c
+++ b/src/tests/test_entity.c
@@ -38,12 +38,12 @@ MU_TEST(test_meta_create)
     {"N", "Length of dimension N."}
   };
   DLiteProperty properties[] = {
-    /* name           type            size         ndims dims   unit descr */
-    {"a-string",      dliteStringPtr, sizeof(char *), 0, NULL,  "",  "..."},
-    {"a-float",       dliteFloat,     sizeof(float),  0, NULL,  "m", ""},
-    {"an-int-arr",    dliteInt,       sizeof(int),    2, dims0, "#", "descr.."},
-    {"a-string-arr",  dliteStringPtr, sizeof(char *), 1, dims1, "",  "descr.."},
-    {"a-string3-arr", dliteFixString, 3,              1, dims2, "",  "descr.."}
+    /* name          type            size           ref ndims dims  unit descr*/
+    {"a-string",     dliteStringPtr, sizeof(char *),NULL, 0, NULL,  "",  "..."},
+    {"a-float",      dliteFloat,     sizeof(float), NULL, 0, NULL,  "m", ""},
+    {"an-int-arr",   dliteInt,       sizeof(int),   NULL, 2, dims0, "#", "..."},
+    {"a-string-arr", dliteStringPtr, sizeof(char *),NULL, 1, dims1, "",  "..."},
+    {"a-string3-arr",dliteFixString, 3,             NULL, 1, dims2, "",  "..."}
   };
 
   mu_check((entity = (DLiteMeta *)dlite_meta_create(uri, "My test entity.",
@@ -350,11 +350,11 @@ MU_TEST(test_instance_get_hash)
   mu_assert_string_eq(hash, gethash(s, (DLiteInstance *)inst->meta));
 
   // metadata schema
-  hash = "d6d28a55e987f90e08edb553aa5ae811b7bb9459294c36c6ea8ea3899ba9b22a";
+  hash = "f362a385fc407242064eaadce973d86c1b8a45c6eac9b08355980404dfeb0db2";
   mu_assert_string_eq(hash, gethash(s, (DLiteInstance *)inst->meta->meta));
 
   // basic metadata schema
-  hash = "a7d885be5227c2e4ac3b3686c5a603566d70e1563ee221d4cdb24f53569c1ce5";
+  hash = "ddca715584cf0243344e207c18532cddc0d4d28516c9b7e0c815a7329f86ee61";
   mu_assert_string_eq(hash, gethash(s, (DLiteInstance *)inst->meta->meta->meta));
 
   // basic metadata schema
diff --git a/src/tests/test_json.c b/src/tests/test_json.c
index 5d6c5d0e2..88e6f3d38 100644
--- a/src/tests/test_json.c
+++ b/src/tests/test_json.c
@@ -38,13 +38,13 @@ MU_TEST(test_sprint)
   m = dlite_json_sprint(buf, sizeof(buf), (DLiteInstance *)meta, 0, 0);
   //printf("\n--------------------------------------------------------\n");
   //printf("%s\n", buf);
-  mu_assert_int_eq(798, m);
+  mu_assert_int_eq(799, m);
 
   m = dlite_json_sprint(buf, sizeof(buf), (DLiteInstance *)meta, 2,
                         dliteJsonWithUuid);
   //printf("\n--------------------------------------------------------\n");
   //printf("%s\n", buf);
-  mu_assert_int_eq(922, m);
+  mu_assert_int_eq(923, m);
 
   m = dlite_json_sprint(buf, sizeof(buf), inst, 4, 0);
   //printf("\n--------------------------------------------------------\n");
@@ -84,7 +84,7 @@ int append(const char *str)
   m = dlite_json_append(&s, &size, inst, 0);
   dlite_errclr();
   if (m < 0) retval=m;
-  printf("%s", s);
+  //printf("%s", s);
   free(s);
   return retval;
 }
diff --git a/src/tests/test_property.c b/src/tests/test_property.c
index 9b90dbd06..db77b412b 100644
--- a/src/tests/test_property.c
+++ b/src/tests/test_property.c
@@ -81,7 +81,7 @@ MU_TEST(test_print)
                       "\"description\": \"number of something\"}", s);
 
   char *dims[] = {"M", "N"};
-  DLiteProperty p = {"x", dliteInt, 4, 2, dims, "m", "about x..."};
+  DLiteProperty p = {"x", dliteInt, 4, NULL, 2, dims, "m", "about x..."};
   prop.type = dliteProperty;
   prop.size = sizeof(p);
   m = dlite_property_aprint(&s, &size, n, &p, &prop, NULL, 0, -2, 0);
diff --git a/src/tests/test_ref.c b/src/tests/test_ref.c
new file mode 100644
index 000000000..a0d583c71
--- /dev/null
+++ b/src/tests/test_ref.c
@@ -0,0 +1,41 @@
+/***************************************************************
+ * Test dliteRef
+ ***************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "minunit/minunit.h"
+
+#include "dlite.h"
+#include "dlite-macros.h"
+
+
+MU_TEST(test_load)
+{
+  char *path = STRINGIFY(dlite_SOURCE_DIR) "/src/tests/test_ref.json";
+  DLiteInstance *inst = dlite_instance_load_loc("json", path, NULL, "engine1");
+
+  mu_check(inst);
+
+
+  printf("\n==========================================\n");
+  dlite_json_print(inst);
+
+  dlite_instance_decref(inst);
+}
+
+
+/***********************************************************************/
+
+MU_TEST_SUITE(test_suite)
+{
+  MU_RUN_TEST(test_load);
+}
+
+
+int main()
+{
+  MU_RUN_SUITE(test_suite);
+  MU_REPORT();
+  return (minunit_fail) ? 1 : 0;
+}
diff --git a/src/tests/test_ref.json b/src/tests/test_ref.json
new file mode 100644
index 000000000..65ee79a12
--- /dev/null
+++ b/src/tests/test_ref.json
@@ -0,0 +1,61 @@
+{
+
+  "http://onto-ns.com/meta/test/0.1/Engine": {
+    "description": "Entity for an engine.",
+    "dimensions": {
+      "N": "Number of motors."
+    },
+    "properties": {
+      "motors": {
+        "type": "ref",
+        "$ref": "http://onto-ns.com/meta/test/0.1/Motor",
+        "shape": ["N"],
+        "description": "Array of motors in the engine."
+      }
+    }
+  },
+
+  "http://onto-ns.com/meta/test/0.1/Motor": {
+    "description": "Entity for an electric motor.",
+    "dimensions": {},
+    "properties": {
+      "effect": {
+        "type": "float32",
+        "unit": "W"
+      },
+      "voltage": {
+        "type": "float32",
+        "unit": "V"
+      }
+    }
+  },
+
+  "engine1": {
+    "meta": "http://onto-ns.com/meta/test/0.1/Engine",
+    "dimensions": {
+      "N": 2
+    },
+    "properties": {
+      "motors": ["motor1", "motor2"]
+    }
+  },
+
+  "motor1": {
+    "meta": "http://onto-ns.com/meta/test/0.1/Motor",
+    "dimensions": {},
+    "properties": {
+      "effect": 5.0,
+      "voltage": 9.0
+    }
+  },
+
+  "motor2": {
+    "meta": "http://onto-ns.com/meta/test/0.1/Motor",
+    "dimensions": {},
+    "properties": {
+      "effect": 2.0,
+      "voltage": 5.0
+    }
+  }
+
+}
diff --git a/src/tests/test_type.c b/src/tests/test_type.c
index c40fbeb0c..e4456c853 100644
--- a/src/tests/test_type.c
+++ b/src/tests/test_type.c
@@ -22,6 +22,7 @@ MU_TEST(test_get_dtypename)
   mu_assert_string_eq("blob", dlite_type_get_dtypename(dliteBlob));
   mu_assert_string_eq("bool", dlite_type_get_dtypename(dliteBool));
   mu_assert_string_eq("string", dlite_type_get_dtypename(dliteStringPtr));
+  mu_assert_string_eq("ref", dlite_type_get_dtypename(dliteRef));
   mu_assert_string_eq("relation", dlite_type_get_dtypename(dliteRelation));
 }
 
@@ -31,6 +32,8 @@ MU_TEST(test_get_enum_name)
   mu_assert_string_eq("dliteBool", dlite_type_get_enum_name(dliteBool));
   mu_assert_string_eq("dliteFixString",
                       dlite_type_get_enum_name(dliteFixString));
+  mu_assert_string_eq("dliteRef",
+                      dlite_type_get_enum_name(dliteRef));
   mu_assert_string_eq("dliteProperty",
                       dlite_type_get_enum_name(dliteProperty));
 }
@@ -41,6 +44,9 @@ MU_TEST(test_get_dtype)
   mu_assert_int_eq(dliteInt, dlite_type_get_dtype("int"));
   mu_assert_int_eq(dliteFloat, dlite_type_get_dtype("float"));
   mu_assert_int_eq(-1, dlite_type_get_dtype("float32"));
+  mu_assert_int_eq(dliteRef, dlite_type_get_dtype("ref"));
+  mu_assert_int_eq(dliteRef,
+                   dlite_type_get_dtype("http://onto-ns.com/meta/0.1/Entity"));
 }
 
 MU_TEST(test_set_typename)
@@ -51,6 +57,11 @@ MU_TEST(test_set_typename)
 
   mu_assert_int_eq(0, dlite_type_set_typename(dliteUInt, 8, typename, 32));
   mu_assert_string_eq("uint64", typename);
+
+  mu_assert_int_eq(0, dlite_type_set_typename(dliteRef,
+                                              sizeof(DLiteInstance *),
+                                              typename, 32));
+  mu_assert_string_eq("ref", typename);
 }
 
 MU_TEST(test_set_cdecl)
@@ -67,6 +78,11 @@ MU_TEST(test_set_cdecl)
   mu_assert_int_eq(6,
                    dlite_type_set_cdecl(dliteInt, 4, "n", 1, decl, 80, 1));
   mu_assert_string_eq("int *n", decl);
+
+  mu_assert_int_eq(17,
+                   dlite_type_set_cdecl(dliteRef, sizeof(DLiteInstance *),
+                                        "q", 1, decl, 80, 0));
+  mu_assert_string_eq("DLiteInstance **q", decl);
 }
 
 MU_TEST(test_is_type)
@@ -78,6 +94,7 @@ MU_TEST(test_is_type)
   mu_check(dlite_is_type("longdouble"));
   mu_check(dlite_is_type("blob42"));
   mu_check(dlite_is_type("string60"));
+  mu_check(dlite_is_type("ref"));
 }
 
 
@@ -97,18 +114,30 @@ MU_TEST(test_set_dtype_and_size)
   mu_assert_int_eq(dliteStringPtr, type);
   mu_assert_int_eq(sizeof(char *), size);
 
+  mu_assert_int_eq(0, dlite_type_set_dtype_and_size("ref", &type, &size));
+  mu_assert_int_eq(dliteRef, type);
+  mu_assert_int_eq(sizeof(DLiteInstance *), size);
+
+  mu_assert_int_eq(0, dlite_type_set_dtype_and_size("http://meta/0.1/Data",
+                                                    &type, &size));
+  mu_assert_int_eq(dliteRef, type);
+  mu_assert_int_eq(sizeof(DLiteInstance *), size);
+
+  // invalid type
+  mu_check(dlite_type_set_dtype_and_size("git://meta/0.1/Data", &type, &size));
+
   mu_assert_int_eq(0, dlite_type_set_dtype_and_size("property", &type, &size));
   mu_assert_int_eq(dliteProperty, type);
   mu_assert_int_eq(sizeof(DLiteProperty), size);
 
   // ok with comma following the type string
-  mu_assert_int_eq(0, dlite_type_set_dtype_and_size("string10,", &type, &size));
+  mu_assert_int_eq(0, dlite_type_set_dtype_and_size("string8,", &type, &size));
   mu_assert_int_eq(dliteFixString, type);
-  mu_assert_int_eq(11, size);
+  mu_assert_int_eq(9, size);
 
   mu_check(dlite_type_set_dtype_and_size("blob5a", &type, &size)); // fails
   mu_assert_int_eq(dliteFixString, type);
-  mu_assert_int_eq(11, size);
+  mu_assert_int_eq(9, size);  // unchanged
   err_clear();
 }
 
@@ -117,6 +146,7 @@ MU_TEST(test_is_allocated)
   mu_check(!dlite_type_is_allocated(dliteInt));
   mu_check(!dlite_type_is_allocated(dliteFixString));
   mu_check(dlite_type_is_allocated(dliteStringPtr));
+  mu_check(!dlite_type_is_allocated(dliteRef));
   mu_check(dlite_type_is_allocated(dliteDimension));
   mu_check(dlite_type_is_allocated(dliteProperty));
   mu_check(dlite_type_is_allocated(dliteRelation));
@@ -124,24 +154,32 @@ MU_TEST(test_is_allocated)
 
 MU_TEST(test_copy)
 {
-  double dest, src=3.4;
-  char sdst[32], ssrc[]="my source string";
-  mu_check(dlite_type_copy(&dest, &src, dliteFloat, sizeof(double)));
+  double dest=0.0, src=3.4;
+  char sdst[32]="", ssrc[]="my source string";
+  DLiteInstance *idst=NULL, *isrc=(DLiteInstance *)&src;
+  mu_check(dlite_type_copy(&dest, &src, dliteFloat, sizeof(src)));
   mu_assert_double_eq(src, dest);
 
   mu_check(dlite_type_copy(&sdst, &ssrc, dliteFixString, sizeof(ssrc)));
   mu_assert_string_eq(ssrc, sdst);
+
+  mu_check(dlite_type_copy(&idst, &isrc, dliteRef, sizeof(isrc)));
+  mu_assert_ptr_eq(isrc, idst);
 }
 
 MU_TEST(test_clear)
 {
   double v=3.4;
   char s[]="my source string";
-  mu_check(dlite_type_clear(&v, dliteFloat, sizeof(double)));
+  DLiteInstance *i=(DLiteInstance *)dlite_collection_create(NULL);
+  mu_check(dlite_type_clear(&v, dliteFloat, sizeof(v)));
   mu_assert_double_eq(0.0, v);
 
   mu_check(dlite_type_clear(&s, dliteFixString, sizeof(s)));
   mu_assert_string_eq("", s);
+
+  mu_check(dlite_type_clear(&i, dliteRef, sizeof(i)));
+  mu_assert_ptr_eq(NULL, i);
 }
 
 MU_TEST(test_print)
@@ -151,6 +189,7 @@ MU_TEST(test_print)
   int n;
   double v=3.141592;
   char *p=NULL, s[]="my source string", *q=s;
+  DLiteInstance *inst = (DLiteInstance *)dlite_collection_create("myid");
 
   mu_assert_int_eq(7, dlite_type_print(buf, sizeof(buf), &v, dliteFloat,
                                        sizeof(double), 0, -2, 0));
@@ -169,20 +208,25 @@ MU_TEST(test_print)
   mu_assert_string_eq("     3.14159", buf);
 
   mu_assert_int_eq(18, dlite_type_print(buf, sizeof(buf), &q, dliteStringPtr,
-                                        sizeof(char **), -1, -1,
+                                        sizeof(char *), -1, -1,
                                         dliteFlagQuoted));
   mu_assert_string_eq("\"my source string\"", buf);
 
   mu_assert_int_eq(4, dlite_type_print(buf, sizeof(buf), &p, dliteStringPtr,
-                                       sizeof(char **), -1, -1, 0));
+                                       sizeof(char *), -1, -1, 0));
   mu_assert_string_eq("null", buf);
 
+  mu_assert_int_eq(4, dlite_type_print(buf, sizeof(buf), &inst, dliteRef,
+                                        sizeof(DLiteInstance *), -1, -1, 0));
+  mu_assert_string_eq("myid", buf);
+
   n = dlite_type_aprint(&ptr, &size, 0, &q, dliteStringPtr, sizeof(char **),
                         -1, -1, dliteFlagQuoted);
   mu_assert_int_eq(18, n);
   mu_check((int)size > n);
   mu_assert_string_eq("\"my source string\"", ptr);
   free(ptr);
+  dlite_instance_decref(inst);
 }
 
 MU_TEST(test_scan)
@@ -213,6 +257,16 @@ MU_TEST(test_scan)
   mu_assert_int_eq(1, blob[0]);
   mu_assert_int_eq(254, blob[1]);
 
+  n = dlite_type_scan("01fe", -1, blob, dliteBlob, 2, dliteFlagRaw);
+  mu_assert_int_eq(4, n);
+  mu_assert_int_eq(1, blob[0]);
+  mu_assert_int_eq(254, blob[1]);
+
+  //n = dlite_type_scan("  01fe ", -1, blob, dliteBlob, 2, dliteFlagStrip);
+  //mu_assert_int_eq(8, n);
+  //mu_assert_int_eq(1, blob[0]);
+  //mu_assert_int_eq(254, blob[1]);
+
   n = dlite_type_scan("01fx", -1, blob, dliteBlob, 2, 0);
   mu_assert_int_eq(-1, n);
   err_clear();
@@ -308,6 +362,23 @@ MU_TEST(test_scan)
   mu_assert_string_eq("3.14", s);
   free(s);
 
+  /* ref */
+  DLiteInstance *inst=(DLiteInstance *)dlite_collection_create("instid");
+  DLiteInstance *inst2=inst;
+
+  n = dlite_type_scan(" null  ", -1, &inst2,
+                      dliteRef, sizeof(DLiteInstance **), 0);
+  mu_assert_int_eq(5, n);
+  mu_assert_ptr_eq(NULL, inst2);
+
+  n = dlite_type_scan("\"d04b56b9-d451-5c87-b34e-1b6fe96a9ade\"", -1, &inst2,
+                      dliteRef, sizeof(DLiteInstance **), dliteFlagQuoted);
+  mu_assert_int_eq(38, n);
+  mu_assert_ptr_eq(inst, inst2);
+
+  dlite_instance_decref(inst);
+  dlite_instance_decref(inst2);
+
   /* dliteDimension */
   memset(&dim, 0, sizeof(DLiteDimension));
 
@@ -437,7 +508,7 @@ MU_TEST(test_update_sha3)
   mu_assert_string_eq(hash, gethash(s, &d2, dliteDimension, sizeof(d2)));
 
   char *dims[] = {"dim1", "dim2"};
-  DLiteProperty p = {"propname", dliteStringPtr, sizeof(char *), 2, dims,
+  DLiteProperty p = {"propname", dliteStringPtr, sizeof(char *), NULL, 2, dims,
     "m/s", NULL};
   hash = "f52af54cb773ec8a7499ae44f3499c58fba40203d870c0c52842d10cbbcdc13a";
   mu_assert_string_eq(hash, gethash(s, &p, dliteProperty, sizeof(p)));
@@ -461,11 +532,13 @@ MU_TEST(test_get_alignment)
   mu_assert_int_eq(1,  dlite_type_get_alignment(dliteFixString, 3));
 #if defined(i386) && i386
   mu_assert_int_eq(4,  dlite_type_get_alignment(dliteInt,  8));
+  mu_assert_int_eq(4,  dlite_type_get_alignment(dliteRef, 4));
   mu_assert_int_eq(4,  dlite_type_get_alignment(dliteDimension,
                                                 sizeof(DLiteDimension)));
 #else
   mu_assert_int_eq(8,  dlite_type_get_alignment(dliteInt,  8));
   mu_assert_int_eq(8,  dlite_type_get_alignment(dliteStringPtr, 8));
+  mu_assert_int_eq(8,  dlite_type_get_alignment(dliteRef, 8));
   mu_assert_int_eq(8,  dlite_type_get_alignment(dliteDimension,
                                                 sizeof(DLiteDimension)));
 #endif