Repository: SINTEF/dlite (language: python)

Issue:
More semantic naming of plugin_path_delete() and similar functions
Rename:
```
int plugin_path_delete(PluginInfo *info, int n)  -->  int plugin_path_delete_index(PluginInfo *info, int index)  

int plugin_path_remove(PluginInfo *info, const char *path) --> int plugin_path_delete(PluginInfo *info, const char *path)
```
Similar changes should be done in the following files:
* src/utils/plugin.c
* src/utils/fileutils.c
* src/dlite-mapping.c 
* src/dlite-storage.c
* src/dlite-mapping-plugins.c 
* src/dlite-storage-plugins.c


Discussion / hints:

Fix patch:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 62ee15c2c..1f4bc3d6e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,9 +66,6 @@ option(WITH_EXAMPLES    "Whether to build/run examples during testing"   ON)
 option(FORCE_EXAMPLES   "Whether to force building/running examples"     OFF)
 option(ALLOW_WARNINGS   "Whether to not fail on compilation warnings"    OFF)
 
-option(BUILD_HDF5       "If not installed, download and compile HDF5"    OFF)
-option(BUILD_JSON       "If not installed, download and compile jansson" OFF)
-
 
 # Append our cmake-modules to CMAKE_MODULE_PATH
 list(APPEND CMAKE_MODULE_PATH ${dlite_SOURCE_DIR}/cmake)
@@ -147,19 +144,16 @@ set(DLITE_CONFIG_VARS
   DLITE_STORAGE_PLUGIN_DIRS
   DLITE_MAPPING_PLUGIN_DIRS
   )
-  if(WITH_JSON)
-    list(APPEND DLITE_CONFIG_VARS JANSSON_LIBRARIES)
-  endif()
-  if(WITH_HDF5)
-    list(APPEND DLITE_CONFIG_VARS HDF5_LIBRARIES)
-  endif()
-  if(WITH_PYTHON)
-    list(APPEND DLITE_CONFIG_VARS
-      Python3_LIBRARIES
-      DLITE_PYTHON_STORAGE_PLUGIN_DIRS
-      DLITE_PYTHON_MAPPING_PLUGIN_DIRS
-      )
-  endif()
+if(WITH_HDF5)
+  list(APPEND DLITE_CONFIG_VARS HDF5_LIBRARIES)
+endif()
+if(WITH_PYTHON)
+  list(APPEND DLITE_CONFIG_VARS
+    Python3_LIBRARIES
+    DLITE_PYTHON_STORAGE_PLUGIN_DIRS
+    DLITE_PYTHON_MAPPING_PLUGIN_DIRS
+    )
+endif()
 
 # ------------------------
 # Installation environment
@@ -186,159 +180,18 @@ make_platform_paths(
   )
 
 
-
-####################################################################
-# Use ExternalProject_add instead of add_subdirectory for packages.
-# For explanation of the approach, see
-# https://coderwall.com/p/y3zzbq/use-cmake-enabled-libraries-in-your-cmake-project
-#
-# In short, main reasons for using ExternalProject:
-#   - Avoids problems with multible definitions of standard targets like
-#     doc, memcheck, etc...
-#   - External dependencies are transparently downloaded, configured
-#     and compiled as a part of the build process.
-#
-# Since external download and configure steps of ExternalProject
-# happens at compile time, find_package(), which operate at configure
-# time, cannot be used with ExternalProject.  For this reason some
-# people configures everything as ExternalProject (even the main
-# project) and creates a super project that calls
-# ExternalProject_Add() in the correct order such all dependencies are
-# build when the ExternalProject of the main project is configured,
-# allowing them to use configure project.
-#
-# Here we take another approach. Instead of relying on find_package()
-# we manually sets the path of the resources (headers, libraries)
-# provided by the dependency after ExternalProject_Add() is called.
-# This way, the main project can follow the standard cmake structure.
-#
-####################################################################
-include(ExternalProject)
-
-# A directory that external projects install into
-set(external_INSTALL_DIR ${dlite_BINARY_DIR}/local)
-
-# Short names
-set(LIB_PRE ${CMAKE_STATIC_LIBRARY_PREFIX})
-set(LIB_EXT ${CMAKE_STATIC_LIBRARY_SUFFIX})
-set(SH_PRE  ${CMAKE_SHARED_LIBRARY_PREFIX})
-set(SH_EXT  ${CMAKE_SHARED_LIBRARY_SUFFIX})
-
 #
 # hdf5
 # ====
 if(WITH_HDF5)
-  find_package(HDF5 1.8.20 COMPONENTS C)
-  if(NOT HDF5_FOUND)
-    if(NOT BUILD_HDF5)
-      message(FATAL_ERROR "HDF5 not found.  Try to set BUILD_HDF5 to ON")
-    endif()
-    message(STATUS "Download and build HDF5 as external package")
-
-    set(hdf5_MAJOR_VERSION  "1.10")
-    set(hdf5_MINOR_VERSION  "4")
-    set(hdf5_MD5            cdf02e61f0d9920a7e7183aa0fb35429)
-    set(hdf5_VERSION        "${hdf5_MAJOR_VERSION}.${hdf5_MINOR_VERSION}")
-    set(hdf5_NAME           "hdf5-${hdf5_VERSION}")
-    set(hdf5_SOURCE_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/external/${hdf5_NAME}")
-    set(hdf5_INSTALL_PREFIX "${external_INSTALL_DIR}")
-
-    set(cmake_args
-      -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-      -DHDF5_BUILD_CXX=OFF
-      -DHDF5_BUILD_FORTRAN=OFF
-      -DCMAKE_BUILD_TYPE=Release
-      -DCMAKE_INSTALL_PREFIX=${hdf5_INSTALL_PREFIX}
-      -DHAVE_IOEO_EXITCODE="FAILED_TO_RUN"
-      -DBUILD_SHARED_LIBS=ON
-      -DH5_BUILT_AS_DYNAMIC_LIB
-    )
-    if(CMAKE_TOOLCHAIN_FILE)
-      get_filename_component(toolchain_file "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE)
-      list(APPEND cmake_args "-DCMAKE_TOOLCHAIN_FILE=${toolchain_file}")
-    endif()
-
-    ExternalProject_Add(
-      hdf5Proj
-      PREFIX            ${CMAKE_CURRENT_BINARY_DIR}/external/${hdf5_NAME}
-      URL               https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-${hdf5_MAJOR_VERSION}/${hdf5_NAME}/src/${hdf5_NAME}.tar.gz
-      URL_MD5           ${hdf5_MD5}
-      SOURCE_DIR        ${hdf5_SOURCE_DIR}
-      UPDATE_COMMAND    ""
-      CMAKE_ARGS        ${cmake_args}
-      TEST_COMMAND      ctest -C Release
-      INSTALL_COMMAND   cmake --build . --target install
-    )
-    set(HDF5_DEPENDENCIES hdf5Proj)
-
-    # Define the following variables:
-    #
-    #   HDF5_INCLUDE_DIRS - location of the HDF5 include dirs
-    #   HDF5_LIBRARY_DIRS - location of the HDF5 library dirs
-    #   HDF5_LIBRARIES    - required libraries
-    #
-    set(HDF5_INCLUDE_DIRS ${hdf5_INSTALL_PREFIX}/include)
-    set(HDF5_LIBRARY_DIRS ${hdf5_INSTALL_PREFIX}/lib)
-    set(HDF5_LIBRARIES
-      ${hdf5_INSTALL_PREFIX}/lib/${SH_PRE}hdf5${SH_EXT})
-
-    if(WIN32)
-      list(APPEND HDF5_LIBRARIES Kernel32)
-    else()
-      list(APPEND HDF5_LIBRARIES m dl)
-    endif()
-
-  endif()
-else()
-  set(HDF5_LIBRARIES "")
-endif()
-
-
-#
-# jansson
-# =======
-if(WITH_JSON)
-  include(Findjansson)
-  find_package(jansson)
-
-  if(NOT JANSSON_FOUND)
-    if(NOT BUILD_JSON)
-      message(FATAL_ERROR "Jansson not found.  Try to set BUILD_JSON to ON")
-    endif()
-    message(STATUS "Download and build jansson as external package")
-    set(jansson_SOURCE_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/external/jansson")
-    set(jansson_INSTALL_PREFIX "${external_INSTALL_DIR}")
-    set(cmake_args
-      -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-      -DCMAKE_BUILD_TYPE=Release
-      -DCMAKE_INSTALL_PREFIX=${jansson_INSTALL_PREFIX}
-      -DBUILD_SHARED_LIBS=ON
-      )
-    if(CMAKE_TOOLCHAIN_FILE)
-      get_filename_component(toolchain_file "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE)
-      list(APPEND cmake_args "-DCMAKE_TOOLCHAIN_FILE=${toolchain_file}")
-    endif()
-    ExternalProject_Add(
-      janssonProj
-      PREFIX          ${CMAKE_CURRENT_BINARY_DIR}/external/jansson
-      SOURCE_DIR      ${jansson_SOURCE_DIR}
-      GIT_REPOSITORY  "https://github.com/akheron/jansson.git"
-      GIT_TAG         v2.9
-      CMAKE_ARGS      ${cmake_args}
-      UPDATE_COMMAND  ""
-      TEST_COMMAND    ctest
-      INSTALL_COMMAND cmake --build . --target install
-      )
-    set(JANSSON_DEPENDENCIES janssonProj)
-    set(JANSSON_INCLUDE_DIRS ${jansson_INSTALL_PREFIX}/include)
-    set(JANSSON_LIBRARIES
-      ${jansson_INSTALL_PREFIX}/lib/${SH_PRE}jansson${SH_EXT})
+  find_package(HDF5 1.8.20 COMPONENTS C REQUIRED)
+  if(WIN32)
+    list(APPEND HDF5_LIBRARIES Kernel32)
+  else()
+    list(APPEND HDF5_LIBRARIES m dl)
   endif()
-else()
-  set(JANSSON_LIBRARIES "")
 endif()
 
-
 #
 # Redland
 # =======
@@ -519,14 +372,12 @@ if(WIN32)
     )
   if(WITH_PYTHON)
     build_append(dlite_PATH ${dlite_BINARY_DIR}/src/pyembed)
+    list(APPEND dlite_PATH ${Python3_RUNTIME_LIBRARY_DIRS})
     list(APPEND dlite_PATH ${Python3_NumPy_LIBRARY_DIRS})
   endif()
   if(WITH_FORTRAN)
     build_append(dlite_PATH ${dlite_BINARY_DIR}/bindings/fortran)
   endif()
-  if(WITH_JSON)
-    list(APPEND dlite_PATH ${JANSSON_RUNTIME_DIR})
-  endif()
   if(WITH_HDF5)
     list(APPEND dlite_PATH ${HDF5_RUNTIME_DIR})
   endif()
@@ -554,9 +405,6 @@ if(CROSS_TARGET STREQUAL "win64" OR CROSS_TARGET STREQUAL "win32")
   if(WITH_FORTRAN)
     list(APPEND dlite_WINEPATH ${dlite_BINARY_DIR}/bindings/fortran)
   endif()
-  if(WITH_JSON)
-    list(APPEND dlite_WINEPATH ${JANSSON_RUNTIME_DIR})
-  endif()
   if(WITH_HDF5)
     list(APPEND dlite_WINEPATH ${HDF5_RUNTIME_DIR})
   endif()
@@ -577,7 +425,6 @@ if(WITH_PYTHON)
   build_append(dlite_LD_LIBRARY_PATH ${dlite_BINARY_DIR}/src/pyembed)
 endif()
 #list(APPEND dlite_LD_LIBRARY_PATH
-#  ${JANSSON_RUNTIME_DIR}
 #  ${HDF5_RUNTIME_DIR}
 #  )
 list(REMOVE_DUPLICATES dlite_LD_LIBRARY_PATH)
@@ -680,18 +527,26 @@ set(test_env
   "export DLITE_TEMPLATE_DIRS=${dlite_TEMPLATES}"
   "export DLITE_STORAGES='${dlite_STORAGES}'"
   ""
-  "========== Python =================="
+  "========== Python ==================="
   "Python3_LIBRARIES:          ${Python3_LIBRARIES}"
   "Python3_LIBRARY_DIRS:       ${Python3_LIBRARY_DIRS}"
   "Python3_INCLUDE_DIRS:       ${Python3_INCLUDE_DIRS}"
   "Python3_NumPy_INCLUDE_DIRS: ${Python3_NumPy_INCLUDE_DIRS}"
   "Python3_NumPy_LIBRARY_DIRS: ${Python3_NumPy_LIBRARY_DIRS}"
   ""
-  "========== Jansson =================="
-  "JANSSON_INCLUDE_DIRS: ${JANSSON_INCLUDE_DIRS}"
-  "JANSSON_LIBRARIES:    ${JANSSON_LIBRARIES}"
-  "JANSSON_LIBRARY_DIR:  ${JANSSON_LIBRARY_DIR}"
-  "JANSSON_RUNTIME_DIR:  ${JANSSON_RUNTIME_DIR}"
+  "========== Redland =================="
+  "REDLAND_INCLUDE_DIRS: ${REDLAND_INCLUDE_DIRS}"
+  "REDLAND_LIBRARIES:    ${REDLAND_LIBRARIES}"
+  "REDLAND_LIBRARY_DIR:  ${REDLAND_LIBRARY_DIR}"
+  "REDLAND_RUNTIME_DIR:  ${REDLAND_RUNTIME_DIR}"
+  ""
+  "RASQAL_INCLUDE_DIRS: ${RASQAL_INCLUDE_DIRS}"
+  "RASQAL_LIBRARIES:    ${RASQAL_LIBRARIES}"
+  "RASQAL_DEFINITIONS:  ${RASQAL_DEFINITIONS}"
+  ""
+  "RAPTOR_INCLUDE_DIRS: ${RAPTOR_INCLUDE_DIRS}"
+  "RAPTOR_LIBRARIES:    ${RAPTOR_LIBRARIES}"
+  "RAPTOR_DEFINITIONS:  ${RAPTOR_DEFINITIONS}"
   ""
   "========== HDF5 ====================="
   "HDF5_INCLUDE_DIRS:    ${HDF5_INCLUDE_DIRS}"
@@ -779,7 +634,6 @@ set(CPACK_SOURCE_IGNORE_FILES ${CPACK_SOURCE_IGNORE_FILES}
 # DEB package
 set(CPACK_DEBIAN_PACKAGE_DEPENDS
   libhdf5
-  libjansson
   python3
   python3-numpy
   )
@@ -788,7 +642,6 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS
 set(CPACK_RPM_PACKAGE_LICENSE "${dlite_LICENSE}")
 set(CPACK_RPM_PACKAGE_REQUIRES
   hdf5
-  jansson
   python3
   python3-numpy
   )
diff --git a/Dockerfile b/Dockerfile
index 9daa5cff6..27695b795 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -42,7 +42,6 @@ RUN DEBIAN_FRONTEND="noninteractive" apt-get install -qq -y --fix-missing \
         git \
         g++ \
         libhdf5-dev \
-        libjansson-dev \
         librdf0-dev \
         librasqal3-dev \
         libraptor2-dev \
@@ -137,7 +136,6 @@ RUN apt -qq update \
   && rm -rf /var/lib/apt/lists/*
 # Copy needed dlite files and libraries to slim image
 COPY --from=build /tmp/dlite-install /usr/local
-COPY --from=build /usr/lib/x86_64-linux-gnu/libjansson.so* /usr/local/lib/
 COPY --from=build /usr/lib/x86_64-linux-gnu/libhdf5*.so* /usr/local/lib/
 COPY --from=build /usr/lib/x86_64-linux-gnu/libsz.so* /usr/local/lib/
 COPY --from=build /usr/lib/x86_64-linux-gnu/libaec.so* /usr/local/lib/
diff --git a/Dockerfile-mingw b/Dockerfile-mingw
index 0c10e29c1..ac8f11337 100644
--- a/Dockerfile-mingw
+++ b/Dockerfile-mingw
@@ -63,7 +63,6 @@ RUN dnf install -y \
         mingw64-openssl \
         mingw64-python3 \
         mingw64-python3-numpy \
-        mingw64-jansson \
         mingw64-gcc-gfortran \
         mingw64-gcc-c++ \
         mingw64-dlfcn \
diff --git a/README.md b/README.md
index 111e1b4a0..4363b10f5 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
 <img src="doc/figs/logo.svg" align="right" />
 
+
 DLite
 =====
 > Lightweight data-centric framework for working with scientific data
@@ -207,9 +208,9 @@ Main features
 -------------
 See [doc/features.md](doc/features.md) for a more detailed list.
   - Enables semantic interoperability via simple formalised metadata and data
-  - Metadta can be linked to or generated from ontologies
+  - Metadata can be linked to or generated from ontologies
   - Code generation for simple integration in existing code bases
-  - Plugin API for data storages
+  - Plugin API for data storages (json, hdf5, rdf, yaml, postgresql, blob, csv...)
   - Plugin API for mapping between metadata
   - Bindings to C, Python and Fortran
 
@@ -289,7 +290,7 @@ Dependencies
 
 ### Runtime dependencies
   - [HDF5][3], optional (needed by HDF5 storage plugin)
-  - [Jansson][4], optional (needed by JSON storage plugin)
+  - [librdf][4], optional (needed by RDF (Redland) storage plugin)
   - [Python 3][5], optional (needed by Python bindings and some plugins)
     - [NumPy][6], required if Python is enabled
     - [PyYAML][7], optional (used for generic YAML storage plugin)
@@ -300,7 +301,7 @@ Dependencies
 ### Build dependencies
   - [cmake][9], required for building
   - hdf5 development libraries, optional (needed by HDF5 storage plugin)
-  - Jansson development libraries, optional (needed by JSON storage plugin)
+  - librdf development libraries, optional (needed by librdf storage plugin)
   - Python 3 development libraries, optional (needed by Python bindings)
   - NumPy development libraries, optional (needed by Python bindings)
   - [SWIG v3][10], optional (needed by building Python bindings)
@@ -337,16 +338,15 @@ system defined in Dockerfile.
 
 ### Build on Linux
 
-Install the hdf5 (does not include the parallel component) and jansson
-libraries
+Install the hdf5 (does not include the parallel component) libraries
 
 On Ubuntu:
 
-    sudo apt-get install libhdf5-serial-dev libjansson-dev
+    sudo apt-get install libhdf5-serial-dev
 
 On Redhad-based distributions (Fedora, Centos, ...):
 
-    sudo dnf install hdf5-devel jansson-devel
+    sudo dnf install hdf5-devel
 
 Build with:
 
@@ -411,7 +411,7 @@ DLite is developed with the hope that it will be a delight to work with.
 [1]: https://stash.code.sintef.no/projects/SOFT/repos/soft5/
 [2]: https://github.com/NanoSim/Porto/blob/porto/Preview-Final-Release/doc/manual/02_soft_introduction.md#soft5-features
 [3]: https://support.hdfgroup.org/HDF5/
-[4]: http://www.digip.org/jansson/
+[4]: https://librdf.org/
 [5]: https://www.python.org/
 [6]: https://pypi.org/project/numpy/
 [7]: https://pypi.org/project/PyYAML/
diff --git a/bindings/python/dlite-path-python.i b/bindings/python/dlite-path-python.i
index c995031fb..ae0cd5f2b 100644
--- a/bindings/python/dlite-path-python.i
+++ b/bindings/python/dlite-path-python.i
@@ -6,6 +6,14 @@
     def __contains__(self, value):
         return value in self.aslist()
 
+    def __getitem__(self, key):
+        n = len(self)
+        if key < 0:
+            key += n
+        if key < 0 or key >= n:
+            raise IndexError(f'key out of range: {key}')
+        return self.getitem(key)
+
     def aslist(self):
         return [self[i] for i in range(len(self))]
 
@@ -14,9 +22,10 @@
 
 
 %pythoncode %{
-storage_path = FUPath("storage")
-mapping_path = FUPath("mapping")
-python_storage_path = FUPath("python-storage")
-python_mapping_path = FUPath("python-mapping")
+storage_path = FUPath("storages")
+storage_plugin_path = FUPath("storage-plugins")
+mapping_plugin_path = FUPath("mapping-plugins")
+python_storage_plugin_path = FUPath("python-storage-plugins")
+python_mapping_plugin_path = FUPath("python-mapping-plugins")
 
 %}
diff --git a/bindings/python/dlite-path.i b/bindings/python/dlite-path.i
index 6606f0fa4..097504eb8 100644
--- a/bindings/python/dlite-path.i
+++ b/bindings/python/dlite-path.i
@@ -30,13 +30,15 @@ Creates a _Path instance of type `pathtype`.
 ") _FUPaths;
 %extend struct _FUPaths {
   _FUPaths(const char *pathtype) {
-    if (strcmp(pathtype, "storage") == 0) {
+    if (strcmp(pathtype, "storages") == 0) {
       return dlite_storage_paths();
-    } else if (strcmp(pathtype, "mapping") == 0) {
+    } else if (strcmp(pathtype, "storage-plugins") == 0) {
+      return dlite_storage_plugin_paths_get();
+    } else if (strcmp(pathtype, "mapping-plugins") == 0) {
       return dlite_mapping_plugin_paths_get();
-    } else if (strcmp(pathtype, "python-storage") == 0) {
+    } else if (strcmp(pathtype, "python-storage-plugins") == 0) {
       return dlite_python_storage_paths();
-    } else if (strcmp(pathtype, "python-mapping") == 0) {
+    } else if (strcmp(pathtype, "python-mapping-plugins") == 0) {
       return dlite_python_mapping_paths();
     } else {
       return dlite_err(1, "invalid pathtype: %s", pathtype), NULL;
@@ -54,7 +56,7 @@ Creates a _Path instance of type `pathtype`.
   int __len__(void) {
     return $self->n;
   }
-  const char *__getitem__(int index) {
+  const char *getitem(int index) {
     if (index < 0) index += $self->n;
     if (index < 0 || index >= (int)$self->n)
       return dlite_err(1, "index out of range: %d", index), NULL;
@@ -65,12 +67,12 @@ Creates a _Path instance of type `pathtype`.
     if (index < 0 || index >= (int)$self->n) {
       dlite_err(1, "index out of range: %d", index);
     } else {
-      fu_paths_remove($self, index);
+      fu_paths_delete($self, index);
       fu_paths_insert($self, path, index);
     }
   }
   void __delitem__(int index) {
-    fu_paths_remove($self, index);
+    fu_paths_delete($self, index);
   }
 
   void insert(int index, const char *path) {
diff --git a/bootstrap-win.sh b/bootstrap-win.sh
index 80edc36d1..c808f9af5 100644
--- a/bootstrap-win.sh
+++ b/bootstrap-win.sh
@@ -47,26 +47,6 @@ if [ -f bootstrap-win-user.sh ]; then
     source bootstrap-win-user.sh
 fi
 #
-# Build and install jansson
-#
-if [ ! "$WITH_JSON" = OFF ] && [ ! -f $INSTALL_PREFIX/lib/jansson.lib ]; then
-    cd "$ROOT_PATH"
-    if [ ! -d jansson ]; then
-        git clone https://github.com/akheron/jansson.git
-    fi
-    cd jansson
-    mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
-    cmake \
-        -G "$GENERATOR" \
-        -DCMAKE_INSTALL_PREFIX:PATH="$INSTALL_PREFIX" \
-        -DJANSSON_BUILD_SHARED_LIBS=OFF \
-        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-        -DJANSSON_BUILD_DOCS=OFF \
-        ..
-    cmake --build . --config Debug --target install
-    cmake --build . --config Release --target install
-fi
-#
 # Build and install hdf5
 #
 if [ ! "$WITH_HDF5" = OFF ] && [ ! -f $INSTALL_PREFIX/lib/libhdf5.lib ]; then
diff --git a/cmake/Finddlite.cmake b/cmake/Finddlite.cmake
index 25573714c..849825c7a 100644
--- a/cmake/Finddlite.cmake
+++ b/cmake/Finddlite.cmake
@@ -96,27 +96,12 @@ else()
     ${DLITE_UTILS_LIBRARY}
     )
 
-  # Find HDF5 and Jansson
-  # FIXME - when dlite is using dynamic loaded plugins we should:
-  #     Windows: append Jansson and HDF5 runtime library paths to dlite_PATH
-  #     Linux:   append Jansson and HDF5 library paths to dlite_LD_LIBRARY_PATH
-  find_library(JANSSON_LIBRARY
-    NAMES
-      jansson
-    PATHS
-      ${JANSSON_ROOT}/lib
-      $ENV{HOME}/.local/lib
-      /usr/lib
-      /usr/local/lib
-      /opt/local/lib
-      /sw/lib
-    )
-  list(APPEND JANSSON_LIBRARIES ${JANSSON_LIBRARY})
-
+  # Find HDF5
   find_package(HDF5 COMPONENTS C)
 
+  # FIXME - should we also find redland?
+
   list(APPEND DLITE_LIBRARIES
-    ${JANSSON_LIBRARIES}
     ${HDF5_LIBRARIES}
     )
 
diff --git a/cmake/Findjansson.cmake b/cmake/Findjansson.cmake
deleted file mode 100644
index 1a945ec43..000000000
--- a/cmake/Findjansson.cmake
+++ /dev/null
@@ -1,71 +0,0 @@
-# - Try to find Jansson
-# Once done this will define
-#
-#  JANSSON_FOUND        - whether system has Jansson
-#  JANSSON_INCLUDE_DIRS - the Jansson include directory
-#  JANSSON_LIBRARIES    - link these to use Jansson
-#  JANSSON_LIBRARY_DIR  - Jansson library directory (lib files)
-#  JANSSON_RUNTIME_DIR  - Jansson runtime directory (dlls)
-#
-
-
-if(JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS)
-  # in cache already
-  set(JANSSON_FOUND TRUE)
-else()
-  find_path(JANSSON_INCLUDE_DIR
-    NAMES
-      jansson.h
-    PATHS
-      ${JANSSON_ROOT}/include
-      $ENV{JANSSON_ROOT}/include
-      $ENV{HOME}/.local/include
-      /usr/include
-      /usr/local/include
-      /opt/local/include
-      /mingw/include
-      /sw/include
-    )
-  list(APPEND JANSSON_INCLUDE_DIRS ${JANSSON_INCLUDE_DIR})
-  get_filename_component(JANSSON_ROOT ${JANSSON_INCLUDE_DIR} DIRECTORY)
-
-  find_library(JANSSON_LIBRARY
-    NAMES
-      jansson
-      libjansson.dll.a
-    PATHS
-      ${JANSSON_ROOT}/lib
-      /usr/lib
-      /usr/local/lib
-      /opt/local/lib
-      /mingw/lib
-      /sw/lib
-    )
-  list(APPEND JANSSON_LIBRARIES ${JANSSON_LIBRARY})
-
-  get_filename_component(JANSSON_LIBRARY_DIR ${JANSSON_LIBRARY} DIRECTORY)
-
-  if(EXISTS ${JANSSON_ROOT}/bin)
-    set(JANSSON_RUNTIME_DIR ${JANSSON_ROOT}/bin)
-  else()
-    set(JANSSON_RUNTIME_DIR "")
-  endif()
-
-  include(FindPackageHandleStandardArgs)
-  find_package_handle_standard_args(Jansson DEFAULT_MSG
-    JANSSON_INCLUDE_DIRS
-    JANSSON_LIBRARIES
-    JANSSON_LIBRARY_DIR
-    JANSSON_RUNTIME_DIR
-    )
-
-  # show the JANSSON_INCLUDE_DIRS and JANSSON_LIBRARIES variables only
-  # in the advanced view
-  mark_as_advanced(
-    JANSSON_INCLUDE_DIRS
-    JANSSON_LIBRARIES
-    JANSSON_LIBRARY_DIR
-    JANSSON_RUNTIME_DIR
-    )
-
-endif()
diff --git a/cmake/dliteConfig.cmake.in b/cmake/dliteConfig.cmake.in
index f91bd5dd3..a9cae0ea5 100644
--- a/cmake/dliteConfig.cmake.in
+++ b/cmake/dliteConfig.cmake.in
@@ -54,19 +54,14 @@ if(NOT DLITE_LIBRARIES)
     list(APPEND dlite_LIBRARIES "${Python3_LIBRARIES}")
   endif()
 
-  # Jansson
-  if(WITH_JSON)
-    set(JANSON_LIBRARIES "@JANSSON_LIBRARIES@" CACHE STRING
-      "Jansson libraries")
-    list(APPEND dlite_LIBRARIES "${JANSSON_LIBRARIES}")
-  endif()
-
   # HDF5
   if(WITH_HDF5)
     set(HDF5_LIBRARIES "@HDF5_LIBRARIES@" CACHE STRING "HDF5 libraries")
     list(APPEND dlite_LIBRARIES "${HDF5_LIBRARIES}")
   endif()
 
+  # FIXME: also add redland
+
   set(DLITE_LIBRARIES "${dlite_LIBRARIES}" CACHE STRING "DLite libraries")
 endif()
 
diff --git a/doc/build-with-vs.md b/doc/build-with-vs.md
index ddfe60a5b..7db64d9f3 100644
--- a/doc/build-with-vs.md
+++ b/doc/build-with-vs.md
@@ -58,14 +58,14 @@ To run the tests, do
     ctest -C Debug
 
 
-Summary to build and install DLite when hdf5 and jansson lib are installed
+Summary to build and install DLite when hdf5 lib are installed
 in the given path LOCAL_DIR
 
     LOCAL_DIR=/C/Users/tco/Documents/Programs/philib/local
     PATH=$PATH:$LOCAL_DIR/bin
     cd dlite
     mkdir build && cd build
-    cmake -G "Visual Studio 14 2015 Win64" -DHDF5_DIR=$LOCAL_DIR/cmake/hdf5 -DJANSSON_ROOT=$LOCAL_DIR/ -DCMAKE_INSTALL_PREFIX=$LOCAL_DIR/ ..
+    cmake -G "Visual Studio 14 2015 Win64" -DHDF5_DIR=$LOCAL_DIR/cmake/hdf5 -DCMAKE_INSTALL_PREFIX=$LOCAL_DIR/ ..
     cmake --build . --config Debug --target doc
     cmake --build . --config Debug --target install
     cmake --build . --config Release --target install
diff --git a/doc/features.md b/doc/features.md
index 9dec4b344..086305ee4 100644
--- a/doc/features.md
+++ b/doc/features.md
@@ -16,7 +16,8 @@ Features
       - property (only intended for metadata)
   - Supports units and multi-dimensional arrays
   - Fully implemented metadata model as presented by Thomas Hagelien
-  - Builtin HDF5, JSON, YAML and PostgreSQL storage plugins
+  - Builtin JSON, HDF5, RDF, YAML, PostgreSQL, csv, blob storage plugins
+    (JSON is always available, the other depends on external libraries)
   - Plugin system for user-provided storage drivers
   - Memory for metadata and instances is reference counted
   - Lookup of metadata and instances at pre-defined locations (initiated
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index b298be3b8..355d9f9ac 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -83,7 +83,7 @@ if(FORCE_EXAMPLES OR EXISTS ${CMAKE_INSTALL_PREFIX}/share/dlite/examples)
     add_test(
       NAME ${name}
       COMMAND ${RUNNER} ${Python3_EXECUTABLE}
-          ${CMAKE_CURRENT_SOURCE_DIR}/read-csv/main.py
+          ${CMAKE_CURRENT_SOURCE_DIR}/${name}/main.py
       )
     set_property(TEST ${name} PROPERTY
       ENVIRONMENT "PATH=${dlite_PATH_NATIVE}")
@@ -93,6 +93,8 @@ if(FORCE_EXAMPLES OR EXISTS ${CMAKE_INSTALL_PREFIX}/share/dlite/examples)
       ENVIRONMENT "LD_LIBRARY_PATH=${dlite_LD_LIBRARY_PATH_NATIVE}")
     set_property(TEST ${name} APPEND PROPERTY
       ENVIRONMENT "DLITE_USE_BUILD_ROOT=YES")
+    set_property(TEST ${name} PROPERTY
+      SKIP_RETURN_CODE 77)
   endforeach()
 
 endif()
diff --git a/examples/ex3/example-AlMgSi.json b/examples/ex3/example-AlMgSi.json
index d84e86256..adac2f111 100644
--- a/examples/ex3/example-AlMgSi.json
+++ b/examples/ex3/example-AlMgSi.json
@@ -1,10 +1,7 @@
 {
-  "d3d18712-3e8f-5f02-a1ea-ab147199c4f1": {
-    "meta": {
-      "name": "PhilibTable",
-      "version": "0.1",
-      "namespace": "http://onto-ns.com/meta/philib"
-    },
+  "d3d18712-3e8f-5f02-a1ea-ab147199c4f1":   {
+    "uri": "example-AlMgSi",
+    "meta": "http://onto-ns.com/meta/philib/0.1/PhilibTable",
     "dimensions": {
       "nelements": 3,
       "nphases": 2,
@@ -16,62 +13,18 @@
     },
     "properties": {
       "database": null,
-      "elements": [
-        "Al",
-        "Mg",
-        "Si"
-      ],
-      "phases": [
-        "FCC_A1",
-        "MG2SI"
-      ],
-      "phaseselementdep": [
-        0,
-        1
-      ],
-      "varnames": [
-        "T"
-      ],
-      "varranges": [
-        [
-          500.0,
-          800.0
-        ]
-      ],
-      "condnames": [
-        "X0(Mg)",
-        "X0(Si)"
-      ],
-      "condvalues": [
-        0.10000000000000001,
-        0.10000000000000001
-      ],
-      "calcnames": [
-        "fv(MG2SI)",
-        "X(MG2SI,Si)"
-      ],
-      "calcvalues": [
-        [
-          0.20000000000000001,
-          0.10000000000000001
-        ],
-        [
-          0.33000000000000002,
-          0.38
-        ]
-      ],
+      "elements": ["Al", "Mg", "Si"],
+      "phases": ["FCC_A1", "MG2SI"],
+      "phaseselementdep": [0, 1],
+      "varnames": ["T"],
+      "varranges": [[500, 800]],
+      "condnames": ["X0(Mg)", "X0(Si)"],
+      "condvalues": [0.1, 0.1],
+      "calcnames": ["fv(MG2SI)", "X(MG2SI,Si)"],
+      "calcvalues": [[0.2, 0.1], [0.33, 0.38]],
       "regular_grid": false,
-      "ticks": [
-        0
-      ],
-      "points": [
-        [
-          0.0
-        ],
-        [
-          0.0
-        ]
-      ]
+      "ticks": [0],
+      "points": [[0], [0]]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/examples/ex4/example-AlMgSi.json b/examples/ex4/example-AlMgSi.json
index d84e86256..16b60c491 100644
--- a/examples/ex4/example-AlMgSi.json
+++ b/examples/ex4/example-AlMgSi.json
@@ -74,4 +74,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/examples/ex5d/Plot PhilibTable.ipynb b/examples/ex5d/PlotPhilibTable.ipynb
similarity index 100%
rename from examples/ex5d/Plot PhilibTable.ipynb
rename to examples/ex5d/PlotPhilibTable.ipynb
diff --git a/examples/read-csv/main.py b/examples/read-csv/main.py
index 3753a6ff3..23aa90446 100644
--- a/examples/read-csv/main.py
+++ b/examples/read-csv/main.py
@@ -1,1 +1,7 @@
-import readcsv
+try:
+    import pandas  # noqa: F401
+except ImportError:
+    import sys
+    sys.exit(77)  # skip this test if pandas is not available
+
+import readcsv  # noqa: F401
diff --git a/examples/read-csv/python-storage-plugins/csv.py b/examples/read-csv/python-storage-plugins/csv.py
index f4a9af198..b372abe27 100644
--- a/examples/read-csv/python-storage-plugins/csv.py
+++ b/examples/read-csv/python-storage-plugins/csv.py
@@ -1,6 +1,4 @@
 """Storage plugin that reading/writing CSV files."""
-import sys
-import re
 import warnings
 import hashlib
 import ast
@@ -126,7 +124,7 @@ def infer_meta(data, metauri, uri):
         metauri = f'onto-ns.com/meta/1.0/generated_from_{fmt}_{hash}'
     elif dlite.has_instance(metauri):
         warnings.warn(f'csv option infer is true, but explicit instance id '
-                      '"{metauri}" already exists')
+                      f'"{metauri}" already exists')
 
     dims_ = [dlite.Dimension('rows', 'Number of rows.')]
     props = []
@@ -150,7 +148,6 @@ def optstring2keywords(optstring):
     s = '{%s}' % (optstring, )
     try:
         return ast.literal_eval(s)
-    except:
-        exc, val, tr = sys.exc_info()
+    except Exception as e:
         raise ValueError(
-            f'invalid in option string ({exc.__name__}): {optstring!r}')
+            f'invalid in option string ({e.__name__}): {optstring!r}')
diff --git a/examples/read-csv/readcsv.py b/examples/read-csv/readcsv.py
index ceb857a4b..5376c0968 100644
--- a/examples/read-csv/readcsv.py
+++ b/examples/read-csv/readcsv.py
@@ -1,5 +1,3 @@
-import os
-
 from config_paths import csvfile
 import dlite
 
@@ -18,7 +16,7 @@
 
 # ... or excel writer (requires openpyxl)
 try:
-    import openpyxl
+    import openpyxl  # noqa: F401
     csv.save('csv:newfile.xlsx')
 except ImportError:
     pass
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0b40e426b..94acf41fb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -42,7 +42,7 @@ set(sources
   dlite-mapping-plugins.c
   dlite-codegen.c
   dlite-getlicense.c
-  dlite-print.c
+  dlite-json.c
   getuuid.c
   pathshash.c
   triple.c
diff --git a/src/dlite-collection.c b/src/dlite-collection.c
index 2f5509936..7a2227889 100644
--- a/src/dlite-collection.c
+++ b/src/dlite-collection.c
@@ -11,6 +11,7 @@
 #include "dlite-schemas.h"
 #include "dlite-storage-plugins.h"
 #include "dlite-collection.h"
+#include "dlite.h"
 
 
 
@@ -22,9 +23,8 @@
 int dlite_collection_init(DLiteInstance *inst)
 {
   DLiteCollection *coll = (DLiteCollection *)inst;
-  assert(coll->nrelations == 0);
-  assert(coll->relations == NULL);
-  coll->rstore = triplestore_create();
+  if (coll->rstore) return errx(1, "triplestore already initialised");
+  if (!(coll->rstore = triplestore_create())) return 1;
   return 0;
 }
 
@@ -43,7 +43,7 @@ int dlite_collection_deinit(DLiteInstance *inst)
     if ((inst2 = dlite_instance_get(r->o))) {
       dlite_instance_decref(inst2);
     } else {
-      warn("cannot remove missing instance: %s", r->o);
+      warnx("cannot remove missing instance: %s", r->o);
     }
   }
   dlite_collection_deinit_state(&state);
@@ -63,12 +63,28 @@ int dlite_collection_getdim(const DLiteInstance *inst, size_t i)
 /* Loads instance relations to triplestore.  Returns -1 on error. */
 int dlite_collection_loadprop(const DLiteInstance *inst, size_t i)
 {
+  int retval = 0;
   DLiteCollection *coll = (DLiteCollection *)inst;
+  TripleState state;
+  const Triple *t;
   if (i != 0) return err(-1, "index out of range: %lu", (unsigned long)i);
   triplestore_clear(coll->rstore);
   if (triplestore_add_triples(coll->rstore, coll->relations, coll->nrelations))
     return -1;
-  return 0;
+
+  /* Iterate over all newly added triples and load instances corresponding
+     to "_has-uuid" relations. */
+  triplestore_init_state(coll->rstore, &state);
+  while ((t = triplestore_find(&state, NULL, "_has-uuid", NULL))) {
+    DLiteInstance *inst2 = dlite_instance_get(t->o);
+    if (!inst2) retval = errx(1, "cannot get instance \"%s\" labeled \"%s\" "
+                              "from collection \"%s\".  "
+                              "Is DLITE_STORAGES properly set?",
+                              t->o, t->s, coll->uuid);
+  }
+  triplestore_deinit_state(&state);
+
+  return retval;
 }
 
 /* Saves triplestore to instance relations. Returns non-zero on error. */
@@ -77,15 +93,20 @@ int dlite_collection_saveprop(DLiteInstance *inst, size_t i)
   DLiteCollection *coll = (DLiteCollection *)inst;
   TripleState state;
   const Triple *t;
-  size_t n = 0;
+  int n, j=0;
+
+  if ((n = dlite_instance_get_dimension_size_by_index(inst, i)) < 0) return -1;
   if (i != 0) return err(-1, "index out of range: %lu", (unsigned long)i);
+
   triplestore_init_state(coll->rstore, &state);
   while ((t = triplestore_next(&state))) {
-    assert(n < coll->nrelations);
-    triple_copy(coll->relations + n, t);
-    n++;
+    assert(j < (int)coll->nrelations);
+    triple_copy(coll->relations + j, t);
+    j++;
   }
   triplestore_deinit_state(&state);
+
+  assert(j == n);
   return 0;
 }
 
@@ -408,11 +429,14 @@ const DLiteInstance *dlite_collection_get(const DLiteCollection *coll,
   const DLiteRelation *r;
   if ((r = dlite_collection_find(coll, NULL, label, "_has-uuid", NULL))) {
     DLiteInstance *inst = dlite_instance_get(r->o);
-    assert(inst->_refcount >= 2);
-    dlite_instance_decref(inst);
+    //assert(inst->_refcount >= 2);
+    //dlite_instance_decref(inst);
+
+    /* FIXME - this looks like a hack, should be solved properly... */
+    if (inst->_refcount >= 2) dlite_instance_decref(inst);
     return inst;
   }
-  err(1, "cannot load instance '%s' from collection", label);
+  errx(1, "cannot load instance '%s' from collection", label);
   return NULL;
 }
 
@@ -444,7 +468,7 @@ DLiteInstance *dlite_collection_get_new(const DLiteCollection *coll,
   if (!inst) return NULL;
   if (metaid) {
     if (!(inst = dlite_mapping(metaid, (const DLiteInstance **)&inst, 1)))
-      err(1, "cannot map instance labeled '%s' to '%s'", label, metaid);
+      errx(1, "cannot map instance labeled '%s' to '%s'", label, metaid);
   } else
     dlite_instance_incref(inst);
   return inst;
diff --git a/src/dlite-entity.c b/src/dlite-entity.c
index 66930c7a8..af3a660a9 100644
--- a/src/dlite-entity.c
+++ b/src/dlite-entity.c
@@ -9,8 +9,7 @@
 #include "utils/map.h"
 #include "utils/fileutils.h"
 #include "utils/infixcalc.h"
-#define JSMN_HEADER
-#include "utils/jsmn.h"
+#include "utils/jsmnx.h"
 
 #include "dlite.h"
 #include "dlite-macros.h"
@@ -605,6 +604,7 @@ DLiteInstance *dlite_instance_has(const char *id, bool check_storages)
     ErrTry:
       if ((inst = dlite_instance_get(id)))
         dlite_instance_decref(inst);
+      assert(inst->_refcount > 0);
     ErrOther:
       break;
     ErrEnd;
@@ -617,9 +617,10 @@ DLiteInstance *dlite_instance_has(const char *id, bool check_storages)
   Returns a new reference to instance with given `id` or NULL if no such
   instance can be found.
 
-  If the instance exists in the in-memory store it is returned.
-  Otherwise it is searched for in the storage plugin path (initiated
-  from the DLITE_STORAGES environment variable).
+  If the instance exists in the in-memory store it is returned (with
+  its refcount increased by one).  Otherwise it is searched for in the
+  storage plugin path (initiated from the DLITE_STORAGES environment
+  variable).
 
   It is an error message if the instance cannot be found.
 */
@@ -1309,24 +1310,31 @@ int dlite_instance_is_metameta(const DLiteInstance *inst)
 
 
 /*
-  Updates dimension sizes from the getdim() method of
-  extended metadata.  Does nothing, if the metadata has no getdim() method.
+  Updates dimension sizes from internal state by calling the getdim()
+  method of extended metadata.  Does nothing, if the metadata has no
+  getdim() method.
 
   Returns non-zero on error.
  */
 int dlite_instance_sync_to_dimension_sizes(DLiteInstance *inst)
 {
-  int *dims=NULL, retval=1;
-  size_t i;
+  int n, retval=1, update=0, *newdims=NULL;
+  size_t i, *dims=DLITE_DIMS(inst);
   if (!inst->meta->_getdim) return 0;
-  if (!(dims = calloc(inst->meta->_ndimensions, sizeof(int))))
-    return err(1, "allocation failure");
-  for (i=0; i<inst->meta->_ndimensions; i++)
-    if ((dims[i] = inst->meta->_getdim(inst, i)) < 0) goto fail;
-  if (dlite_instance_set_dimension_sizes(inst, dims)) goto fail;
+  for (i=0; i<inst->meta->_ndimensions; i++) {
+    if ((n = inst->meta->_getdim(inst, i)) < 0) goto fail;
+    if (n != (int)dims[i]) update=1;
+  }
+  if (update) {
+    if (!(newdims = calloc(inst->meta->_ndimensions, sizeof(int))))
+      return err(1, "allocation failure");
+    for (i=0; i<inst->meta->_ndimensions; i++)
+      newdims[i] = inst->meta->_getdim(inst, i);
+    if (dlite_instance_set_dimension_sizes(inst, newdims)) goto fail;
+  }
   retval = 0;
  fail:
-  if (dims) free(dims);
+  if (newdims) free(newdims);
   return retval;
 }
 
@@ -1994,6 +2002,7 @@ DLiteMeta *dlite_meta_get(const char *id)
 DLiteMeta *dlite_meta_load(const DLiteStorage *s, const char *id)
 {
   DLiteInstance *inst = dlite_instance_load(s, id);
+  if (!inst) return NULL;
   if (!dlite_instance_is_meta(inst))
     return err(1, "not metadata: %s (%s)", s->location, id), NULL;
   return (DLiteMeta *)inst;
@@ -2423,8 +2432,8 @@ static int scandim(int d, const char *src, void **pptr,
   For arrays, `ptr` should points to the first element and will not be
   not dereferenced.  Evaluated dimension sizes are given by `dims`.
 
-  The `flags` provides some format options.  If zero (default) bools
-  and strings are expected to be quoted.
+  The `flags` provides some format options.  If zero (default)
+  strings are expected to be quoted.
 
   Returns number of characters consumed from `src` or a negative
   number on error.
@@ -2439,7 +2448,6 @@ int dlite_property_scan(const char *src, void *ptr, const DLiteProperty *p,
     jsmntok_t *tokens=NULL, *t;
     jsmn_parser parser;
     jsmn_init(&parser);
-    if (flags == dliteFlagDefault) flags = dliteFlagQuoted;
     r = jsmn_parse_alloc(&parser, src, strlen(src), &tokens, &ntokens);
     if (r < 0) return err(r, "error parsing input: %s", jsmn_strerror(r));
     t = tokens;
diff --git a/src/dlite-entity.h b/src/dlite-entity.h
index 5d5ce8dd9..4089e402a 100644
--- a/src/dlite-entity.h
+++ b/src/dlite-entity.h
@@ -337,6 +337,10 @@ typedef struct _DLiteMetaModel DLiteMetaModel;
 #define DLITE_PROP(inst, n) \
   ((void *)((char *)(inst) + ((DLiteInstance *)(inst))->meta->_propoffsets[n]))
 
+/** Expands to number of dimensions of property `n` --> (int) */
+#define DLITE_PROP_NDIM(inst, n) \
+  (((DLiteInstance *)(inst))->meta->_properties[n].ndims)
+
 /** Expands to array of dimension sizes for property `n` --> (size_t *) */
 #define DLITE_PROP_DIMS(inst, n) \
   ((size_t *)((char *)(inst) + \
@@ -486,9 +490,10 @@ DLiteInstance *dlite_instance_has(const char *id, bool check_storages);
   Returns a new reference to instance with given `id` or NULL if no such
   instance can be found.
 
-  If the instance exists in the in-memory store it is returned.
-  Otherwise it is searched for in the storage plugin path (initiated
-  from the DLITE_STORAGES environment variable).
+  If the instance exists in the in-memory store it is returned (with
+  its refcount increased by one).  Otherwise it is searched for in the
+  storage plugin path (initiated from the DLITE_STORAGES environment
+  variable).
 
   It is an error message if the instance cannot be found.
 */
@@ -701,8 +706,9 @@ int dlite_instance_is_metameta(const DLiteInstance *inst);
 
 
 /**
-  Help function that update dimension sizes from the getdim() method of
-  extended metadata.  Does nothing, if the metadata has no getdim() method.
+  Updates dimension sizes from internal state by calling the getdim()
+  method of extended metadata.  Does nothing, if the metadata has no
+  getdim() method.
 
   Returns non-zero on error.
  */
diff --git a/src/dlite-json.c b/src/dlite-json.c
new file mode 100644
index 000000000..d68442102
--- /dev/null
+++ b/src/dlite-json.c
@@ -0,0 +1,942 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "utils/err.h"
+#include "utils/compat.h"
+#include "utils/strutils.h"
+
+#include "dlite.h"
+#include "dlite-macros.h"
+#include "dlite-json.h"
+
+
+/* Expands to `a - b` if `a > b` else to `0`. */
+#define PDIFF(a, b) (((size_t)(a) > (size_t)(b)) ? (a) - (b) : 0)
+
+/* These macros could have been simplified with __VA_ARGS__, but not
+   all compilers support that... */
+#define PRINT(fmt)                                      \
+  do {                                                  \
+    int m = snprintf(dest+n, PDIFF(size, n), fmt);      \
+    if (m < 0) goto fail;                               \
+    n += m;                                             \
+  } while (0)
+#define PRINT1(fmt, a1)                                 \
+  do {                                                  \
+    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1);  \
+    if (m < 0) goto fail;                               \
+    n += m;                                             \
+  } while (0)
+#define PRINT2(fmt, a1, a2)                                     \
+  do {                                                          \
+    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1, a2);      \
+    if (m < 0) goto fail;                                       \
+    n += m;                                                     \
+  } while (0)
+#define PRINT3(fmt, a1, a2, a3)                                 \
+  do {                                                          \
+    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1, a2, a3);  \
+    if (m < 0) goto fail;                                       \
+    n += m;                                                     \
+  } while (0)
+#define PRINT4(fmt, a1, a2, a3, a4)                                     \
+  do {                                                                  \
+    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1, a2, a3, a4);      \
+    if (m < 0) goto fail;                                               \
+    n += m;                                                             \
+  } while (0)
+
+
+/* Forward declarations */
+static const jsmntok_t *nexttok(DLiteJsonIter *iter, int *length);
+
+
+/*
+  Serialise instance `inst` to `dest`, formatted as JSON.
+
+  No more than `size` bytes are written to `dest` (incl. the
+  terminating NUL).
+
+  Returns number of bytes written to `dest`.  If the output is
+  truncated because it exceeds `size`, the number of bytes that would
+  have been written if `size` was large enough is returned.  On error, a
+  negative value is returned.
+*/
+int dlite_json_sprint(char *dest, size_t size, const DLiteInstance *inst,
+                      int indent, DLiteJsonFlag flags)
+{
+  DLiteTypeFlag f = (DLiteTypeFlag)flags;
+  int n=0, ok=0, m, j;
+  size_t i;
+  char *in = malloc(indent + 1);
+  memset(in, ' ', indent);
+  in[indent] = '\0';
+
+  PRINT1("%s{\n", in);
+  if (flags & dliteJsonWithUuid)
+    PRINT2("%s  \"uuid\": \"%s\",\n", in, inst->uuid);
+  if (inst->uri)
+    PRINT2("%s  \"uri\": \"%s\",\n", in, inst->uri);
+  PRINT2("%s  \"meta\": \"%s\",\n", in, inst->meta->uri);
+
+  if ((flags & dliteJsonMetaAsData) || dlite_instance_is_data(inst)) {
+    /* Standard format */
+    PRINT1("%s  \"dimensions\": {\n", in);
+    for (i=0; i < inst->meta->_ndimensions; i++) {
+      char *name = inst->meta->_dimensions[i].name;
+      int val = DLITE_DIM(inst, i);
+      char *c = (i < inst->meta->_ndimensions - 1) ? "," : "";
+      PRINT4("%s    \"%s\": %d%s\n", in, name, val, c);
+    }
+    PRINT1("%s  },\n", in);
+
+    PRINT1("%s  \"properties\": {\n", in);
+    for (i=0; i < inst->meta->_nproperties; i++) {
+      char *c = (i < inst->meta->_nproperties - 1) ? "," : "";
+      DLiteProperty *p = inst->meta->_properties + i;
+      void *ptr = dlite_instance_get_property_by_index(inst, i);
+      size_t *dims = DLITE_PROP_DIMS(inst, i);
+      PRINT2("%s    \"%s\": ", in, p->name);
+      m = dlite_property_print(dest+n, PDIFF(size, n), ptr, p, dims, 0, -2, f);
+      if (m < 0) return -1;
+      n += m;
+      PRINT1("%s\n", c);
+    }
+    PRINT1("%s  }\n", in);
+
+  } else {
+    /* Special format for entities */
+    void *ptr;
+    DLiteMeta *met = (DLiteMeta *)inst;
+    if ((ptr = dlite_instance_get_property(inst, "description")))
+      PRINT2("%s  \"description\": \"%s\",\n", in, *(char **)ptr);
+
+    PRINT1("%s  \"dimensions\": [\n", in);
+    for (i=0; i < met->_ndimensions; i++) {
+      char *c = (i < met->_ndimensions - 1) ? "," : "";
+      DLiteDimension *d = met->_dimensions + i;
+      PRINT1("%s    {\n", in);
+      PRINT2("%s      \"name\": \"%s\"", in, d->name);
+      if (d->description)
+        PRINT2(",\n%s      \"description\": \"%s\"\n", in, d->description);
+      PRINT2("%s    }%s\n", in, c);
+    }
+    PRINT1("%s  ],\n", in);
+
+    PRINT1("%s  \"properties\": [\n", in);
+    for (i=0; i < met->_nproperties; i++) {
+      char typename[32];
+      char *c = (i < met->_nproperties - 1) ? "," : "";
+      DLiteProperty *p = met->_properties + i;
+      dlite_type_set_typename(p->type, p->size, typename, sizeof(typename));
+      PRINT1("%s    {\n", in);
+      PRINT2("%s      \"name\": \"%s\",\n", in, p->name);
+      PRINT2("%s      \"type\": \"%s\"", in, typename);
+      if (p->ndims) {
+        PRINT1(",\n%s      \"dims\": [", in);
+        for (j=0; j < p->ndims; j++) {
+          char *cc = (j < p->ndims - 1) ? ", " : "";
+          PRINT2("\"%s\"%s", p->dims[j], cc);
+        }
+        PRINT("]");
+      }
+      if (p->unit)
+        PRINT2(",\n%s      \"unit\": \"%s\"", in, p->unit);
+      if (p->description)
+        PRINT2(",\n%s      \"description\": \"%s\"", in, p->description);
+      PRINT2("\n%s    }%s\n", in, c);
+    }
+    PRINT1("%s  ]\n", in);
+  }
+
+  PRINT1("%s}", in);
+
+  ok = 1;
+ fail:
+  free(in);
+  return (ok) ? n : -1;
+}
+
+
+/*
+  Like dlite_json_sprint(), but prints to allocated buffer.
+
+  Prints to position `pos` in `*dest`, which should point to a buffer
+  of size `*size`.  `*dest` is reallocated if needed.
+
+  Returns number or bytes written or a negative number on error.
+*/
+int dlite_json_asprint(char **dest, size_t *size, size_t pos,
+                       const DLiteInstance *inst, int indent,
+                       DLiteJsonFlag flags)
+{
+  int m;
+  void *q;
+  size_t newsize;
+  if (!dest && !*dest) *size = 0;
+  m = dlite_json_sprint(*dest + pos, PDIFF(*size, pos), inst, indent, flags);
+  if (m < 0) return m;
+  if (m < (int)PDIFF(*size, pos)) return m;
+
+  /* Reallocate buffer to required size. */
+  newsize = m + pos + 1;
+  if (!(q = realloc(*dest, newsize))) return -1;
+  *dest = q;
+  *size = newsize;
+  m = dlite_json_sprint(*dest + pos, PDIFF(*size, pos), inst, indent, flags);
+  assert(0 <= m && m < (int)*size);
+  return m;
+}
+
+
+/*
+  Like dlite_json_sprint(), but returns allocated buffer with
+  serialised instance.
+*/
+char *dlite_json_aprint(const DLiteInstance *inst, int indent,
+                        DLiteJsonFlag flags)
+{
+  char *dest=NULL;
+  size_t size=0;
+  if ((dlite_json_asprint(&dest, &size, 0, inst, indent, flags)) < 0)
+    return NULL;
+  return dest;
+}
+
+/*
+  Like dlite_json_sprint(), but prints to stream `fp`.
+
+  Returns number or bytes printed or a negative number on error.
+*/
+int dlite_json_fprint(FILE *fp, const DLiteInstance *inst, int indent,
+                      DLiteJsonFlag flags)
+{
+  int m;
+  char *buf=NULL;
+  size_t size=0;
+  if ((m = dlite_json_asprint(&buf, &size, 0, inst, indent, flags)) >= 0) {
+    fprintf(fp, "%s\n", buf);
+    free(buf);
+  }
+  return m;
+}
+
+/*
+  Prints json representation of `inst` to standard output.
+
+  Returns number or bytes printed or a negative number on error.
+*/
+int dlite_json_print(const DLiteInstance *inst)
+{
+  return dlite_json_fprint(stdout, inst, 0, 0);
+}
+
+
+/*
+  Appends json representation of `inst` to json string pointed to by `*s`.
+
+  On input, `*s` should be a malloc'ed string representation of a json object.
+  It will be reallocated as needed.
+
+  `*size` if the allocated size of `*s`.  It will be updated when `*s`
+  is realocated.
+
+  Returns number or bytes inserted or a negative number on error.
+*/
+int dlite_json_append(char **s, size_t *size, const DLiteInstance *inst,
+                      DLiteJsonFlag flags)
+{
+  int r, n=0, retval=-1;
+  unsigned int ntokens=0;
+  jsmn_parser parser;
+  jsmntok_t *tokens=NULL;
+  size_t pos;
+
+  errno = 0;
+  jsmn_init(&parser);
+  if ((r = jsmn_parse_alloc(&parser, *s, *size, &tokens, &ntokens)) < 0)
+    FAIL1("error parsing json: %s", jsmn_strerror(r));
+  if (r == 0) FAIL("cannot append to empty json string");
+  if (tokens[0].type != JSMN_OBJECT)
+    FAIL("can only append to json object");
+
+  pos = tokens[0].end - 1;
+  assert(pos > 0);
+  /* Remove trailing spaces after the last item for prettier output.
+     Also accept malformed json input with a trailing comma after the
+     last item. */
+  while (isspace((*s)[pos-1]) || (*s)[pos-1] == ',') pos--;
+
+  if (tokens[0].size > 0 && (n = asnpprintf(s, size, pos, ",")) < 0)
+    goto fail;
+  pos += n;
+  if ((n = asnpprintf(s, size, pos, "\n  \"%s\": ", inst->uuid)) < 0)
+    goto fail;
+  pos += n;
+  if ((n = dlite_json_asprint(s, size, pos, inst, 2, flags)) < 0) goto fail;
+  pos += n;
+  if ((n = asnpprintf(s, size, pos, "\n}\n")) < 0) goto fail;
+  pos += n;
+  retval = pos - tokens[0].end;
+ fail:
+  free(tokens);
+  return retval;
+}
+
+
+/* ================================================================
+ * Scanning
+ * ================================================================ */
+
+/* Returns a malloc buffer with the URI of `obj` or NULL on error. */
+static char *get_uri(const char *src, const jsmntok_t *obj)
+{
+  const jsmntok_t *t, *t1, *t2, *t3;
+  if ((t = jsmn_item(src, obj, "uri")))
+    return strndup(src + t->start, t->end - t->start);
+  if ((t1 = jsmn_item(src, obj, "name")) &&
+      (t2 = jsmn_item(src, obj, "version")) &&
+      (t3 = jsmn_item(src, obj, "namespace"))) {
+    char *buf=NULL;
+    asprintf(&buf, "%.*s/%.*s/%.*s",
+             t3->end - t3->start, src + t3->start,
+             t2->end - t2->start, src + t2->start,
+             t1->end - t1->start, src + t1->start);
+    return buf;
+  }
+  return NULL;
+}
+
+/* Returns a malloc buffer with the URI of the metadata of `obj` or
+   NULL on error. */
+static char *get_meta_uri(const char *src, const jsmntok_t *obj)
+{
+  const jsmntok_t *item;
+  char *buf=NULL;
+  size_t size=0;
+  const char *s = src + obj->start;
+  int len = obj->end - obj->start;
+  if (!(item = jsmn_item(src, (jsmntok_t *)obj, "meta")))
+    FAIL2("no 'meta' in json repr. of instance: %.*s", len, s);
+
+  if (item->type == JSMN_OBJECT) {
+    if (!(buf = get_uri(src, item)))
+      FAIL2("invalid meta for object %.*s", len, s);
+  } else if (item->type == JSMN_STRING) {
+    strnput(&buf, &size, 0, src + item->start, item->end - item->start);
+  } else
+    return err(1, "\"meta\" in json repr. of instance should be either an "
+               "object or a string: %.*s", len, s), NULL;
+ fail:
+  return buf;
+}
+
+/* Writes the UUID of the instance represented by `obj` to `uuid`.
+   Returns non-zero on error. */
+static int get_meta_uuid(char *uuid, const char *src, const jsmntok_t *obj)
+{
+  int retval=1;
+  char *buf;
+  if (!(buf = get_meta_uri(src, obj))) goto fail;
+  if (dlite_get_uuid(uuid, buf) < 0) goto fail;
+  retval = 0;
+ fail:
+  free(buf);
+  return retval;
+}
+
+
+/*
+  Help function for parsing an instance.
+  - src: json source
+  - obj: jsmn representation of the json object to parse
+  - id:  id of `obj`.  If NULL, it will be inferred.  Used for error reporting
+
+  Returns new `instance` or NULL on error.
+*/
+static DLiteInstance *parse_instance(const char *src, jsmntok_t *obj,
+                                     const char *id)
+{
+  int ok=0;
+  const jsmntok_t *item, *t;
+  char *buf=NULL, *uri=NULL, *metauri=NULL, uuid[DLITE_UUID_LENGTH+1];
+  size_t i, size=0, *dims=NULL;
+  DLiteInstance *inst=NULL;
+  const DLiteMeta *meta=NULL;
+  jsmntype_t dimtype = 0;
+  char *name=NULL, *version=NULL, *namespace=NULL;
+
+  assert(obj->type == JSMN_OBJECT);
+
+  /* Get uri and uuid */
+  uri = get_uri(src, obj);
+  if (!uri && (item = jsmn_item(src, obj, "uuid"))) {
+    strncpy(uuid, src + item->start, DLITE_UUID_LENGTH);
+    uuid[DLITE_UUID_LENGTH] = '\0';
+  } else {
+    if (dlite_get_uuid(uuid, (uri) ? uri : id) < 0) goto fail;
+  }
+
+  /* Check id */
+  if (id && *id) {
+    char uuid2[DLITE_UUID_LENGTH+1];
+    if (dlite_get_uuid(uuid2, id) < 0) goto fail;
+    if (strcmp(uuid, uuid2) != 0)
+      FAIL3("instance has id \"%s\", expected \"%s\" (%s)", uuid, uuid2, id);
+  } else {
+    id = (uri) ? uri : uuid;
+  }
+
+  /* Get metadata */
+  if ((item = jsmn_item(src, obj, "meta"))) {
+    if (!(metauri = get_meta_uri(src, obj))) goto fail;
+    if (!(meta = dlite_meta_get(metauri)))
+      FAIL2("cannot find metadata '%s' when loading '%s' - please add the "
+            "right storage to DLITE_STORAGES and try again", metauri, id);
+  } else {
+    /* If "meta" is not given, we assume it is an entity. */
+    meta = dlite_get_entity_schema();  // borrowed reference
+    dlite_meta_incref((DLiteMeta *)meta);
+  }
+  assert(meta);
+
+  /* Allocate dimensions */
+  if (!(dims = calloc(meta->_ndimensions, sizeof(size_t))))
+    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 (t->type != JSMN_PRIMITIVE)
+          FAIL3("value '%.*s' of dimension should be an integer: %s",
+                t->end-t->start, src+t->start, id);
+        dims[i] = atoi(src + t->start);
+      }
+    } 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);
+    }
+  }
+
+  /* Create instance */
+  if (!(inst = dlite_instance_create(meta, dims, id))) goto fail;
+
+  /* Parse properties */
+  if (meta->_nproperties > 0) {
+    const jsmntok_t *base=obj;  // assigned to make cppcheck happy...
+    if (!(item = jsmn_item(src, obj, "properties")))
+      FAIL1("no \"properties\" in object %s", id);
+    if (dimtype && item->type != dimtype)
+      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);
+    assert(base->type == JSMN_OBJECT);
+
+    /* -- infer name, version and namespace */
+    if (dlite_instance_is_meta(inst)) {
+      if (dlite_split_meta_uri((uri) ? uri : id, &name, &version, &namespace))
+        FAIL1("cannot infer name, version and namespace from id: %s", id);
+    }
+
+    /* -- assign uri */
+    if (!inst->uri) {
+      char uuid2[DLITE_UUID_LENGTH+1];
+      if (uri)
+        inst->uri = strdup(uri);
+      else if (id && dlite_get_uuid(uuid2, id) > 0)
+        inst->uri = strdup(id);
+    }
+
+    /* -- read properties */
+    for (i=0; i < meta->_nproperties; i++) {
+      DLiteProperty *p = meta->_properties + i;
+      size_t *pdims = DLITE_PROP_DIMS(inst, i);
+      void *ptr = DLITE_PROP(inst, i);
+      if (DLITE_PROP_NDIM(inst, i) > 0) ptr = *(void **)ptr;
+      if ((t = jsmn_item(src, base, p->name))) {
+        strnput(&buf, &size, 0, src+t->start, t->end-t->start);
+        if (dlite_property_scan(buf, ptr, p, pdims, 0) < 0) goto fail;
+      //} else {
+      } else if (dlite_instance_is_meta(inst)) {
+        /* -- if not given, use inferred name, version and namespace */
+        if (strcmp(p->name, "name") == 0) {
+          if (dlite_property_scan(name, ptr, p, pdims, 0) < 0) goto fail;
+        } else if (strcmp(p->name, "version") == 0) {
+          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);
+      } 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);
+  if (version) free(version);
+  if (namespace) free(namespace);
+  if (dims) free(dims);
+  if (buf) free(buf);
+  if (uri) free(uri);
+  if (metauri) free(metauri);
+  if (!ok && inst) {
+    dlite_instance_decref(inst);
+    inst = NULL;
+  }
+  if (meta) dlite_meta_decref((DLiteMeta *)meta);
+  return inst;
+}
+
+
+/*
+  Returns a new instance scanned from `src`.
+
+  `id` is the uri or uuid of the instance to load.  If the string only
+  contain one instance (of the required metadata), `id` may be NULL.
+
+  If `metaid` is not NULL, it should be the URI or UUID of the
+  metadata of the returned instance.  It is an error if no such
+  instance exists in the source.
+
+  Returns the instance or NULL on error.
+*/
+DLiteInstance *dlite_json_sscan(const char *src, const char *id,
+                                const char *metaid)
+{
+  int i, r;
+  char *buf=NULL;
+  DLiteJsonIter *iter=NULL;
+  DLiteInstance *inst=NULL;
+  unsigned int ntokens=0;
+  jsmntok_t *tokens=NULL, *root;
+  jsmn_parser parser;
+  size_t srclen = strlen(src);
+  errno = 0;
+
+  jsmn_init(&parser);
+  r = jsmn_parse_alloc(&parser, src, srclen, &tokens, &ntokens);
+  if (r < 0) FAIL1("error parsing json: %s", jsmn_strerror(r));
+  root = tokens;
+  if (root->type != JSMN_OBJECT) FAIL("json root should be an object");
+
+  if (jsmn_item(src, root, "properties")) {
+    inst = parse_instance(src, root, id);
+  } else if (!id || !*id) {
+    int len;
+    if (!(iter = dlite_json_iter_create(src, srclen, metaid))) goto fail;
+    const jsmntok_t *t1 = nexttok(iter, &len);
+    const jsmntok_t *t2 = nexttok(iter, NULL);
+    if (!t1) {
+      if (metaid)
+        FAIL1("json source has no instance with meta id: '%s'", metaid);
+      else
+        FAIL("no instances in json source");
+    }
+    if (t2) FAIL("`id` (or `metaid`) is required when scanning json input "
+                 "with multiple instances");
+    jsmntok_t *val = (jsmntok_t *)t1 + 1;
+    buf = strndup(src + t1->start, t1->end - t1->start);
+    inst = parse_instance(src, val, buf);
+  } else {
+    int n=1;
+    char uuid[DLITE_UUID_LENGTH+1];
+    if (dlite_get_uuid(uuid, id) < 0) goto fail;
+    for (i=0; i < root->size; i++) {
+      char uuid2[DLITE_UUID_LENGTH+1];
+      jsmntok_t *key = root + n;
+      jsmntok_t *val = root + n+1;
+      int len = key->end - key->start;
+      if (key->type != JSMN_STRING) FAIL("expect json keys to be strings");
+      buf = strndup(src+key->start, len);
+      if (dlite_get_uuid(uuid2, buf) < 0) goto fail;
+      free(buf);
+      buf = NULL;
+      if (strcmp(uuid2, uuid) == 0) {
+        if (!(inst = parse_instance(src, val, id))) goto fail;
+        break;
+      }
+      n += jsmn_count(val) + 2;
+    }
+  }
+  if (!inst) goto fail;
+
+  if (metaid) {
+    char uuid[DLITE_UUID_LENGTH + 1];
+    if (dlite_get_uuid(uuid, metaid) < 0 ||
+        (strcmp(metaid, uuid) != 0 && strcmp(metaid, inst->meta->uri) != 0)) {
+      if (!id) id = (inst->iri) ? inst->iri : inst->uuid;
+      err(1, "instance '%s' has meta id '%s' but '%s' is expected",
+          id, inst->meta->uri, metaid);
+      dlite_instance_decref(inst);
+      inst = NULL;
+    }
+  }
+
+ fail:
+  free(tokens);
+  if (buf) free(buf);
+  if (iter) dlite_json_iter_free(iter);
+
+  return inst;
+}
+
+
+/*
+  Like dlite_json_sscan(), but scans instance `id` from stream `fp` instead
+  of a string.
+
+  Returns the instance or NULL on error.
+*/
+DLiteInstance *dlite_json_fscan(FILE *fp, const char *id, const char *metaid)
+{
+  DLiteInstance *inst;
+  char *buf;
+  if (!(buf = fu_readfile(fp))) return NULL;
+  inst = dlite_json_sscan(buf, id, metaid);
+  free(buf);
+  return inst;
+}
+
+
+/*
+  Like dlite_json_sscan(), but scans instance `id` from file
+  `filename` instead of a string.
+
+  Returns the instance or NULL on error.
+*/
+DLiteInstance *dlite_json_scanfile(const char *filename, const char *id,
+                                   const char *metaid)
+{
+  DLiteInstance *inst;
+  FILE *fp;
+  char *buf;
+  if (!(fp = fopen(filename, "r")))
+    FAIL1("cannot open storage \"%s\"", filename);
+  if (!(buf = fu_readfile(fp))) goto fail;
+  if (!(inst = dlite_json_sscan(buf, id, metaid))) {
+    /* write full error message */
+    char *msg=NULL;
+    size_t size=0, n=0;
+    n += asnpprintf(&msg, &size, n, "error loading instance ");
+    if (id) n += asnpprintf(&msg, &size, n, "with id \"%s\" ", id);
+    if (metaid) n += asnpprintf(&msg, &size, n, "of type \"%s\" ", metaid);
+    n += asnpprintf(&msg, &size, n, "from file \"%s\"", filename);
+    errx(1, "%s", msg);
+    free(msg);
+  }
+ fail:
+  if (fp) fclose(fp);
+  if (buf) free(buf);
+  return inst;
+}
+
+
+/* ================================================================
+ * Iteration
+ * ================================================================ */
+
+/* Iterator struct */
+struct _DLiteJsonIter {
+  const char *src;        /*!< Source document to search */
+  jsmntok_t *tokens;      /*!< Allocated tokens */
+  unsigned int ntokens;   /*!< Number of allocated tokens */
+  const jsmntok_t *t;     /*!< Pointer to current token */
+  unsigned int n;         /*!< Current token number */
+  unsigned int size;      /*!< Size of the root object */
+  char metauuid[DLITE_UUID_LENGTH+1];  /*!< UUID of metadata */
+};
+
+
+/*
+  Search for instances in the JSON document provided to dlite_json_iter_create()
+  and returns a pointer to the corresponding jsmn token.
+
+  Returns NULL if there are no more tokens left.
+
+  See also dlite_json_next().
+*/
+static const jsmntok_t *nexttok(DLiteJsonIter *iter, int *length)
+{
+  while (iter->n < iter->size) {
+    const jsmntok_t *t = iter->t;
+    if (length) *length = t->end - t->start;
+    iter->t += jsmn_count(t + 1) + 2;
+    iter->n++;
+    if (*iter->metauuid) {
+      char uuid[DLITE_UUID_LENGTH+1];
+      const jsmntok_t *v = t + 1;
+      get_meta_uuid(uuid, iter->src, v);
+      if (strncmp(uuid, iter->metauuid, DLITE_UUID_LENGTH) == 0) return t;
+    } else
+      return t;
+  }
+  return NULL;
+}
+
+/*
+  Creates and returns a new iterator used by dlite_json_next().
+
+  Arguments
+  - src: input JSON string to search.
+  - length: length of `src`.  If zero or negative, all of `src` will be used.
+  - metaid: limit the search to instances of metadata with this id.
+
+  The source should be a JSON object with keys being instance UUIDs
+  and values being the JSON representation of the individual instances.
+
+  Returns new iterator or NULL on error.
+*/
+DLiteJsonIter *dlite_json_iter_create(const char *src, int length,
+                                      const char *metaid)
+{
+  int r, ok=0;
+  DLiteJsonIter *iter=NULL;
+  jsmn_parser parser;
+
+  if (!(iter = calloc(1, sizeof(DLiteJsonIter)))) FAIL("allocation failure");
+
+  if (length <= 0) length = strlen(src);
+  jsmn_init(&parser);
+  r = jsmn_parse_alloc(&parser, src, length, &iter->tokens, &iter->ntokens);
+  if (r < 0) FAIL1("error parsing json: %s", jsmn_strerror(r));
+  if (r == 0) goto fail;
+  if (iter->tokens->type != JSMN_OBJECT) FAIL("json root should be an object");
+  iter->src = src;
+  iter->t = iter->tokens + 1;
+  iter->size = iter->tokens->size;
+  if (metaid && dlite_get_uuid(iter->metauuid, metaid) < 0) goto fail;
+
+  ok=1;
+ fail:
+  if (!ok) dlite_json_iter_free(iter);
+  return iter;
+}
+
+/*
+  Free's iterator created with dlite_json_iter_create().
+*/
+void dlite_json_iter_free(DLiteJsonIter *iter)
+{
+  if (iter) {
+    if (iter->tokens) free(iter->tokens);
+    free(iter);
+  }
+}
+
+/*
+  Search for instances in the JSON document provided to dlite_json_iter_create()
+  and returns a pointer to instance UUIDs.
+
+  `iter` should be an iterator created with dlite_json_iter_create().
+
+  If `length` is given, it is set to the length of the returned identifier.
+
+  Returns a pointer to the next matching UUID or NULL if there are no more
+  matches left.
+*/
+const char *dlite_json_next(DLiteJsonIter *iter, int *length)
+{
+  const jsmntok_t *t = nexttok(iter, length);
+  if (t) return iter->src + t->start;
+  return NULL;
+}
+
+
+
+/* ================================================================
+ * JSON store
+ * ================================================================ */
+
+/* Iterater struct */
+struct _DLiteJStoreIter {
+  JStoreIter jiter;                    /*!< jstore iterater */
+  char metauuid[DLITE_UUID_LENGTH+1];  /*!< UUID of metadata */
+  jsmntok_t *tokens;                   /*!< pointer to allocated tokens */
+  unsigned int ntokens;                /*!< number of allocated tokens */
+};
+
+
+/*
+  Load content of json string `src` to json store `js`.
+  `len` is the length of `src`.
+
+  Returns json format or -1 on error.
+ */
+DLiteJsonFormat dlite_jstore_loads(JStore *js, const char *src, int len)
+{
+  jsmn_parser parser;
+  jsmntok_t *tokens=NULL;
+  unsigned int ntokens=0;
+  char uuid[DLITE_UUID_LENGTH+1], *uri=NULL;
+  int r;
+  DLiteJsonFormat retval=-1;
+  char *dots = (len > 30) ? "..." : "";
+  jsmn_init(&parser);
+  if ((r = jsmn_parse_alloc(&parser, src, len, &tokens, &ntokens)) < 0)
+    FAIL3("error parsing json string: \"%.30s%s\": %s",
+          src, dots, jsmn_strerror(r));
+  if (tokens->type != JSMN_OBJECT)
+    FAIL2("root of json data must be an object: \"%.30s%s\"", src, dots);
+
+  if (jsmn_item(src, tokens, "properties")) {
+    /* metadata format */
+    if (!(uri = get_uri(src, tokens)))
+      FAIL2("missing uri in metadata-formatted json data: \"%.30s%s\"",
+            src, dots);
+    if (dlite_get_uuid(uuid, uri) < 0) goto fail;
+    jstore_addn(js, uuid, DLITE_UUID_LENGTH, src, len);
+    retval = dliteJsonMetaFormat;
+  } else {
+    /* data format */
+    jsmntok_t *t = tokens + 1;
+    int i;
+    for (i=0; i < tokens->size; i++) {
+      jsmntok_t *v = t+1;
+      if (dlite_get_uuidn(uuid, src + t->start, t->end - t->start) < 0)
+        goto fail;
+      if (jstore_addn(js, uuid, DLITE_UUID_LENGTH,
+                      src + v->start, v->end - v->start)) goto fail;
+      t += jsmn_count(v) + 2;
+    }
+    retval = dliteJsonDataFormat;
+  }
+ fail:
+  if (tokens) free(tokens);
+  if (uri) free(uri);
+  return retval;
+}
+
+/*
+  Read content of `filename` to json store `js`.
+
+  Returns json format or -1 on error.
+ */
+DLiteJsonFormat dlite_jstore_loadf(JStore *js, const char *filename)
+{
+  char *buf = jstore_readfile(filename);
+  int fmt;
+  if (!buf) return err(1, "cannot load json file \"%s\"", filename);
+  fmt = dlite_jstore_loads(js, buf, strlen(buf));
+  free(buf);
+  return fmt;
+}
+
+/*
+  Add json representation of `inst` to json store `js`.
+
+  Returns non-zero on error.
+ */
+int dlite_jstore_add(JStore *js, const DLiteInstance *inst, DLiteJsonFlag flags)
+{
+  char *buf;
+  if (!(buf = dlite_json_aprint(inst, 2, flags))) return -1;
+  return jstore_addstolen(js, inst->uuid, buf);
+}
+
+/*
+  Removes instance with given id from json store `js`.
+
+  Returns non-zero on error.
+ */
+int dlite_jstore_remove(JStore *js, const char *id)
+{
+  return jstore_remove(js, id);
+}
+
+/*
+  Initiate iterator `init` from json store `js`.
+  If `metaid` is provided, the iterator will only iterate over instances
+  of this metadata.
+
+  Returns a new iterator or NULL on error.
+ */
+DLiteJStoreIter *dlite_jstore_iter_create(JStore *js, const char *metaid)
+{
+  DLiteJStoreIter *iter;
+  if (!(iter = calloc(1, sizeof(DLiteJStoreIter))))
+    return err(1, "allocation failure"), NULL;
+  if (jstore_iter_init(js, &iter->jiter)) return NULL;
+  if (metaid && dlite_get_uuid(iter->metauuid, metaid) < 0) return NULL;
+  return iter;
+}
+
+/*
+  Free iterater.  Returns non-zero on error.
+*/
+int dlite_jstore_iter_free(DLiteJStoreIter *iter)
+{
+  jstore_iter_deinit(&iter->jiter);
+  if (iter->tokens) free(iter->tokens);
+  free(iter);
+  return 0;
+}
+
+/*
+  Return the id of the next instance in the json store or NULL if the
+  iterator is exausted.
+ */
+const char *dlite_jstore_iter_next(DLiteJStoreIter *iter)
+{
+  const char *iid;
+  JStore *js = iter->jiter.js;
+  jsmn_parser parser;
+  jsmn_init(&parser);
+  while ((iid = jstore_iter_next(&iter->jiter))) {
+    if (iter->metauuid[0]) {
+      char metauuid[DLITE_UUID_LENGTH+1];
+      const char *val = jstore_get(js, iid);
+      if (jsmn_parse_alloc(&parser, val, strlen(val),
+                           &iter->tokens, &iter->ntokens) < 0) {
+        err(-1, "invalid json input: \"%s\"", val);
+        continue;
+      }
+      if (get_meta_uuid(metauuid, val, iter->tokens)) {
+        err(-1, "json input has no meta uri: \"%s\"", val);
+        continue;
+      }
+      if (strcmp(metauuid, iter->metauuid)) continue;
+    }
+    return iid;
+  }
+  return NULL;
+}
diff --git a/src/dlite-json.h b/src/dlite-json.h
new file mode 100644
index 000000000..120150f3f
--- /dev/null
+++ b/src/dlite-json.h
@@ -0,0 +1,260 @@
+/**
+  @file
+  @brief Provides built-in support for JSON in dlite
+
+  A set of utility function for serialising and deserialising dlite
+  instances to/from JSON.
+*/
+#ifndef _DLITE_JSON_H
+#define _DLITE_JSON_H
+
+#include "utils/jstore.h"
+#include "utils/jsmnx.h"
+
+
+/** Flags for serialisation */
+typedef enum {
+  dliteJsonWithUuid=1,    /*!< Whether to include uuid in output */
+  dliteJsonMetaAsData=2   /*!< Whether to write metadata as data */
+} DLiteJsonFlag;
+
+/** JSON formats */
+typedef enum {
+  dliteJsonDataFormat,    /*!< Data format - single item */
+  dliteJsonMetaFormat     /*!< Metadata format - multiple items */
+} DLiteJsonFormat;
+
+
+///** Iterater struct */
+//typedef struct _DLiteJsonIter {
+//  JStoreIter jiter;                    /*!< jstore iterater */
+//  char metauuid[DLITE_UUID_LENGTH+1];  /*!< UUID of metadata */
+//  jsmntok_t *tokens;                   /*!< pointer to allocated tokens */
+//  unsigned int ntokens;                /*!< number of allocated tokens */
+//} DLiteJsonIter;
+
+
+/**
+ * @name Serilisation
+ */
+/** @{ */
+
+
+/**
+  Serialise instance `inst` to `dest`, formatted as JSON.
+
+  No more than `size` bytes are written to `dest` (incl. the
+  terminating NUL).
+
+  Returns number of bytes written to `dest`.  If the output is
+  truncated because it exceeds `size`, the number of bytes that would
+  have been written if `size` was large enough is returned.  On error, a
+  negative value is returned.
+*/
+int dlite_json_sprint(char *dest, size_t size, const DLiteInstance *inst,
+                      int indent, DLiteJsonFlag flags);
+
+/**
+  Like dlite_sprint(), but prints to allocated buffer.
+
+  Prints to position `pos` in `*dest`, which should point to a buffer
+  of size `*size`.  `*dest` is reallocated if needed.
+
+  Returns number or bytes written or a negative number on error.
+ */
+int dlite_json_asprint(char **dest, size_t *size, size_t pos,
+                       const DLiteInstance *inst, int indent,
+                       DLiteJsonFlag flags);
+
+/**
+  Like dlite_sprint(), but returns allocated buffer with serialised instance.
+ */
+char *dlite_json_aprint(const DLiteInstance *inst, int indent,
+                        DLiteJsonFlag flags);
+
+/**
+  Like dlite_sprint(), but prints to stream `fp`.
+
+  Returns number or bytes printed or a negative number on error.
+ */
+int dlite_json_fprint(FILE *fp, const DLiteInstance *inst, int indent,
+                      DLiteJsonFlag flags);
+
+/**
+  Prints json representation of `inst` to standard output.
+
+  Returns number or bytes printed or a negative number on error.
+*/
+int dlite_json_print(const DLiteInstance *inst);
+
+/**
+  Appends json representation of `inst` to json string pointed to by `*s`.
+
+  On input, `*s` should be a malloc'ed string representation of a json object.
+  It will be reallocated as needed.
+
+  `*size` if the allocated size of `*s`.  It will be updated when `*s`
+  is realocated.
+
+  Returns number or bytes inserted or a negative number on error.
+ */
+int dlite_json_append(char **s, size_t *size, const DLiteInstance *inst,
+                      DLiteJsonFlag flags);
+
+
+/** @} */
+/**
+ * @name Deserialisation
+ */
+/** @{ */
+
+/**
+  Returns a new instance scanned from `src`.
+
+  `id` is the uri or uuid of the instance to load.  If `src` only
+  contain one instance (of the required metadata), `id` may be NULL.
+
+  If `metaid` is not NULL, it should be the URI or UUID of the
+  metadata of the returned instance.  It is an error if no such
+  instance exists in the source.
+
+  Returns the instance or NULL on error.
+ */
+DLiteInstance *dlite_json_sscan(const char *src, const char *id,
+                                const char *metaid);
+
+/**
+  Like dlite_sscan(), but scans instance `id` from stream `fp` instead
+  of a string.
+
+  Returns the instance or NULL on error.
+ */
+DLiteInstance *dlite_json_fscan(FILE *fp, const char *id, const char *metaid);
+
+/**
+  Like dlite_json_sscan(), but scans instance `id` from file
+  `filename` instead of a string.
+
+  Returns the instance or NULL on error.
+*/
+DLiteInstance *dlite_json_scanfile(const char *filename, const char *id,
+                                   const char *metaid);
+
+
+
+
+/** @} */
+/**
+ * @name Iterator
+ */
+/** @{ */
+
+/** Opaque iterator struct */
+typedef struct _DLiteJsonIter DLiteJsonIter;
+
+/**
+  Creates and returns a new iterator used by dlite_json_next().
+
+  Arguments
+  - src: input JSON string to search.
+  - length: length of `src`.  If zero or negative, all of `src` will be used.
+  - metaid: limit the search to instances of metadata with this id.
+
+  The source should be a JSON object with keys being instance UUIDs
+  and values being the JSON representation of the individual instances.
+
+  Returns a new iterator or NULL on error.
+ */
+DLiteJsonIter *dlite_json_iter_create(const char *src, int length,
+                                      const char *metaid);
+
+/**
+  Free's iterator created with dlite_json_iter_create().
+ */
+void dlite_json_iter_free(DLiteJsonIter *iter);
+
+/**
+  Search for instances in the JSON document provided to dlite_json_iter_create()
+  and returns a pointer to instance UUIDs.
+
+  `iter` should be an iterator created with dlite_json_iter_create().
+
+  If `length` is given, it is set to the length of the returned identifier.
+
+  Returns a pointer to the next matching UUID or NULL if there are no more
+  matches left.
+ */
+const char *dlite_json_next(DLiteJsonIter *iter, int *length);
+
+
+
+/** @} */
+/**
+ * @name JSON store
+ */
+/** @{ */
+
+
+/**
+  Load content of json string `src` to json store `js`.
+  `len` is the length of `src`.
+
+  Returns json format or -1 on error.
+ */
+DLiteJsonFormat dlite_jstore_loads(JStore *js, const char *src, int len);
+
+/**
+  Read content of `filename` to json store `js`.
+
+  Returns json format or -1 on error.
+ */
+DLiteJsonFormat dlite_jstore_loadf(JStore *js, const char *filename);
+
+/** Opaque iterator struct */
+typedef struct _DLiteJStoreIter DLiteJStoreIter;
+
+/**
+  Add json representation of `inst` to json store `js`.
+
+  Returns non-zero on error.
+ */
+int dlite_jstore_add(JStore *js, const DLiteInstance *inst,
+                     DLiteJsonFlag flags);
+
+/**
+  Removes instance with given id from json store `js`.
+
+  Returns non-zero on error.
+ */
+int dlite_jstore_remove(JStore *js, const char *id);
+
+/**
+  Initiate iterator `init` from json store `js`.
+  If `metaid` is provided, the iterator will only iterate over instances
+  of this metadata.
+
+  Returns a new iterator or NULL on error.
+ */
+DLiteJStoreIter *dlite_jstore_iter_create(JStore *js, const char *metaid);
+
+/**
+  Deinitialises iterater.
+
+  Return non-zero on error.
+*/
+int dlite_jstore_iter_free(DLiteJStoreIter *iter);
+
+/**
+  Return the id of the next instance in the json store or NULL if the
+  iterator is exausted.
+ */
+const char *dlite_jstore_iter_next(DLiteJStoreIter *iter);
+
+
+
+
+
+/** @} */
+
+
+#endif /*_ DLITE_JSON_H */
diff --git a/src/dlite-mapping-plugins.c b/src/dlite-mapping-plugins.c
index 90612323a..9cb9f051c 100644
--- a/src/dlite-mapping-plugins.c
+++ b/src/dlite-mapping-plugins.c
@@ -302,9 +302,9 @@ int dlite_mapping_plugin_path_appendn(const char *path, size_t n)
 
   Returns non-zero on error.
 */
-int dlite_mapping_plugin_path_remove(int n)
+int dlite_mapping_plugin_path_delete(int n)
 {
   PluginInfo *info;
   if (!(info = get_mapping_plugin_info())) return 1;
-  return plugin_path_remove(info, n);
+  return plugin_path_delete(info, n);
 }
diff --git a/src/dlite-mapping-plugins.h b/src/dlite-mapping-plugins.h
index 0b3ce9795..cdab8032e 100644
--- a/src/dlite-mapping-plugins.h
+++ b/src/dlite-mapping-plugins.h
@@ -164,7 +164,7 @@ int dlite_mapping_plugin_path_appendn(const char *path, size_t n);
 
   Returns non-zero on error.
 */
-int dlite_mapping_plugin_path_remove(int n);
+int dlite_mapping_plugin_path_delete(int n);
 
 
 /** @} */
diff --git a/src/dlite-misc.c b/src/dlite-misc.c
index 10c6d2fdf..9bc68e52f 100644
--- a/src/dlite-misc.c
+++ b/src/dlite-misc.c
@@ -71,6 +71,14 @@ int dlite_get_uuid(char *buff, const char *id)
   return getuuid(buff, id);
 }
 
+/*
+  Like dlite_get_uuid(), but takes the the length of `id` as an
+  additional parameter.
+ */
+int dlite_get_uuidn(char *buff, const char *id, size_t len)
+{
+  return getuuidn(buff, id, len);
+}
 
 /*
   Returns an unique uri for metadata defined by `name`, `version`
@@ -296,8 +304,10 @@ int dlite_split_url_winpath(char *url, char **driver, char **location,
 
   /* assign driver and location */
   i = strcspn(url, ":/");
-  if (winpath && strlen(url) > 3 && (url[0] == 'C' || url[0] == 'c') &&
-      url[1] == ':' && (url[2] == '\\' || url[2] == '/')) {
+  if (winpath && strlen(url) > 3 &&
+      ((url[0] >= 'A' && url[0] <= 'Z') || (url[0] >= 'a' && url[0] <= 'z')) &&
+      url[1] == ':' &&
+      (url[2] == '\\' || url[2] == '/')) {
     /* special case: url is a windows path */
     if (driver) *driver = NULL;
     if (location) *location = url;
diff --git a/src/dlite-misc.h b/src/dlite-misc.h
index 49a356577..40619fb43 100644
--- a/src/dlite-misc.h
+++ b/src/dlite-misc.h
@@ -47,6 +47,12 @@ FUPlatform dlite_get_platform(void);
  */
 int dlite_get_uuid(char *buff, const char *id);
 
+/**
+  Like dlite_get_uuid(), but takes the the length of `id` as an
+  additional parameter.
+ */
+int dlite_get_uuidn(char *buff, const char *id, size_t len);
+
 
 /**
   Returns an unique uri for metadata defined by `name`, `version`
diff --git a/src/dlite-print.c b/src/dlite-print.c
deleted file mode 100644
index 344a84eea..000000000
--- a/src/dlite-print.c
+++ /dev/null
@@ -1,548 +0,0 @@
-#include <assert.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include "utils/err.h"
-#include "utils/compat.h"
-#include "utils/strutils.h"
-#define JSMN_HEADER
-#include "utils/jsmn.h"
-
-#include "dlite.h"
-#include "dlite-macros.h"
-
-
-/* Expands to `a - b` if `a > b` else to `0`. */
-#define PDIFF(a, b) (((size_t)(a) > (size_t)(b)) ? (a) - (b) : 0)
-
-/* These macros could have been simplified with __VA_ARGS__, but not
-   all compilers support that... */
-#define PRINT(fmt)                                      \
-  do {                                                  \
-    int m = snprintf(dest+n, PDIFF(size, n), fmt);      \
-    if (m < 0) goto fail;                               \
-    n += m;                                             \
-  } while (0)
-#define PRINT1(fmt, a1)                                 \
-  do {                                                  \
-    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1);  \
-    if (m < 0) goto fail;                               \
-    n += m;                                             \
-  } while (0)
-#define PRINT2(fmt, a1, a2)                                     \
-  do {                                                          \
-    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1, a2);      \
-    if (m < 0) goto fail;                                       \
-    n += m;                                                     \
-  } while (0)
-#define PRINT3(fmt, a1, a2, a3)                                 \
-  do {                                                          \
-    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1, a2, a3);  \
-    if (m < 0) goto fail;                                       \
-    n += m;                                                     \
-  } while (0)
-#define PRINT4(fmt, a1, a2, a3, a4)                                 \
-  do {                                                              \
-    int m = snprintf(dest+n, PDIFF(size, n), fmt, a1, a2, a3, a4);  \
-    if (m < 0) goto fail;                                           \
-    n += m;                                                         \
-  } while (0)
-
-
-/* Iterator struct */
-struct _DLiteJsonIter {
-  const char *src;        /*!< Source document to search */
-  jsmntok_t *tokens;      /*!< Allocated tokens */
-  unsigned int ntokens;   /*!< Number of allocated tokens */
-  const jsmntok_t *t;     /*!< Pointer to current token */
-  unsigned int n;         /*!< Current token number */
-  char metauuid[DLITE_UUID_LENGTH+1];  /*!< UUID of metadata */
-};
-
-
-/*
-  Serialise instance `inst` to `dest`, formatted as JSON.
-
-  No more than `size` bytes are written to `dest` (incl. the
-  terminating NUL).
-
-  Returns number of bytes written to `dest`.  If the output is
-  truncated because it exceeds `size`, the number of bytes that would
-  have been written if `size` was large enough is returned.  On error, a
-  negative value is returned.
-*/
-int dlite_json_sprint(char *dest, size_t size, DLiteInstance *inst,
-                      int indent, DLiteJsonFlag flags)
-{
-  DLiteTypeFlag f = (DLiteTypeFlag)flags;
-  int n=0, ok=0, m, j;
-  size_t i;
-  char *in = malloc(indent + 1);
-  memset(in, ' ', indent);
-  in[indent] = '\0';
-
-  PRINT1("%s{\n", in);
-  if (flags & dliteJsonUuid)
-    PRINT2("%s  \"uuid\": \"%s\",\n", in, inst->uuid);
-  if (inst->uri)
-    PRINT2("%s  \"uri\": \"%s\",\n", in, inst->uri);
-  PRINT2("%s  \"meta\": \"%s\",\n", in, inst->meta->uri);
-
-  if ((flags & dliteJsonMetaAsData) || dlite_instance_is_data(inst)) {
-    /* Standard format */
-    PRINT1("%s  \"dimensions\": {\n", in);
-    for (i=0; i < inst->meta->_ndimensions; i++) {
-      char *name = inst->meta->_dimensions[i].name;
-      int val = DLITE_DIM(inst, i);
-      char *c = (i < inst->meta->_ndimensions - 1) ? "," : "";
-      PRINT4("%s    \"%s\": %d%s\n", in, name, val, c);
-    }
-    PRINT1("%s  },\n", in);
-
-    PRINT1("%s  \"properties\": {\n", in);
-    for (i=0; i < inst->meta->_nproperties; i++) {
-      char *c = (i < inst->meta->_nproperties - 1) ? "," : "";
-      DLiteProperty *p = inst->meta->_properties + i;
-      void *ptr = dlite_instance_get_property_by_index(inst, i);
-      size_t *dims = DLITE_PROP_DIMS(inst, i);
-      PRINT2("%s    \"%s\": ", in, p->name);
-      m = dlite_property_print(dest+n, PDIFF(size, n), ptr, p, dims, 0, -2, f);
-      if (m < 0) return -1;
-      n += m;
-      PRINT1("%s\n", c);
-    }
-    PRINT1("%s  }\n", in);
-
-  } else {
-    /* Special format for entities */
-    void *ptr;
-    DLiteMeta *met = (DLiteMeta *)inst;
-    if ((ptr = dlite_instance_get_property(inst, "description")))
-      PRINT2("%s  \"description\": \"%s\",\n", in, *(char **)ptr);
-
-    PRINT1("%s  \"dimensions\": [\n", in);
-    for (i=0; i < met->_ndimensions; i++) {
-      char *c = (i < met->_ndimensions - 1) ? "," : "";
-      DLiteDimension *d = met->_dimensions + i;
-      PRINT1("%s    {\n", in);
-      PRINT2("%s      \"name\": \"%s\"", in, d->name);
-      if (d->description)
-        PRINT2(",\n%s      \"description\": \"%s\"\n", in, d->description);
-      PRINT2("%s    }%s\n", in, c);
-    }
-    PRINT1("%s  ],\n", in);
-
-    PRINT1("%s  \"properties\": [\n", in);
-    for (i=0; i < met->_nproperties; i++) {
-      char typename[32];
-      char *c = (i < met->_nproperties - 1) ? "," : "";
-      DLiteProperty *p = met->_properties + i;
-      dlite_type_set_typename(p->type, p->size, typename, sizeof(typename));
-      PRINT1("%s    {\n", in);
-      PRINT2("%s      \"name\": \"%s\",\n", in, p->name);
-      PRINT2("%s      \"type\": \"%s\"", in, typename);
-      if (p->ndims) {
-        PRINT1(",\n%s      \"dims\": [", in);
-        for (j=0; j < p->ndims; j++) {
-          char *cc = (j < p->ndims - 1) ? ", " : "";
-          PRINT2("\"%s\"%s", p->dims[j], cc);
-            }
-        PRINT("]");
-      }
-      if (p->unit)
-          PRINT2(",\n%s      \"unit\": \"%s\"", in, p->unit);
-      if (p->description)
-        PRINT2(",\n%s      \"description\": \"%s\"", in, p->description);
-      PRINT2("\n%s    }%s\n", in, c);
-    }
-    PRINT1("%s  ],\n", in);
-  }
-
-  PRINT1("%s}", in);
-
-  ok = 1;
- fail:
-  free(in);
-  return (ok) ? n : -1;
-}
-
-
-/*
-  Like dlite_json_sprint(), but prints to allocated buffer.
-
-  Prints to position `pos` in `*dest`, which should point to a buffer
-  of size `*size`.  `*dest` is reallocated if needed.
-
-  Returns number or bytes written or a negative number on error.
- */
-int dlite_json_asprint(char **dest, size_t *size, size_t pos,
-                       DLiteInstance *inst, int indent, DLiteJsonFlag flags)
-{
-  int m;
-  void *q;
-  size_t newsize;
-  if (!dest && !*dest) *size = 0;
-  m = dlite_json_sprint(*dest + pos, PDIFF(*size, pos), inst, indent, flags);
-  if (m < 0) return m;
-  if (m < (int)PDIFF(*size, pos)) return m;
-
-  /* Reallocate buffer to required size. */
-  newsize = m + pos + 1;
-  if (!(q = realloc(*dest, newsize))) return -1;
-  *dest = q;
-  *size = newsize;
-  m = dlite_json_sprint(*dest + pos, PDIFF(*size, pos), inst, indent, flags);
-  assert(0 <= m && m < (int)*size);
-  return m;
-}
-
-
-/*
-  Like dlite_json_sprint(), but returns allocated buffer with
-  serialised instance.
- */
-char *dlite_json_aprint(DLiteInstance *inst, int indent, DLiteJsonFlag flags)
-{
-  char *dest=NULL;
-  size_t size=0;
-  if ((dlite_json_asprint(&dest, &size, 0, inst, indent, flags)) < 0)
-    return NULL;
-  return dest;
-}
-
-/*
-  Like dlite_json_sprint(), but prints to stream `fp`.
-
-  Returns number or bytes printed or a negative number on error.
- */
-int dlite_json_fprint(FILE *fp, DLiteInstance *inst, int indent,
-                      DLiteJsonFlag flags)
-{
-  int m;
-  char *buf=NULL;
-  size_t size=0;
-  if ((m = dlite_json_asprint(&buf, &size, 0, inst, indent, flags)) >= 0) {
-    fprintf(fp, "%s\n", buf);
-    free(buf);
-  }
-  return m;
-}
-
-
-
-
-/*
-  Help function for parsing an instance.
-  - src: json source
-  - obj: jsmn representation of the json object to parse
-  - id:  id of `obj`
-
-  Returns new `instance` or NULL on error.
-*/
-static DLiteInstance *parse_instance(const char *src, jsmntok_t *obj,
-                                     const char *id)
-{
-  int ok=0;
-  jsmntok_t *item, *t;
-  char *buf=NULL;
-  size_t i, size=0, *dims=NULL;
-  DLiteInstance *inst=NULL;
-  const DLiteMeta *meta;
-  jsmntype_t dimtype = 0;
-  char *name=NULL, *version=NULL, *namespace=NULL;
-
-  assert(obj->type == JSMN_OBJECT);
-
-  /* Get metadata */
-  if ((item = jsmn_item(src, obj, "meta"))) {
-    int len = item->end - item->start;
-    if (item->type == JSMN_STRING) {
-      strnput(&buf, &size, 0, src + item->start, len);
-    } else if (item->type == JSMN_OBJECT) {
-      int n=0;
-      if (!(t = jsmn_item(src, item, "namespace")))
-        FAIL1("no \"namespace\" in meta for object %s", id);
-      n += strnput(&buf, &size, n, src + t->start, t->end - t->start);
-
-      if (!(t = jsmn_item(src, item, "version")))
-        FAIL1("no \"version\" in meta for object %s", id);
-      n += asnpprintf(&buf, &size, n, "/%.*s", t->end - t->start, src+t->start);
-
-      if (!(t = jsmn_item(src, item, "name")))
-        FAIL1("no \"name\" in meta for object %s", id);
-      n += asnpprintf(&buf, &size, n, "/%.*s", t->end - t->start, src+t->start);
-    } else {
-      FAIL1("\"meta\" not string or object in object %s", id);
-    }
-    if (!(meta = dlite_meta_get(buf)) &&
-        !(meta = (DLiteMeta *)dlite_json_sscan(src, buf)))
-      FAIL2("cannot find metadata '%s' when loading '%s' - please add the "
-            "right storage to DLITE_STORAGES and try again", buf, id);
-  } else {
-    /* If "meta" is not given, we assume it is an entity */
-    meta = dlite_get_entity_schema();
-  }
-  assert(meta);
-
-  /* Allocate dimensions */
-  if (!(dims = calloc(meta->_ndimensions, sizeof(size_t))))
-    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 (t->type != JSMN_PRIMITIVE)
-          FAIL3("value '%.*s' of dimension should be an integer: %s",
-                t->end-t->start, src+t->start, id);
-        dims[i] = atoi(src + t->start);
-      }
-    } else if (item->type == JSMN_ARRAY) {
-      int n=0;
-      if (!dlite_meta_is_metameta(meta))
-        FAIL1("only metadata can have array dimensions: %s", id);
-      if (meta->_ndimensions >= 2) dims[n++] = item->size;
-      if ((t = jsmn_item(src, obj, "properties")))
-        dims[n++] = t->size;
-      if (meta->_ndimensions >= 3 && (t = jsmn_item(src, obj, "relations")))
-        dims[n++] = t->size;
-    } else {
-      FAIL1("\"dimensions\" must be object or array: %s", id);
-    }
-  }
-
-  /* Create instance */
-  if (!(inst = dlite_instance_create(meta, dims, id))) goto fail;
-
-  /* Parse properties */
-  if (meta->_nproperties > 0) {
-    jsmntok_t *base=obj;  // assigned to make cppcheck happy...
-    if (!(item = jsmn_item(src, obj, "properties")))
-      FAIL1("no \"properties\" in object %s", id);
-    if (dimtype && item->type != dimtype)
-      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);
-    assert(base->type == JSMN_OBJECT);
-
-    /* -- infer name, version and namespace */
-    if ((t = jsmn_item(src, base, "uri"))) {
-      if (t->type != JSMN_STRING) FAIL1("uri must be a string: %s", id);
-      strnput(&buf, &size, 0, src + t->start, t->end - t->start);
-      if (dlite_split_meta_uri(buf, &name, &version, &namespace))
-        FAIL2("invalid uri '%s' in %s", buf, id);
-      if (!inst->uri) inst->uri = strdup(buf);
-    } else {
-      int status;
-
-      ErrTry:
-        status = dlite_split_meta_uri(id, &name, &version, &namespace);
-      ErrCatch(1):
-        break;
-      ErrEnd;
-
-      if (status && dlite_instance_is_meta(inst))
-        FAIL1("cannot infer name, version and namespace from id: %s", id);
-    }
-
-    /* -- read properties */
-    for (i=0; i < meta->_nproperties; i++) {
-      void *ptr;
-      DLiteProperty *p = meta->_properties + i;
-      size_t *pdims = DLITE_PROP_DIMS(inst, i);
-      if (!(ptr = dlite_instance_get_property_by_index(inst, i))) goto fail;
-      if ((t = jsmn_item(src, base, p->name))) {
-        strnput(&buf, &size, 0, src+t->start, t->end-t->start);
-        if (dlite_property_scan(buf, ptr, p, pdims, 0) < 0) goto fail;
-      } else {
-        /* -- if not given, use inferred name, version and namespace */
-        if (strcmp(p->name, "name") == 0) {
-          if (dlite_property_scan(name, ptr, p, pdims, 0) < 0) goto fail;
-        } else if (strcmp(p->name, "version") == 0) {
-          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
-          FAIL2("missing property \"%s\" in %s", p->name, id);
-      }
-    }
-  }
-  if (dlite_instance_is_meta(inst)) dlite_meta_init((DLiteMeta *)inst);
-
-  ok = 1;
- fail:
-  if (name) free(name);
-  if (version) free(version);
-  if (namespace) free(namespace);
-  if (dims) free(dims);
-  if (buf) free(buf);
-  if (!ok && inst) {
-    dlite_instance_decref(inst);
-    inst = NULL;
-  }
-  return inst;
-}
-
-
-/*
-  Returns a new instance scanned from `src`.
-
-  `id` is the uri or uuid of the instance to load.  If the string only
-  contain one instance, `id` may be NULL.
-
-  Returns the instance or NULL on error.
- */
-DLiteInstance *dlite_json_sscan(const char *src, const char *id)
-{
-  int i, r;
-  DLiteInstance *inst=NULL;
-  unsigned int ntokens=0;
-  jsmntok_t *tokens=NULL, *root;
-  jsmn_parser parser;
-
-  errno = 0;
-
-  jsmn_init(&parser);
-  r = jsmn_parse_alloc(&parser, src, strlen(src), &tokens, &ntokens);
-  if (r < 0) FAIL1("error parsing json: %s", jsmn_strerror(r));
-  root = tokens;
-  if (root->type != JSMN_OBJECT) FAIL("json root should be an object");
-
-  if (!id) {
-    inst = parse_instance(src, root, id);
-  } else {
-    int n=1;
-    char uuid[DLITE_UUID_LENGTH+1];
-    if (dlite_get_uuid(uuid, id) < 0) goto fail;
-    for (i=0; i < root->size; i++) {
-      char uuid2[DLITE_UUID_LENGTH+1], buf[128];
-      jsmntok_t *key = root + n;
-      jsmntok_t *val = root + n+1;
-      int len = key->end - key->start;
-      if (key->type != JSMN_STRING) FAIL("expect json keys to be strings");
-      if (len >= (int)sizeof(buf))
-        FAIL3("key exceeded maximum key length (%d): %.*s",
-              (int)sizeof(buf), len, src+key->start);
-      strncpy(buf, src+key->start, len);
-      buf[len] = '\0';
-      if (dlite_get_uuid(uuid2, buf) < 0) goto fail;
-      if (strcmp(uuid2, uuid) == 0) {
-        inst = parse_instance(src, val, id);
-        break;
-      }
-      n += jsmn_count(val) + 2;
-    }
-  }
- fail:
-  free(tokens);
-
-  return inst;
-}
-
-/*
-  Like dlite_json_sscan(), but scans instance `id` from stream `fp` instead
-  of a string.
-
-  Returns the instance or NULL on error.
- */
-DLiteInstance *dlite_json_fscan(FILE *fp, const char *id)
-{
-  DLiteInstance *inst;
-  char *buf;
-  if (!(buf = fu_readfile(fp))) return NULL;
-  inst = dlite_json_sscan(buf, id);
-  free(buf);
-  return inst;
-}
-
-
-
-
-
-
-/*
-  Creates and returns a new iterator used by dlite_json_next().
-
-  Arguments
-  - src: input JSON string to search.
-  - length: length of `src`.  If zero or negative, all of `src` will be used.
-  - metaid: limit the search to instances of metadata with this id.
-
-  Returns new iterator or NULL on error.
- */
-DLiteJsonIter *dlite_json_iter_init(const char *src, int length,
-                                    const char *metaid)
-{
-  int r, ok=0;
-  DLiteJsonIter *iter=NULL;
-  jsmn_parser parser;
-
-  if (!(iter = calloc(1, sizeof(DLiteJsonIter)))) FAIL("allocation failure");
-
-  if (length <= 0) length = strlen(src);
-  jsmn_init(&parser);
-  r = jsmn_parse_alloc(&parser, src, length, &iter->tokens, &iter->ntokens);
-  if (r < 0) FAIL1("error parsing json: %s", jsmn_strerror(r));
-  if (iter->tokens->type != JSMN_OBJECT) FAIL("json root should be an object");
-  iter->src = src;
-  iter->t = iter->tokens + 1;
-  if (metaid && dlite_get_uuid(iter->metauuid, metaid) < 0) goto fail;
-
-  ok=1;
- fail:
-  if (!ok) dlite_json_iter_deinit(iter);
-  return iter;
-}
-
-/*
-  Free's iterator created with dlite_json_iter_init().
- */
-void dlite_json_iter_deinit(DLiteJsonIter *iter)
-{
-  if (iter) {
-    if (iter->tokens) free(iter->tokens);
-    free(iter);
-  }
-}
-
-/*
-  Search for instances in the JSON document provided to dlite_json_iter_init()
-  and returns a pointer to instance UUIDs.
-
-  Returns a pointer to the next matching UUID or NULL if there are no more
-  matches left.
- */
-  const char *dlite_json_next(DLiteJsonIter *iter, int *length)
-
-{
-  const jsmntok_t *t = iter->t;
-  while (iter->n < iter->ntokens) {
-    const char *p = iter->src + t->start;
-    if (length) *length = t->end - t->start;
-    iter->t += jsmn_count(t + 1) + 2;
-    if (*iter->metauuid) {
-      if (strncmp(p, iter->metauuid, DLITE_UUID_LENGTH) == 0) return p;
-    } else
-      return p;
-  }
-  return NULL;
-}
diff --git a/src/dlite-print.h b/src/dlite-print.h
deleted file mode 100644
index e908f69bf..000000000
--- a/src/dlite-print.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* A set of utility function for serialising and deserialising dlite
-   instances to/from JSON.
-
-   This file provides built-in support for JSON to DLite.  This will not
-   replace
- */
-
-#ifndef _DLITE_PRINT_H
-#define _DLITE_PRINT_H
-
-
-typedef enum {
-  dliteJsonUuid=1,        /*!< Whether to include uuid */
-  dliteJsonMetaAsData=2,  /*!< Whether to print entities simplified */
-} DLiteJsonFlag;
-
-/**
-  Serialise instance `inst` to `dest`, formatted as JSON.
-
-  No more than `size` bytes are written to `dest` (incl. the
-  terminating NUL).
-
-  Returns number of bytes written to `dest`.  If the output is
-  truncated because it exceeds `size`, the number of bytes that would
-  have been written if `size` was large enough is returned.  On error, a
-  negative value is returned.
-*/
-int dlite_json_sprint(char *dest, size_t size, DLiteInstance *inst,
-                      int indent, DLiteJsonFlag flags);
-
-/**
-  Like dlite_sprint(), but prints to allocated buffer.
-
-  Prints to position `pos` in `*dest`, which should point to a buffer
-  of size `*size`.  `*dest` is reallocated if needed.
-
-  Returns number or bytes written or a negative number on error.
- */
-int dlite_json_asprint(char **dest, size_t *size, size_t pos,
-                       DLiteInstance *inst, int indent, DLiteJsonFlag flags);
-
-
-/**
-  Like dlite_sprint(), but returns allocated buffer with serialised instance.
- */
-char *dlite_json_aprint(DLiteInstance *inst, int indent, DLiteJsonFlag flags);
-
-
-/**
-  Like dlite_sprint(), but prints to stream `fp`.
-
-  Returns number or bytes printed or a negative number on error.
- */
-int dlite_json_fprint(FILE *fp, DLiteInstance *inst, int indent,
-                      DLiteJsonFlag flags);
-
-
-
-/**
-  Returns a new instance scanned from `src`.
-
-  `id` is the uri or uuid of the instance to load.  If the string only
-  contain one instance, `id` may be NULL.
-
-  Returns the instance or NULL on error.
- */
-DLiteInstance *dlite_json_sscan(const char *src, const char *id);
-
-/**
-  Like dlite_sscan(), but scans instance `id` from stream `fp` instead
-  of a string.
-
-  Returns the instance or NULL on error.
- */
-DLiteInstance *dlite_json_fscan(FILE *fp, const char *id);
-
-
-
-/** Opaque iterator struct */
-typedef struct _DLiteJsonIter DLiteJsonIter;
-
-/**
-  Creates and returns a new iterator used by dlite_json_next().
-
-  Arguments
-  - src: input JSON string to search.
-  - length: length of `src`.  If zero or negative, all of `src` will be used.
-  - metaid: limit the search to instances of metadata with this id.
-
-  Returns new iterator or NULL on error.
- */
-DLiteJsonIter *dlite_json_iter_init(const char *src, int length,
-                                    const char *metaid);
-
-/**
-  Free's iterator created with dlite_json_iter_init().
- */
-void dlite_json_iter_deinit(DLiteJsonIter *iter);
-
-/**
-  Search for instances in the JSON document provided to dlite_json_iter_init()
-  and returns a pointer to instance UUIDs.
-
-  Returns a pointer to the next matching UUID or NULL if there are no more
-  matches left.
- */
-const char *dlite_json_next(DLiteJsonIter *iter, int *length);
-
-
-
-#endif /*_ DLITE_PRINT_H */
diff --git a/src/dlite-storage-plugins.c b/src/dlite-storage-plugins.c
index 2a2d35254..0a8eb37c6 100644
--- a/src/dlite-storage-plugins.c
+++ b/src/dlite-storage-plugins.c
@@ -110,7 +110,7 @@ const DLiteStoragePlugin *dlite_storage_plugin_get(const char *name)
     r = asnpprintf(&buf, &size, m, "cannot find storage plugin for driver "
                    "\"%s\" in search path:\n", name);
     if (r >= 0) m += r;
-    while ((p = *(paths++)) && ++n) {
+    while (paths && (p = *(paths++)) && ++n) {
       r = asnpprintf(&buf, &size, m, "    %s\n", p);
       if (r >= 0) m += r;
     }
@@ -281,9 +281,21 @@ int dlite_storage_plugin_path_appendn(const char *path, size_t n)
 
   Returns non-zero on error.
 */
-int dlite_storage_plugin_path_remove(int n)
+int dlite_storage_plugin_path_delete(int n)
 {
   PluginInfo *info;
   if (!(info = get_storage_plugin_info())) return 1;
-  return plugin_path_remove(info, n);
+  return plugin_path_delete(info, n);
+}
+
+/*
+  Removes path `path` from current search path.
+
+  Returns non-zero if there is no such path.
+*/
+int dlite_storage_plugin_path_remove(const char *path)
+{
+  PluginInfo *info;
+  if (!(info = get_storage_plugin_info())) return 1;
+  return plugin_path_remove(info, path);
 }
diff --git a/src/dlite-storage-plugins.h b/src/dlite-storage-plugins.h
index 99ee66cdd..74b349643 100644
--- a/src/dlite-storage-plugins.h
+++ b/src/dlite-storage-plugins.h
@@ -181,7 +181,7 @@ int dlite_storage_plugin_path_insert(int n, const char *path);
 /**
   Appends `path` into the current search path.
 
-  Returns non-zero on error.
+  Returns the index of the newly appended element or -1 on error.
 */
 int dlite_storage_plugin_path_append(const char *path);
 
@@ -189,7 +189,7 @@ int dlite_storage_plugin_path_append(const char *path);
   Like dlite_storage_plugin_path_append(), but appends at most the
   first `n` bytes of `path` to the current search path.
 
-  Returns non-zero on error.
+  Returns the index of the newly appended element or -1 on error.
 */
 int dlite_storage_plugin_path_appendn(const char *path, size_t n);
 
@@ -198,7 +198,14 @@ int dlite_storage_plugin_path_appendn(const char *path, size_t n);
 
   Returns non-zero on error.
 */
-int dlite_storage_plugin_path_remove(int n);
+int dlite_storage_plugin_path_delete(int n);
+
+/**
+  Removes path `path` from current search path.
+
+  Returns non-zero if there is no such path.
+*/
+int dlite_storage_plugin_path_remove(const char *path);
 
 
 /** @} */
@@ -264,8 +271,8 @@ typedef int (*Close)(DLiteStorage *s);
 typedef void *(*IterCreate)(const DLiteStorage *s, const char *pattern);
 
 /**
-  Writes the UUID to buffer pointed to by `buf` of the next instance
-  in `iter`, where `iter` is an iterator created with IterCreate().
+  Writes the uuid of the next instance to `buf`, where `iter` is an
+  iterator returned by IterCreate().
 
   Returns zero on success, 1 if there are no more UUIDs to iterate
   over and a negative number on other errors.
diff --git a/src/dlite-storage.c b/src/dlite-storage.c
index bcadecf4e..6b7b40d05 100644
--- a/src/dlite-storage.c
+++ b/src/dlite-storage.c
@@ -296,10 +296,10 @@ int dlite_storage_paths_append(const char *path)
 
   Returns non-zero on error.
  */
-int dlite_storage_paths_remove(int n)
+int dlite_storage_paths_delete(int n)
 {
   FUPaths *paths = dlite_storage_paths();
-  return fu_paths_remove(paths, n);
+  return fu_paths_delete(paths, n);
 }
 
 /*
diff --git a/src/dlite-storage.h b/src/dlite-storage.h
index 16dbd8fed..4c9a7bccf 100644
--- a/src/dlite-storage.h
+++ b/src/dlite-storage.h
@@ -182,7 +182,7 @@ int dlite_storage_paths_append(const char *path);
 
   Returns non-zero on error.
  */
-int dlite_storage_paths_remove(int n);
+int dlite_storage_paths_delete(int n);
 
 /**
   Returns a NULL-terminated array of pointers to paths/urls or NULL if
diff --git a/src/dlite-type.c b/src/dlite-type.c
index 4e88f80cc..fccfab194 100644
--- a/src/dlite-type.c
+++ b/src/dlite-type.c
@@ -15,8 +15,8 @@
 #include "utils/boolean.h"
 #include "utils/strtob.h"
 #include "utils/strutils.h"
-#define JSMN_HEADER
-#include "utils/jsmn.h"
+#include "utils/jsmnx.h"
+
 #include "dlite-entity.h"
 #include "dlite-macros.h"
 #include "dlite-type.h"
@@ -605,9 +605,9 @@ void *dlite_type_clear(void *p, DLiteType dtype, size_t size)
       free(((DLiteProperty *)p)->description);
     break;
   case dliteRelation:
-    free(((DLiteRelation *)p)->s);
-    free(((DLiteRelation *)p)->p);
-    free(((DLiteRelation *)p)->o);
+    if (((DLiteRelation *)p)->s)  free(((DLiteRelation *)p)->s);
+    if (((DLiteRelation *)p)->p)  free(((DLiteRelation *)p)->p);
+    if (((DLiteRelation *)p)->o)  free(((DLiteRelation *)p)->o);
     if (((DLiteRelation *)p)->id) free(((DLiteRelation *)p)->id);
     break;
   }
@@ -847,6 +847,12 @@ 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_RELATION_TOKENS   9
+
 /*
   Scans a value from `src` and write it to memory pointed to by `p`.
 
@@ -992,7 +998,8 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
     {
       DLiteDimension *dim = p;
       jsmn_parser parser;
-      jsmntok_t tokens[5], *t;
+      jsmntok_t tokens[MAX_DIMENSION_TOKENS];
+      const jsmntok_t *t;
       int r;
 
       if (dim->name) free(dim->name);
@@ -1001,7 +1008,7 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
 
       if (len < 0) len = strlen(src);
       jsmn_init(&parser);
-      if ((r = jsmn_parse(&parser, src, len, tokens, 5)) < 0)
+      if ((r = jsmn_parse(&parser, src, len, tokens,MAX_DIMENSION_TOKENS)) < 0)
         return err(-1, "cannot parse dimension: %s: '%s'",
                    jsmn_strerror(r), src);
       if (tokens->type != JSMN_OBJECT)
@@ -1020,7 +1027,8 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
     {
       DLiteProperty *prop = p;
       jsmn_parser parser;
-      jsmntok_t tokens[32], *t, *d;
+      jsmntok_t tokens[MAX_PROPERTY_TOKENS];
+      const jsmntok_t *t, *d;
       int r, i;
 
       if (prop->name) free(prop->name);
@@ -1031,7 +1039,7 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
 
       if (len < 0) len = strlen(src);
       jsmn_init(&parser);
-      if ((r = jsmn_parse(&parser, src, len, tokens, 32)) < 0)
+      if ((r = jsmn_parse(&parser, src, len, tokens, MAX_PROPERTY_TOKENS)) < 0)
         return err(-1, "cannot parse property: %s: '%s'",
                    jsmn_strerror(r), src);
       if (tokens->type != JSMN_OBJECT)
@@ -1073,7 +1081,8 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
     {
       DLiteRelation *rel = p;
       jsmn_parser parser;
-      jsmntok_t tokens[5], *t;
+      jsmntok_t tokens[MAX_RELATION_TOKENS];
+      const jsmntok_t *t;
       int r;
 
       if (rel->s) free(rel->s);
@@ -1084,23 +1093,33 @@ int dlite_type_scan(const char *src, int len, void *p, DLiteType dtype,
 
       if (len < 0) len = strlen(src);
       jsmn_init(&parser);
-      if ((r = jsmn_parse(&parser, src, len, tokens, 5)) < 0)
+      if ((r = jsmn_parse(&parser, src, len, tokens, MAX_RELATION_TOKENS)) < 0)
         return err(-1, "cannot parse relation: %s: '%s'",
                    jsmn_strerror(r), src);
-      if (tokens->type != JSMN_ARRAY)
-        return errx(-1, "relation should be a JSON array");
       if (tokens->size < 3 || tokens->size > 4)
         return errx(-1, "relation should have 3 (optionally 4) elements");
       m = tokens->end - tokens->start;
-
-      if (!(t = jsmn_element(src, tokens, 0))) return -1;
-      rel->s = strndup(src + t->start, t->end - t->start);
-      if (!(t = jsmn_element(src, tokens, 1))) return -1;
-      rel->p = strndup(src + t->start, t->end - t->start);
-      if (!(t = jsmn_element(src, tokens, 2))) return -1;
-      rel->o = strndup(src + t->start, t->end - t->start);
-      if (tokens->size > 3 && (t = jsmn_element(src, tokens, 3)))
-        rel->id = strndup(src + t->start, t->end - t->start);
+      if (tokens->type == JSMN_ARRAY) {
+        if (!(t = jsmn_element(src, tokens, 0))) return -1;
+        rel->s = strndup(src + t->start, t->end - t->start);
+        if (!(t = jsmn_element(src, tokens, 1))) return -1;
+        rel->p = strndup(src + t->start, t->end - t->start);
+        if (!(t = jsmn_element(src, tokens, 2))) return -1;
+        rel->o = strndup(src + t->start, t->end - t->start);
+        if (tokens->size > 3 && (t = jsmn_element(src, tokens, 3)))
+          rel->id = strndup(src + t->start, t->end - t->start);
+      } else if (tokens->type == JSMN_OBJECT) {
+        if (!(t = jsmn_item(src, tokens, "s"))) return -1;
+        rel->s = strndup(src + t->start, t->end - t->start);
+        if (!(t = jsmn_item(src, tokens, "p"))) return -1;
+        rel->p = strndup(src + t->start, t->end - t->start);
+        if (!(t = jsmn_item(src, tokens, "o"))) return -1;
+        rel->o = strndup(src + t->start, t->end - t->start);
+        if ((t = jsmn_item(src, tokens, "id")))
+          rel->id = strndup(src + t->start, t->end - t->start);
+      } else {
+        return errx(-1, "relation should be a JSON array");
+      }
     }
     break;
   }
diff --git a/src/dlite.h b/src/dlite.h
index 8cec883b8..b89c54a2e 100644
--- a/src/dlite.h
+++ b/src/dlite.h
@@ -18,7 +18,7 @@
 #include "dlite-storage.h"
 #include "dlite-collection.h"
 #include "dlite-getlicense.h"
-#include "dlite-print.h"
+#include "dlite-json.h"
 
 
 #endif /* _DLITE_H */
diff --git a/src/getuuid.c b/src/getuuid.c
index 6d0734cc9..38ab290ed 100644
--- a/src/getuuid.c
+++ b/src/getuuid.c
@@ -24,15 +24,26 @@
  * `id` is simply copied.  On error, -1 is returned.
  */
 int getuuid(char *buff, const char *id)
+{
+  return getuuidn(buff, id, (id) ? strlen(id) : 0);
+}
+
+
+/*
+ * Like getuuid(), but takes the the length of `id` as an additional parameter.
+ */
+int getuuidn(char *buff, const char *id, size_t len)
 {
   int i, version;
   uuid_s uuid;
 
+  if (len == 0) id = NULL;
+
   if (!id || !*id) {
     int status = uuid4_generate(buff);
     version = (status == 0) ? 4 : -1;
-  } else if (uuid_from_string(NULL, id)) {
-    uuid_create_sha1_from_name(&uuid, NameSpace_DNS, id, strlen(id));
+  } else if (uuid_from_string(NULL, id, len)) {
+    uuid_create_sha1_from_name(&uuid, NameSpace_DNS, id, len);
     uuid_as_string(&uuid, buff);
     version = 5;
   } else {
diff --git a/src/getuuid.h b/src/getuuid.h
index 8de0ee99a..57c7b9b99 100644
--- a/src/getuuid.h
+++ b/src/getuuid.h
@@ -24,4 +24,10 @@
  */
 int getuuid(char *buff, const char *id);
 
+
+/**
+ * Like getuuid(), but takes the the length of `id` as an additional parameter.
+ */
+int getuuidn(char *buff, const char *id, size_t len);
+
 #endif /* _GETUUID_H */
diff --git a/src/pyembed/dlite-python-mapping.c b/src/pyembed/dlite-python-mapping.c
index 072108740..f9ece7bfa 100644
--- a/src/pyembed/dlite-python-mapping.c
+++ b/src/pyembed/dlite-python-mapping.c
@@ -101,11 +101,11 @@ int dlite_python_mapping_paths_append(const char *path)
   Removes path index `n` to Python mapping paths.
   Returns non-zero on error.
 */
-int dlite_python_mapping_paths_remove(int n)
+int dlite_python_mapping_paths_delete(int n)
 {
   const FUPaths *paths;
   if (!(paths = dlite_python_mapping_paths())) return -1;
-  return fu_paths_remove((FUPaths *)paths, n);
+  return fu_paths_delete((FUPaths *)paths, n);
 }
 
 /*
@@ -210,7 +210,7 @@ static DLiteInstance *mapper(const DLiteMappingPlugin *api,
   Py_XDECREF(insts);
   Py_XDECREF(map);
   for (i=0; i<n; i++) dlite_instance_decref((DLiteInstance *)instances[i]);
-  dlite_meta_decref((DLiteMeta *)inst->meta);  // XXX - is this correct?
+  if (inst) dlite_meta_decref((DLiteMeta *)inst->meta);  // @todo - correct?
   return inst;
 }
 
diff --git a/src/pyembed/dlite-python-mapping.h b/src/pyembed/dlite-python-mapping.h
index 6cd24889a..daaa0696e 100644
--- a/src/pyembed/dlite-python-mapping.h
+++ b/src/pyembed/dlite-python-mapping.h
@@ -38,7 +38,7 @@ int dlite_python_mapping_paths_append(const char *path);
   Removes path index `n` to Python mapping paths.
   Returns non-zero on error.
 */
-int dlite_python_mapping_paths_remove(int n);
+int dlite_python_mapping_paths_delete(int n);
 
 /**
   Returns a pointer to the current Python mapping plugin search path
diff --git a/src/pyembed/dlite-python-storage.c b/src/pyembed/dlite-python-storage.c
index 52590d13a..59bcdfc53 100644
--- a/src/pyembed/dlite-python-storage.c
+++ b/src/pyembed/dlite-python-storage.c
@@ -112,12 +112,12 @@ int dlite_python_storage_paths_append(const char *path)
   Removes path index `n` to Python storage paths.
   Returns non-zero on error.
 */
-int dlite_python_storage_paths_remove(int n)
+int dlite_python_storage_paths_delete(int n)
 {
   int stat;
   const FUPaths *paths;
   if (!(paths = dlite_python_storage_paths())) return -1;
-  if ((stat = fu_paths_remove((FUPaths *)paths, n)))
+  if ((stat = fu_paths_delete((FUPaths *)paths, n)))
     storage_paths_modified = 1;
   return stat;
 }
diff --git a/src/pyembed/dlite-python-storage.h b/src/pyembed/dlite-python-storage.h
index 48ab9d146..b42bc01a6 100644
--- a/src/pyembed/dlite-python-storage.h
+++ b/src/pyembed/dlite-python-storage.h
@@ -39,7 +39,7 @@ int dlite_python_storage_paths_append(const char *path);
   Removes path index `n` to Python storage paths.
   Returns non-zero on error.
 */
-int dlite_python_storage_paths_remove(int n);
+int dlite_python_storage_paths_delete(int n);
 
 /**
   Returns a pointer to the current Python storage plugin search path
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 1ecd7d737..c06d21d6f 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -259,7 +259,8 @@ set(sources
   tgen.c
   tmpfileplus.c
   infixcalc.c
-  jsmn.c
+  jsmnx.c
+  jstore.c
 
   md5.c
   sha1.c
@@ -338,6 +339,14 @@ install(
   INCLUDES DESTINATION include/dlite/utils
   )
 
+# Install jsmn.h explicitly since it has no corresponding .c file
+# included in `sources`
+install(
+  FILES
+    jsmn.h
+  DESTINATION include/dlite/utils
+  )
+
 install(
   FILES
     compat/dirent.h
diff --git a/src/utils/err.h b/src/utils/err.h
index 153aa1984..c95ae14e4 100644
--- a/src/utils/err.h
+++ b/src/utils/err.h
@@ -152,7 +152,7 @@ int _err_vformat(ErrLevel errlevel, int eval, int errnum, const char *file,
 
 /** @brief Buffer size for error messages. */
 #ifndef ERR_MSGSIZE
-#define ERR_MSGSIZE 1024
+#define ERR_MSGSIZE 4096
 #endif
 
 /** @brief Default error stream (checks the ERR_STREAM environment variable) */
diff --git a/src/utils/fileutils.c b/src/utils/fileutils.c
index 925e7dfd6..f6e806d8c 100644
--- a/src/utils/fileutils.c
+++ b/src/utils/fileutils.c
@@ -45,6 +45,9 @@ struct _FUIter {
   const char *pattern;   /* File name glob pattern to match against. */
   size_t i;              /* Index of current position in `paths`. */
   const FUPaths *paths;  /* Paths */
+  char **origpaths;      /* Copy of paths->paths to not be affected by
+                            inserts and deletions during iteration */
+  size_t origlen;        /* Copy of paths->n */
   const char *filename;  /* Name of current file (excluding directory path) */
   char *dirname;         /* Allocated name of current directory */
   char *path;            /* Full path to current file */
@@ -829,9 +832,13 @@ const char **fu_paths_get(FUPaths *paths)
   return paths->paths;
 }
 
+
 /*
-  Inserts `path` into `paths` before position `n`.  If `n` is negative, it
-  counts from the end (like Python).
+  Inserts `path` into `paths` before position `n`.  If `n` is
+  negative, it counts from the end (like Python).
+
+  If `path` already exists in `paths`, it is moved to position `n`, but
+  not duplicated.
 
   Returns the index of the newly inserted element or -1 on error.
  */
@@ -847,12 +854,19 @@ int fu_paths_insert(FUPaths *paths, const char *path, int n)
   Inserts the `len` first bytes of `path` into `paths` before position `n`.
   If `len` is zero, this is equivalent to fu_paths_insert().
 
+  If `n` is negative, it counts from the end (like Python).
+
+  If `path` already exists in `paths`, it is moved to position `n`, but
+  not duplicated.
+
   Returns the index of the newly inserted element or -1 on error.
  */
 int fu_paths_insertn(FUPaths *paths, const char *path, size_t len, int n)
 {
   int platform = paths->platform;
   char *p=NULL, *tmp=NULL;
+  int index;
+
   if (n < -(int)(paths->n) || n >= (int)paths->n+1)
     FAIL1("path index out of range: %d", n);
   if (n < 0) n += paths->n;
@@ -872,6 +886,18 @@ int fu_paths_insertn(FUPaths *paths, const char *path, size_t len, int n)
   }
   if (!p) FAIL("allocation failure");
 
+  if ((index = fu_paths_index(paths, p)) >= 0) {
+
+    /* path already in paths - remove it if it is not at expected position */
+    if (index == n || (n == (int)paths->n && index == (int)paths->n - 1)) {
+      if (p) free(p);
+      if (tmp) free(tmp);
+      return index;  // path already at expected position
+    }
+    if (fu_paths_delete(paths, index)) goto fail;
+    if (n > index) n--;
+  }
+
   if (paths->n+1 >= paths->size) {
     const char **q;
     paths->size = paths->n + FU_PATHS_CHUNKSIZE;
@@ -893,7 +919,7 @@ int fu_paths_insertn(FUPaths *paths, const char *path, size_t len, int n)
 }
 
 /*
-  Appends `path` to `paths`.  Equivalent to
+  Appends `path` to `paths` (if it not already exists).  Equivalent to
 
       fu_paths_insert(paths, path, paths->n)
 
@@ -968,11 +994,11 @@ int fu_paths_extend_prefix(FUPaths *paths, const char *prefix,
 
   Returns non-zero on error.
  */
-int fu_paths_remove(FUPaths *paths, int n)
+int fu_paths_delete(FUPaths *paths, int n)
 {
+  if (n < -(int)(paths->n) || n >= (int)paths->n+1)
+    return err(-1, "path index out of range: %d", n);
   if (n < 0) n += paths->n;
-  if (n < 0 || n >= (int)paths->n)
-    return err(1, "path index out of range: %d", n);
   assert(paths->paths[n]);
   free((char *)paths->paths[n]);
   memmove((char **)paths->paths+n, paths->paths+n+1,
@@ -981,8 +1007,61 @@ int fu_paths_remove(FUPaths *paths, int n)
   return 0;
 }
 
+/*
+  Removes path `path`.  Returns non-zero if there is no such path.
+ */
+int fu_paths_remove(FUPaths *paths, const char *path)
+{
+  int i = fu_paths_index(paths, path);
+  if (i < 0) return -1;
+  if (fu_paths_delete(paths, i)) return 1;
+  return 0;
+}
+
+/*
+  Returns index of path `path` or -1 if there is no such path in `paths`.
+ */
+int fu_paths_index(FUPaths *paths, const char *path)
+{
+  size_t i;
+  for (i=0; i < paths->n; i++)
+    if (strcmp(paths->paths[i], path) == 0) return i;
+  return -1;
+}
+
+
+
 /* ---------------------------------------------------------- */
 
+/* Frees NULL-terminated list of malloc'ed strings.
+   Returns non-zero on error. */
+static int strlist_free(char **strlist)
+{
+  char **q;
+  if (!strlist) return err(1, "string list is NULL");
+  for (q=strlist; *q; q++) free(*q);
+  free(strlist);
+  return 0;
+}
+
+/* Returns a copy of NULL-terminated list of malloc'ed strings. */
+static char **strlist_copy(const char **strlist)
+{
+  char **cpy;
+  size_t i, n=0;
+  while (strlist[n]) n++;
+  if (!(cpy = calloc(n+1, sizeof(char *))))
+    return err(1, "allocation failure"), NULL;
+  for (i=0; i<n; i++) {
+    if (!(cpy[i] = strdup(strlist[i]))) {
+      strlist_free(cpy);
+      return err(1, "allocation failure"), NULL;
+    }
+  }
+  return cpy;
+}
+
+
 /*
   Returns a new iterator for finding files matching `pattern` in
   directories included in `paths`.
@@ -994,18 +1073,23 @@ int fu_paths_remove(FUPaths *paths, int n)
  */
 FUIter *fu_startmatch(const char *pattern, const FUPaths *paths)
 {
-  FUIter *iter;
-  if (!(iter = calloc(1, sizeof(FUIter))))
-    return err(1, "allocation failure"), NULL;
+  FUIter *iter=NULL;
+  if (!(iter = calloc(1, sizeof(FUIter)))) FAIL("allocation failure");
   iter->pattern = pattern;
   iter->i = 0;
   iter->paths = paths;
+  iter->origlen = paths->n;
+  if (!(iter->origpaths = strlist_copy(paths->paths))) goto fail;
   iter->filename = NULL;
   iter->path = NULL;
   iter->pathsize = 0;
   iter->dir = NULL;
   iter->dirsep = DIRSEP[0];
   return iter;
+ fail:
+  if (iter->origpaths) strlist_free(iter->origpaths);
+  if (iter) free(iter);
+  return NULL;
 }
 
 /*
@@ -1016,18 +1100,23 @@ FUIter *fu_startmatch(const char *pattern, const FUPaths *paths)
   The returned string is owned by the iterator. It will be overwritten
   by the next call to fu_nextmatch() and should not be changed.  Use
   strdup() or strncpy() if a copy is needed.
+
+  Note 2:
+  The iterator will not be affected by adding or deleting paths during
+  iteration.  Hence, it is safe to insert a path or delete the path
+  returned by fu_nextmatch().  But if you delete a yet non-visited path
+  it will still be visited.
  */
 const char *fu_nextmatch(FUIter *iter)
 {
   const char *filename;
-  const FUPaths *p = iter->paths;
   char dirsep[2] = { iter->dirsep, 0 };
-  if (iter->i >= p->n) return NULL;
+  if (iter->i >= iter->origlen) return NULL;
 
-  while (iter->i < p->n) {
-    const char *path = p->paths[iter->i];
+  while (iter->i < iter->origlen) {
+    const char *path = iter->origpaths[iter->i];
     if (!iter->dir) {
-      if (iter->i >= p->n) return NULL;
+      if (iter->i >= iter->origlen) return NULL;
       if (!path[0]) path = ".";
 
       ErrTry:
@@ -1074,10 +1163,12 @@ const char *fu_nextmatch(FUIter *iter)
  */
 int fu_endmatch(FUIter *iter)
 {
+  int status = 0;
   if (iter->path) free(iter->path);
   if (iter->dir) fu_closedir(iter->dir);
+  status |= strlist_free(iter->origpaths);
   free(iter);
-  return 0;
+  return status;
 }
 
 /* ---------------------------------------------------------- */
@@ -1104,7 +1195,12 @@ FUIter *fu_pathsiter_init(const FUPaths *paths, const char *pattern)
   iter->paths = paths;
   iter->pattern = pattern;
   iter->dirsep = DIRSEP[0];
+  iter->origlen = paths->n;
+  if (!(iter->origpaths = strlist_copy(paths->paths))) goto fail;
   return iter;
+ fail:
+  if (iter) free(iter);
+  return NULL;
 }
 
 /*
@@ -1113,10 +1209,9 @@ FUIter *fu_pathsiter_init(const FUPaths *paths, const char *pattern)
  */
 const char *_fu_pathsiter_next(FUIter *iter)
 {
-  const FUPaths *p = iter->paths;
-  if (iter->i >= p->n) return NULL;
+  if (iter->i >= iter->origlen) return NULL;
 
-  while (iter->i < p->n) {
+  while (iter->i < iter->origlen) {
     const char *path;
 
     /* Check for ongoing iteration over a directory... */
@@ -1143,7 +1238,7 @@ const char *_fu_pathsiter_next(FUIter *iter)
         iter->dir = NULL;
         iter->dirname = NULL;
         iter->filename = NULL;
-        if (++iter->i >= p->n) break;
+        if (++iter->i >= iter->origlen) break;
       }
     }
 
@@ -1164,12 +1259,12 @@ const char *_fu_pathsiter_next(FUIter *iter)
       } else {
         fu_globend(iter->globiter);
         iter->globiter = NULL;
-        if (++iter->i >= p->n) break;
+        if (++iter->i >= iter->origlen) break;
       }
     }
 
     assert(!iter->dir && !iter->globiter);
-    path = p->paths[iter->i];
+    path = iter->origpaths[iter->i];
 
     /* Check if `path` is a directory...
        Note that we are calling opendir() directly, to avoid errors if
@@ -1190,6 +1285,12 @@ const char *_fu_pathsiter_next(FUIter *iter)
   Returns the next file or directory in the iterator `iter` created
   with fu_paths_iter_init().  NULL is returned on error or if there
   are no more file names to iterate over.
+
+  Note:
+  The iterator will not be affected by adding or deleting paths during
+  iteration.  Hence, it is safe to insert a path or delete the path
+  returned by fu_pathiter_next().  But if you delete a yet non-visited
+  path it will still be visited.
  */
 const char *fu_pathsiter_next(FUIter *iter)
 {
@@ -1215,6 +1316,7 @@ int fu_pathsiter_deinit(FUIter *iter)
   if (iter->path) free(iter->path);
   if (iter->dir) status |= fu_closedir(iter->dir);
   if (iter->globiter) status |= fu_globend(iter->globiter);
+  status |= strlist_free(iter->origpaths);
   free(iter);
   return status;
 }
diff --git a/src/utils/fileutils.h b/src/utils/fileutils.h
index bba8ac8fe..bd6180dc7 100644
--- a/src/utils/fileutils.h
+++ b/src/utils/fileutils.h
@@ -225,7 +225,8 @@ char *fu_friendly_dirsep(char *path);
       /etc/fstab
       http://example.com
 
-  Note that the length of a returned path can be obtained with `endptr-p`.
+  @note
+  The length of a returned path can be obtained with `endptr-p`.
  */
 const char *fu_nextpath(const char *paths, char **endptr, const char *pathsep);
 
@@ -420,7 +421,18 @@ int fu_paths_extend_prefix(FUPaths *paths, const char *prefix, const char *s,
 
   Returns non-zero on error.
  */
-int fu_paths_remove(FUPaths *paths, int n);
+int fu_paths_delete(FUPaths *paths, int n);
+
+/**
+  Removes path `path`.  Returns non-zero if there is no such path.
+ */
+int fu_paths_remove(FUPaths *paths, const char *path);
+
+/**
+  Returns index of path `path` or -1 if there is no such path in `paths`.
+ */
+int fu_paths_index(FUPaths *paths, const char *path);
+
 
 
 /**
@@ -439,6 +451,12 @@ FUIter *fu_startmatch(const char *pattern, const FUPaths *paths);
   The returned string is owned by the iterator. It will be overwritten
   by the next call to fu_nextmatch() and should not be changed.  Use
   strdup() or strncpy() if a copy is needed.
+
+  @note
+  The iterator will not be affected by adding or deleting paths during
+  iteration.  Hence, it is safe to insert a path or delete the path
+  returned by fu_nextmatch().  But if you delete a yet non-visited path
+  it will still be visited.
  */
 const char *fu_nextmatch(FUIter *iter);
 
@@ -468,6 +486,12 @@ FUIter *fu_pathsiter_init(const FUPaths *paths, const char *pattern);
   Returns the next file or directory in the iterator `iter` created
   with fu_paths_iter_init().  NULL is returned on error or if there
   are no more file names to iterate over.
+
+  @note
+  The iterator will not be affected by adding or deleting paths during
+  iteration.  Hence, it is safe to insert a path or delete the path
+  returned by fu_pathiter_next().  But if you delete a yet non-visited
+  path it will still be visited.
  */
 const char *fu_pathsiter_next(FUIter *iter);
 
diff --git a/src/utils/jsmn.h b/src/utils/jsmn.h
index 61988a710..3178dcc97 100644
--- a/src/utils/jsmn.h
+++ b/src/utils/jsmn.h
@@ -1,5 +1,3 @@
-/* jsmn.h -- small and fast JSON parser, see https://github.com/zserge/jsmn */
-
 /*
  * MIT License
  *
@@ -89,89 +87,20 @@ typedef struct jsmn_parser {
 } jsmn_parser;
 
 /**
- * Initializes a JSON parser.
+ * Create JSON parser over an array of tokens
  */
 JSMN_API void jsmn_init(jsmn_parser *parser);
 
 /**
- * Run JSON parser.
- *
- * It parses a JSON data string into and array of tokens, each
- * describing a single JSON object.
- *
- * Arguments
- * ---------
- *  - parser: pointer to initialized parser
- *  - js: the JSON input string
- *  - len: length of `js`
- *  - tokens: pointer to a preallocated buffer of JSMN tokens.  May be
- *    NULL in order to return the number of tokens in the input.
- *  - num_tokens: the size of `tokens`
- *
- * Returns
- * -------
- * On success, it returns the number of tokens actually used by the parser.
- * On error, one of the following codes is returned:
- *
- *  - JSMN_ERROR_INVAL: bad token, JSON string is corrupted
- *  - JSMN_ERROR_NOMEM: not enough tokens, JSON string is too large
- *  - JSMN_ERROR_PART:  JSON string is too short, expecting more JSON data
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each
+ * describing
+ * a single JSON object.
  */
 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
                         jsmntok_t *tokens, const unsigned int num_tokens);
 
-
-/** Chunck size when reallocating new chunks */
-#ifndef JSMN_CHUNK_SIZE
-#define JSMN_CHUNK_SIZE 4096
-#endif
-
-/**
- * Like jsmn_parse(), but realloc's the buffer pointed to by `tokens_ptr`
- * if it is too small.  `num_tokens_ptr` should point to the number of
- * allocated tokens.
- *
- * Returns JSMN_ERROR_NOMEM on allocation error.
- */
-int jsmn_parse_alloc(jsmn_parser *parser, const char *js, const size_t len,
-                     jsmntok_t **tokens_ptr, unsigned int *num_tokens_ptr);
-
-
-/**
- * Returns number of sub-tokens contained in `t` or -1 on error.
- */
-  JSMN_API int jsmn_count(const jsmntok_t *t);
-
-
-/**
- * Returns a pointer to the value of item `key` of JSMN object token `t`.
- *
- * `js` is the JSON source.
- *
- * Returns NULL on error.
- */
-JSMN_API jsmntok_t *jsmn_item(const char *js, jsmntok_t *t, const char *key);
-
-
-/**
- * Returns a pointer to the element `i` of JSMN array token `t`.
- *
- * `js` is the JSON source.
- *
- * Returns NULL on error.
- */
-JSMN_API jsmntok_t *jsmn_element(const char *js, jsmntok_t *t, int i);
-
-
-/**
- * Returns error message corresponding to return value from jsmn_parse().
- */
-JSMN_API const char *jsmn_strerror(int r);
-
-
-
 #ifndef JSMN_HEADER
-/*
+/**
  * Allocates a fresh unused token from the token pool.
  */
 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
@@ -189,7 +118,7 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
   return tok;
 }
 
-/*
+/**
  * Fills token type and boundaries.
  */
 static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
@@ -200,7 +129,7 @@ static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
   token->size = 0;
 }
 
-/*
+/**
  * Fills next available token with JSON primitive.
  */
 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
@@ -258,7 +187,7 @@ static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
   return 0;
 }
 
-/*
+/**
  * Fills next token with JSON string.
  */
 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
@@ -333,7 +262,7 @@ static int jsmn_parse_string(jsmn_parser *parser, const char *js,
   return JSMN_ERROR_PART;
 }
 
-/*
+/**
  * Parse JSON string and fill tokens.
  */
 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
@@ -523,7 +452,7 @@ JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
   return count;
 }
 
-/*
+/**
  * Creates a new parser based over a given buffer with an array of tokens
  * available.
  */
@@ -533,8 +462,6 @@ JSMN_API void jsmn_init(jsmn_parser *parser) {
   parser->toksuper = -1;
 }
 
-
-
 #endif /* JSMN_HEADER */
 
 #ifdef __cplusplus
diff --git a/src/utils/jsmn.c b/src/utils/jsmnx.c
similarity index 88%
rename from src/utils/jsmn.c
rename to src/utils/jsmnx.c
index e111bc211..9e6631d05 100644
--- a/src/utils/jsmn.c
+++ b/src/utils/jsmnx.c
@@ -1,4 +1,10 @@
-/* jsmn.h -- small and fast JSON parser, see https://github.com/zserge/jsmn */
+/* jsmnx.c -- extended version of the simple JSMN JSON parser
+ *
+ * Copyright (C) 2021 SINTEF
+ *
+ * Distributed under terms of the MIT license.
+ */
+
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
@@ -8,17 +14,11 @@
 /* Include the jsmn.h header without defining JSMN_HEADER defined.
    This defines all functions in jsmn. */
 #define JSNM_STATIC
+#define JSMN_STRICT
+#define JSMN_PARENT_LINKS
 #include "jsmn.h"
 
 
-/*
-  Additional functions not provided with the minimalistic jsmn api
-  ================================================================
-
-  We allow dependencies on the standard library in this file.
- */
-
-
 /*
   Like jsmn_parse(), but realloc's the buffer pointed to by `tokens_ptr`
   if it is too small.  `num_tokens_ptr` should point to the number of
@@ -96,7 +96,7 @@ int jsmn_count(const jsmntok_t *t)
 
   Returns NULL on error.
 */
-jsmntok_t *jsmn_item(const char *js, jsmntok_t *t, const char *key)
+const jsmntok_t *jsmn_item(const char *js, const jsmntok_t *t, const char *key)
 {
   int i, n, nitems;
   int len, keylen=strlen(key);
@@ -122,7 +122,7 @@ jsmntok_t *jsmn_item(const char *js, jsmntok_t *t, const char *key)
 
   Returns NULL on error.
 */
-jsmntok_t *jsmn_element(const char *js, jsmntok_t *t, int i)
+const jsmntok_t *jsmn_element(const char *js, const jsmntok_t *t, int i)
 {
   int j, n;
   (void)js;  // unused
@@ -142,7 +142,7 @@ jsmntok_t *jsmn_element(const char *js, jsmntok_t *t, int i)
 /*
   Returns error message corresponding to return value from jsmn_parse().
 */
-JSMN_API const char *jsmn_strerror(int r)
+const char *jsmn_strerror(int r)
 {
   if (r >= 0) return "success";
   switch (r) {
diff --git a/src/utils/jsmnx.h b/src/utils/jsmnx.h
new file mode 100644
index 000000000..077e605c8
--- /dev/null
+++ b/src/utils/jsmnx.h
@@ -0,0 +1,117 @@
+/* jsmnx.h -- extended version of the simple JSMN JSON parser
+ *
+ * Copyright (C) 2021 SINTEF
+ *
+ * Distributed under terms of the MIT license.
+ */
+
+/**
+  @file
+  @brief Extended version of the simple JSMN JSON parser
+
+  This header provides a few useful additional functions in addition to
+  those provided by jsmn.h.  When using jsmnx, you should import this file
+  instead of jsmn.h in your project.
+
+  We allow dependencies on the standard library in this file.
+
+  For completeness, this file also include prototypes for jsmn_init() and
+  jsmn_parse(), even though they are also declared in jsmn.h.
+
+  @see https://github.com/zserge/jsmn
+*/
+#ifndef JSMNX_H
+#define JSMNX_H
+
+#include <stddef.h>
+
+#define JSMN_HEADER
+#define JSMN_STRICT
+#define JSMN_PARENT_LINKS
+#include "jsmn.h"
+
+/** Chunck size when reallocating new chunks */
+#ifndef JSMN_CHUNK_SIZE
+#define JSMN_CHUNK_SIZE 4096
+#endif
+
+
+/**
+ * Initializes a JSON parser.
+ */
+void jsmn_init(jsmn_parser *parser);
+
+
+/**
+ * Run JSON parser.
+ *
+ * It parses a JSON data string into and array of tokens, each
+ * describing a single JSON object.
+ *
+ * Arguments
+ * ---------
+ *  - parser: pointer to initialized parser
+ *  - js: the JSON input string
+ *  - len: length of `js`
+ *  - tokens: pointer to a preallocated buffer of JSMN tokens.  May be
+ *    NULL in order to return the number of tokens in the input.
+ *  - num_tokens: the size of `tokens`
+ *
+ * Returns
+ * -------
+ * On success, it returns the number of tokens actually used by the parser.
+ * On error, one of the following (negative) codes is returned:
+ *
+ *  - JSMN_ERROR_INVAL: bad token, JSON string is corrupted
+ *  - JSMN_ERROR_NOMEM: not enough tokens, JSON string is too large
+ *  - JSMN_ERROR_PART:  JSON string is too short, expecting more JSON data
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
+               jsmntok_t *tokens, const unsigned int num_tokens);
+
+
+/**
+ * Like jsmn_parse(), but realloc's the buffer pointed to by `tokens_ptr`
+ * if it is too small.  `num_tokens_ptr` should point to the number of
+ * allocated tokens or be zero if `tokens_ptr` is not pre-allocated.
+ *
+ * Returns JSMN_ERROR_NOMEM on allocation error.
+ */
+int jsmn_parse_alloc(jsmn_parser *parser, const char *js,
+                     const size_t len, jsmntok_t **tokens_ptr,
+                     unsigned int *num_tokens_ptr);
+
+
+/**
+ * Returns number of sub-tokens contained in `t` or -1 on error.
+ */
+int jsmn_count(const jsmntok_t *t);
+
+
+/**
+ * Returns a pointer to the value of item `key` of JSMN object token `t`.
+ *
+ * `js` is the JSON source.
+ *
+ * Returns NULL on error.
+ */
+const jsmntok_t *jsmn_item(const char *js, const jsmntok_t *t, const char *key);
+
+
+/**
+ * Returns a pointer to the element `i` of JSMN array token `t`.
+ *
+ * `js` is the JSON source.
+ *
+ * Returns NULL on error.
+ */
+const jsmntok_t *jsmn_element(const char *js, const jsmntok_t *t, int i);
+
+
+/**
+ * Returns error message corresponding to return value from jsmn_parse().
+ */
+const char *jsmn_strerror(int r);
+
+
+#endif /* JSMNX_H */
diff --git a/src/utils/jstore.c b/src/utils/jstore.c
new file mode 100644
index 000000000..87d718baa
--- /dev/null
+++ b/src/utils/jstore.c
@@ -0,0 +1,320 @@
+/* jstore.c -- simple JSON storage
+ *
+ * Copyright (C) 2021 SINTEF
+ *
+ * Distributed under terms of the MIT license.
+ */
+#include "err.h"
+#include "compat.h"
+#include "jstore.h"
+
+
+#define FAIL(msg) do {                          \
+    err(1, msg); goto fail; } while (0)
+
+
+/* JSON store */
+struct _JStore {
+  map_str_t store;
+};
+
+
+/*
+ * Utility functions
+ * -----------------
+ */
+
+
+/* Read stream into an allocated buffer.
+   Returns a pointer to the buffer or NULL on error. */
+char *jstore_readfp(FILE *fp)
+{
+  char *q, *buf=NULL;
+  size_t n, bytes_left, bytes_read=0, size=256;
+  do {
+    if (ferror(fp)) FAIL("stream error");
+    size *= 2;
+    if (!(q = realloc(buf, size))) FAIL("reallocation failure");
+    buf = q;
+    bytes_left = size - bytes_read;
+    n = fread(buf+bytes_read, 1, bytes_left, fp);
+    bytes_read += n;
+  } while (n == bytes_left && !feof(fp));
+  assert(feof(fp));  // stream should be exausted
+  if (!(q = realloc(buf, bytes_read+1))) FAIL("reallocation failure");
+  buf = q;
+  buf[bytes_read] = '\0';
+  return buf;
+ fail:
+  if (buf) free(buf);
+  return NULL;
+}
+
+/* Read file into an allocated buffer.
+   Returns a pointer to the buffer or NULL on error. */
+char *jstore_readfile(const char *filename)
+{
+  char *buf;
+  FILE *fp = fopen(filename, "r");
+  if (!fp) return err(1, "cannot open file: \"%s\"", filename), NULL;
+  buf = jstore_readfp(fp);
+  fclose(fp);
+  if (!buf) err(1, "error reading from file \"%s\"", filename);
+  return buf;
+}
+
+/* Read file into an allocated buffer and parse it with JSMN.
+
+   The `tokens_ptr` and `num_tokens_ptr` arguments are passed on to
+   jsmn_parse_alloc().
+
+   Returns a pointer to the buffer or NULL on error. */
+char *jstore_readfile_to_jsmn(const char *filename, jsmntok_t **tokens_ptr,
+                              unsigned int *num_tokens_ptr)
+{
+  char *buf = jstore_readfile(filename);
+  jsmn_parser parser;
+  int r;
+  if (!buf) return NULL;
+  jsmn_init(&parser);
+  r = jsmn_parse_alloc(&parser, buf, strlen(buf), tokens_ptr, num_tokens_ptr);
+  if (r < 0) {
+    free(buf);
+    return errx(1, "error parsing json file \"%s\": %s",
+                filename, jsmn_strerror(r)), NULL;
+  }
+  return buf;
+}
+
+
+/*
+ * JStore API
+ * ----------
+ */
+
+/* Create a new JSON store and return it.  Returns NULL on error. */
+JStore *jstore_open(void)
+{
+  JStore *js = calloc(1, sizeof(JStore));
+  if (!js) return err(1, "allocation failure"), NULL;
+  map_init(&js->store);
+  return js;
+}
+
+/* Close JSON store.  Returns non-zero on error. */
+int jstore_close(JStore *js)
+{
+  const char *key;
+  map_iter_t miter = map_iter(&js->store);
+  while ((key = map_next(&js->store, &miter))) {
+    char **val = map_get(&js->store, key);
+    assert(val);
+    free(*val);
+  }
+  map_deinit(&js->store);
+  free(js);
+  return 0;
+}
+
+/* Add JSON value to store with given key.
+   If key already exists, it is replaced.
+   Returns non-zero on error. */
+int jstore_add(JStore *js, const char *key, const char *value)
+{
+  return jstore_addn(js, key, 0, value, 0);
+}
+
+/* Add JSON value to store with given key.
+   The lengths of the key and value are provided by `klen` and `vlen`,
+   respectively.  If the strings are NUL-terminated, the corresponding length
+   may be set to zero.
+   If key already exists, it is replaced.
+   Returns non-zero on error. */
+int jstore_addn(JStore *js, const char *key, size_t klen,
+                const char *value, size_t vlen)
+{
+  char *k=(char *)key, *v=NULL;
+  int stat;
+  if (!vlen) vlen = strlen(value);
+  if (klen && !(k = strndup(key, klen))) FAIL("allocation failure");
+  if (!(v = strndup(value, vlen))) FAIL("allocation failure");
+  stat = jstore_addstolen(js, k, v);
+  if (klen) free(k);
+  return stat;
+ fail:
+  if (klen) free(k);
+  if (v) free(v);
+  return 1;
+}
+
+/* Add JSON value to store with given key.
+   The store "steels" the ownership of the memory pointed to by `value`.
+   If key already exists, it is replaced.
+   Returns non-zero on error. */
+int jstore_addstolen(JStore *js, const char *key, const char *value)
+{
+  char **v;
+  if ((v = map_get(&js->store, key))) free(*v);  // free existing value
+  if (map_set(&js->store, key, (char *)value))
+    return err(1, "error adding key \"%s\" to JSON store", key);
+  return 0;
+}
+
+/* Returns JSON value for given key or NULL if the key isn't in the store.
+   This function can also be used to check if a key exists in the store. */
+const char *jstore_get(JStore *js, const char *key)
+{
+  char **p = map_get(&js->store, key);
+  return (p) ? *p : NULL;
+}
+
+/* Removes item corresponding to given key from JSON store.
+   Returns zero on success and 1 if `key` doesn't exists. */
+int jstore_remove(JStore *js, const char *key)
+{
+  char **v;
+  if ((v = map_get(&js->store, key))) {
+    free(*v);
+    map_remove(&js->store, key);
+    return 0;
+  }
+  return 1;
+}
+
+/* Update JSON store with values from `other`. Return non-zero on error. */
+int jstore_update(JStore *js, JStore *other)
+{
+  JStoreIter iter;
+  const char *key;
+  jstore_iter_init(other, &iter);
+  while ((key = jstore_iter_next(&iter))) {
+    const char *val = jstore_get(other, key);
+    assert(val);
+    if (jstore_add(js, key, val)) return 1;
+  }
+  return 0;
+}
+
+/* Update JSON store with values from JSMN token `tok`. The JSMN token must be
+   an object. Return non-zero on error. */
+int jstore_update_from_jsmn(JStore *js, const char *src, jsmntok_t *tok)
+{
+  int i;
+  jsmntok_t *tk = tok+1;
+  if (tok->type != JSMN_OBJECT) return err(1, "jsmn token must be an object");
+  for (i=0; i < tok->size; i++) {
+    jsmntok_t *tv = tk+1;
+    if (jstore_addn(js,
+                    src + tk->start, tk->end - tk->start,
+                    src + tv->start, tv->end - tv->start)) return 1;
+    tk += jsmn_count(tv) + 2;
+  }
+  return 0;
+}
+
+/* Update JSON store with values from file `filename`.
+   Return non-zero on error. */
+int jstore_update_from_file(JStore *js, const char *filename)
+{
+  jsmn_parser parser;
+  jsmntok_t *tokens=NULL;
+  unsigned int ntokens=0;
+  int r, stat;
+  char *buf;
+  if (!(buf = jstore_readfile(filename)))
+    return err(1, "error reading JSON file \"%s\"", filename);
+  jsmn_init(&parser);
+  r = jsmn_parse_alloc(&parser, buf, strlen(buf), &tokens, &ntokens);
+  if (r < 0) {
+    free(buf);
+    return err(1, "error parsing JSON file \"%s\": %s",
+               filename, jsmn_strerror(r));
+  }
+  stat = jstore_update_from_jsmn(js, buf, tokens);
+  free(tokens);
+  free(buf);
+  return stat;
+}
+
+/* Update `filename` from JSON store.
+   The file is first read and then rewritten while the store is unchanged.
+   Return non-zero on error. */
+int jstore_update_file(JStore *js, const char *filename)
+{
+  int retval=1;
+  JStore *js2=NULL;
+  if (!(js2 = jstore_open())) goto fail;
+  if (jstore_update_from_file(js2, filename)) goto fail;
+  if (jstore_update(js2, js)) goto fail;
+  if (jstore_to_file(js2, filename)) goto fail;
+  retval = 0;
+ fail:
+  if (js2) jstore_close(js2);
+  return retval;
+}
+
+/* Returns a malloc()'ed JSON string with the content of the store or
+   NULL on error. */
+char *jstore_to_string(JStore *js)
+{
+  map_iter_t iter = map_iter(&js->store);
+  char *buf=NULL;
+  size_t size=0;
+  int m, n=0, count=0;
+  const char *key;
+  if ((m = asnpprintf(&buf, &size, n, "{")) < 0) goto fail;
+  n += m;
+  while ((key = map_next(&js->store, &iter))) {
+    char **q, *sep = (count++ > 0) ? "," : "";
+    if (!(q = map_get(&js->store, key))) goto fail;
+    if ((m = asnpprintf(&buf, &size, n, "%s\n  \"%s\": %s",
+                        sep, key, *q)) < 0) goto fail;
+    n += m;
+  }
+  if ((m = asnpprintf(&buf, &size, n, "\n}\n")) < 0) goto fail;
+  n += m;
+  return buf;
+ fail:
+  return err(1, "error creating json string"), NULL;
+}
+
+/* Writes JSON store to file.  If `filename` exists, it is overwritten.
+   Returns non-zero on error. */
+int jstore_to_file(JStore *js, const char *filename)
+{
+  char *buf = jstore_to_string(js);
+  FILE *fp;
+  size_t n;
+  if (!buf) return 1;
+  if (!(fp = fopen(filename, "w"))) {
+    free(buf);
+    return err(1, "cannot write JSON store to file \"%s\"", filename);
+  }
+  n = fwrite(buf, strlen(buf), 1, fp);
+  fclose(fp);
+  free(buf);
+  return (n == 1) ? 0 : 1;
+}
+
+/* Initialise iterator.  Return non-zero on error. */
+int jstore_iter_init(JStore *js, JStoreIter *iter)
+{
+  iter->js = js;
+  iter->miter = map_iter(&js->store);
+  return 0;
+}
+
+/* Returns the key to the next item in or NULL if there are no more items
+   in the store.  Use jstore_get() to get the corresponding value. */
+const char *jstore_iter_next(JStoreIter *iter)
+{
+  return map_next(&iter->js->store, &iter->miter);
+}
+
+/* Deinitialise the iterator.  Returns non-zero on error.
+   In the current implementation, this function does nothing. */
+int jstore_iter_deinit(JStoreIter *iter)
+{
+  (void)(iter);  // unused
+  return 0;
+}
diff --git a/src/utils/jstore.h b/src/utils/jstore.h
new file mode 100644
index 000000000..9c655ba73
--- /dev/null
+++ b/src/utils/jstore.h
@@ -0,0 +1,136 @@
+/* jstore.h -- simple JSON storage
+ *
+ * Copyright (C) 2021 SINTEF
+ *
+ * Distributed under terms of the MIT license.
+ */
+
+/**
+  @file
+  @brief Simple JSON storage
+
+  This library maintains a simple JSON storage, whos root is a JSON object.
+  The keys identify storage items and the values are valid JSON string
+  representing the item values.
+
+  Items can be added, updated and removed from the storage.  Iteration
+  over all items in the storage is also supported.
+*/
+#ifndef _JSTORE_H
+#define _JSTORE_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "map.h"
+#include "jsmnx.h"
+
+
+/** JStore object */
+typedef struct _JStore JStore;
+
+/** JStore iterator object */
+typedef struct _JStoreIter {
+  JStore *js;
+  map_iter_t miter;
+} JStoreIter;
+
+
+
+/** @name Utility functions */
+/** @{ */
+
+/** Read stream into an allocated buffer.
+    Returns a pointer to the buffer or NULL on error. */
+char *jstore_readfp(FILE *fp);
+
+/** Read file into an allocated buffer.
+    Returns a pointer to the buffer or NULL on error. */
+char *jstore_readfile(const char *filename);
+
+/** Read file into an allocated buffer and parse it with JSMN.
+
+    The `tokens_ptr` and `num_tokens_ptr` arguments are passed on to
+    jsmn_parse_alloc().
+
+    Returns a pointer to the buffer or NULL on error. */
+char *jstore_readfile_to_jsmn(const char *filename, jsmntok_t **tokens_ptr,
+                              unsigned int *num_tokens_ptr);
+
+
+
+/** @} */
+/** @name JStore API */
+/** @{ */
+
+/** Create a new JSON store and return it.  Returns NULL on error. */
+JStore *jstore_open(void);
+
+/** Close JSON store.  Returns non-zero on error. */
+int jstore_close(JStore *js);
+
+/** Add JSON value to store with given key.
+    If key already exists, it is replaced.
+    Returns non-zero on error. */
+int jstore_add(JStore *js, const char *key, const char *value);
+
+/** Add JSON value to store with given key.
+    The lengths of the key and value are provided by `klen` and `vlen`,
+    respectively.  If the strings are NUL-terminated, the corresponding
+    length may be set to zero.
+    If key already exists, it is replaced.
+    Returns non-zero on error. */
+int jstore_addn(JStore *js, const char *key, size_t klen,
+                const char *value, size_t vlen);
+
+/** Add JSON value to store with given key.
+    The store "steels" the ownership of the memory pointed to by `value`.
+    If key already exists, it is replaced.
+    Returns non-zero on error. */
+int jstore_addstolen(JStore *js, const char *key, const char *value);
+
+/** Returns JSON value for given key or NULL if the key isn't in the store.
+    This function can also be used to check if a key exists in the store. */
+const char *jstore_get(JStore *js, const char *key);
+
+/** Removes item corresponding to given key from JSON store. */
+int jstore_remove(JStore *js, const char *key);
+
+/** Update JSON store with values from `other`. Return non-zero on error. */
+int jstore_update(JStore *js, JStore *other);
+
+/** Update JSON store with values from JSMN token `tok`. The JSMN token must
+    be an object. Return non-zero on error. */
+int jstore_update_from_jsmn(JStore *js, const char *src, jsmntok_t *tok);
+
+/** Update JSON store with values from file `filename`.
+   Return non-zero on error. */
+int jstore_update_from_file(JStore *js, const char *filename);
+
+/** Update `filename` from JSON store.
+    The file is first read and then rewritten while the store is unchanged.
+    Return non-zero on error. */
+int jstore_update_file(JStore *js, const char *filename);
+
+/** Returns a malloc()'ed JSON string with the content of the store or
+    NULL on error. */
+char *jstore_to_string(JStore *js);
+
+/** Writes JSON store to file.  If `filename` exists, it is overwritten.
+    Returns non-zero on error. */
+int jstore_to_file(JStore *js, const char *filename);
+
+
+/** Initialise iterator.  Return non-zero on error. */
+int jstore_iter_init(JStore *js, JStoreIter *iter);
+
+/** Returns the key to the next item in or NULL if there are no more items
+    in the store.  Use jstore_get() to get the corresponding value. */
+const char *jstore_iter_next(JStoreIter *iter);
+
+/** Deinitialise the iterator.  Returns non-zero on error.
+    In the current implementation, this function does nothing. */
+int jstore_iter_deinit(JStoreIter *iter);
+
+/** @} */
+
+#endif  /* _JSTORE_H */
diff --git a/src/utils/plugin.c b/src/utils/plugin.c
index e62901fd3..3ae3abd1a 100644
--- a/src/utils/plugin.c
+++ b/src/utils/plugin.c
@@ -502,7 +502,25 @@ int plugin_path_extend_prefix(PluginInfo *info, const char *prefix,
 
   Returns non-zero on error.
  */
-int plugin_path_remove(PluginInfo *info, int n)
+int plugin_path_delete(PluginInfo *info, int n)
 {
-  return fu_paths_remove(&info->paths, n);
+  return fu_paths_delete(&info->paths, n);
+}
+
+/*
+  Removes path `path`.  Returns non-zero if there is no such path.
+ */
+int plugin_path_remove(PluginInfo *info, const char *path)
+{
+  int i = plugin_path_index(info, path);
+  if (i < 0) return i;
+  return plugin_path_delete(info, i);
+}
+
+/*
+  Returns index of plugin path `path` or -1 on error.
+ */
+int plugin_path_index(PluginInfo *info, const char *path)
+{
+  return fu_paths_index(&info->paths, path);
 }
diff --git a/src/utils/plugin.h b/src/utils/plugin.h
index 17991916d..783a5a64a 100644
--- a/src/utils/plugin.h
+++ b/src/utils/plugin.h
@@ -213,8 +213,17 @@ int plugin_path_extend_prefix(PluginInfo *info, const char *prefix,
 
   Returns non-zero on error.
  */
-int plugin_path_remove(PluginInfo *info, int n);
+int plugin_path_delete(PluginInfo *info, int n);
 
+/**
+  Removes path `path`.  Returns non-zero if there is no such path.
+ */
+int plugin_path_remove(PluginInfo *info, const char *path);
+
+/**
+  Returns index of plugin path `path` or -1 on error.
+ */
+int plugin_path_index(PluginInfo *info, const char *path);
 
 
 #endif /* _PLUGINS_H */
diff --git a/src/utils/uuid.c b/src/utils/uuid.c
index b57136b73..dc1856a99 100644
--- a/src/utils/uuid.c
+++ b/src/utils/uuid.c
@@ -154,7 +154,7 @@ int uuid_create_random(uuid_s *uuid)
   char buf[UUID4_LEN];
   if (uuid4_generate(buf))
     return 1;
-  if (uuid_from_string(uuid, buf))
+  if (uuid_from_string(uuid, buf, 36))
     return 1;
   return 0;
 }
@@ -198,11 +198,11 @@ void uuid_as_string(uuid_s *uuid, char s[37])
 /* uuid_from_string -- set uuid from string s. Returns non-zero if s
    is not formatted as a valid uuid.  If uuid is none, only format
    checking is done. */
-int uuid_from_string(uuid_s *uuid, const char *s)
+int uuid_from_string(uuid_s *uuid, const char *s, size_t len)
 {
   uuid_s u;
   int i, n;
-  if (strlen(s) != 36)
+  if (len != 36)
     return 1;
 
   if (sscanf(s, "%8x-%4hx-%4hx-%n",
diff --git a/src/utils/uuid.h b/src/utils/uuid.h
index e8a104479..67176886c 100644
--- a/src/utils/uuid.h
+++ b/src/utils/uuid.h
@@ -97,9 +97,9 @@ int uuid_compare(uuid_s *u1, uuid_s *u2);
 /* uuid_as_string -- write uuid to string */
 void uuid_as_string(uuid_s *uuid, char s[37]);
 
-/* uuid_from_string -- set uuid from string s. Returns non-zero if s
-   is not formatted as a valid uuid.  If uuid is NULL, only format
-   checking is performed. */
-int uuid_from_string(uuid_s *uuid, const char *s);
+/* uuid_from_string -- set uuid from string `s` of length `len`.
+   Returns non-zero if `s` is not formatted as a valid uuid.
+   If `uuid` is NULL, only format checking is performed. */
+int uuid_from_string(uuid_s *uuid, const char *s, size_t len);
 
 #endif /* !__uuid_h__ */
diff --git a/storages/json/CMakeLists.txt b/storages/json/CMakeLists.txt
index 5a89214c0..5484b7f90 100644
--- a/storages/json/CMakeLists.txt
+++ b/storages/json/CMakeLists.txt
@@ -2,17 +2,13 @@
 #
 
 set(sources
-  json-utils.c
   dlite-json-storage.c
-  str.c
-  vector.c
   )
 
 add_definitions(-DHAVE_CONFIG_H)
 
 add_library(dlite-plugins-json SHARED ${sources})
 target_link_libraries(dlite-plugins-json
-  ${JANSSON_LIBRARIES}
   dlite
   dlite-utils
   )
@@ -20,11 +16,7 @@ target_include_directories(dlite-plugins-json PUBLIC
   ${CMAKE_CURRENT_BINARY_DIR}
   ${dlite-src_SOURCE_DIR}
   ${dlite-src_BINARY_DIR}
-  ${JANSSON_INCLUDE_DIRS}
   )
-if(${JANSSON_DEPENDENCIES})
-  add_dependencies(dlite-plugins-json ${JANSSON_DEPENDENCIES})
-endif()
 set_target_properties(dlite-plugins-json PROPERTIES
   WINDOWS_EXPORT_ALL_SYMBOLS ON
   )
diff --git a/storages/json/dlite-json-storage.c b/storages/json/dlite-json-storage.c
index cf5374c70..fd00a2495 100644
--- a/storages/json/dlite-json-storage.c
+++ b/storages/json/dlite-json-storage.c
@@ -5,982 +5,285 @@
 #include <string.h>
 #include <errno.h>
 
-#include <jansson.h>
-#include "json-utils.h"
-#include "str.h"
-
 #include "config.h"
 
-#include "boolean.h"
-#include "utils/strtob.h"
 #include "utils/err.h"
-
-#include "dlite-misc.h"
-#include "dlite-schemas.h"
-
-/*
-*/
+#include "utils/strtob.h"
+#include "utils/jstore.h"
+#include "utils/map.h"
 #include "dlite.h"
-#include "dlite-datamodel.h"
-
-
-/* Macro for getting rid of unused parameter warnings... */
-#define UNUSED(x) (void)(x)
-
-/* Convinient error macros */
-#define FAIL0(msg) \
-  do {err(-1, msg); goto fail;} while (0)
-
-#define FAIL1(msg, a1) \
-  do {err(-1, msg, a1); goto fail;} while (0)
-
-#define FAIL2(msg, a1, a2) \
-  do {err(-1, msg, a1, a2); goto fail;} while (0)
-
-#define FAIL3(msg, a1, a2, a3) \
-  do {err(-1, msg, a1, a2, a3); goto fail;} while (0)
+#include "dlite-storage-plugins.h"
+#include "dlite-macros.h"
 
-#define FAIL4(msg, a1, a2, a3, a4) \
-  do {err(1, msg, a1, a2, a3, a4); goto fail; } while (0)
+typedef struct { char uuid[DLITE_UUID_LENGTH+1]; } uuid_t;
+typedef map_t(uuid_t) map_uuid_t;
 
-#define FAIL5(msg, a1, a2, a3, a4, a5) \
-  do {err(1, msg, a1, a2, a3, a4, a5); goto fail; } while (0)
-
-
-/* Error macros for when DLiteDataModel instance d in available */
-#define DFAIL0(d, msg) \
-  do {err(-1, "%s/%s: " msg, d->s->location, d->uuid); \
-    goto fail;} while (0)
-
-#define DFAIL1(d, msg, a1) \
-  do {err(-1, "%s/%s: " msg, d->s->location, d->uuid, a1); \
-    goto fail;} while (0)
-
-#define DFAIL2(d, msg, a1, a2) \
-  do {err(-1, "%s/%s: " msg, d->s->location, d->uuid, a1, a2); \
-    goto fail;} while (0)
-
-#define DFAIL3(d, msg, a1, a2, a3) \
-  do {err(-1, "%s/%s: " msg, d->s->location, d->uuid, a1, a2, a3); \
-    goto fail;} while (0)
-
-#define DFAIL4(d, msg, a1, a2, a3, a4)                            \
-  do {err(-1, "%s/%s: " msg, d->s->location, d->uuid, a1, a2, a3, a4); \
-    goto fail;} while (0)
-
-
-
-/* How the json-data is organised */
-typedef enum {
-  fmtNormal,      /* format output as normal data */
-  fmtMeta,        /* format output as SOFT metadata */
-  fmtSchema       /* schemas or meta-metadata */
-} DataFormat;
-
-/* Storage for json backend. */
+/** Storage for json backend. */
 typedef struct {
   DLiteStorage_HEAD
-  json_t *root;       /* json root object */
-  int compact;        /* whether to write output in compact format */
-  DataFormat fmt;     /* layout of json-data */
+  JStore *jstore;       /* json storage */
+  DLiteJsonFlag flags;  /* output flags */
+  int changed;          /* whether the storage is changed */
+  map_uuid_t ids;       /* maps uuids to ids */
 } DLiteJsonStorage;
 
-/* Data model for json backend. */
-typedef struct {
-  DLiteDataModel_HEAD
-  json_t *instance;     /* json object to instance, borrowed reference */
-  json_t *meta;         /* json object to metadata, borrowed reference */
-  json_t *dimensions;   /* json object to dimensions, borrowed reference */
-  json_t *properties;   /* json object to properties, borrowed reference */
-  json_t *relations;    /* json object to relations, borrowed reference */
-  DataFormat fmt;       /* layout of json-data */
-} DLiteJsonDataModel;
-
-
-/*
-  Returns a string value or NULL on error.
- */
-const char *object_get_string(json_t *obj, const char *key)
-{
-  const char *retval=NULL;
-  json_t *value = json_object_get(obj, key);
-  if (json_is_string(value))
-    retval = json_string_value(value);
-  return retval;
-}
-
-void object_set_string(json_t *obj, const char *key, const char *val)
-{
-  json_t *value = json_object_get(obj, key);
-  if (json_is_string(value))
-    json_string_set(value, val);
-  else {
-    value = json_string(val);
-    json_object_set_new(obj, key, value);
-  }
-}
-
-int object_get_integer(json_t *obj, const char *key)
-{
-  int retval=0;
-  json_t *value = json_object_get(obj, key);
-  if (json_is_integer(value))
-     retval = json_integer_value(value);
-  return retval;
-}
 
-void object_set_integer(json_t *obj, const char *key, const int val)
+/** Returns default mode:
+    - 'w': if `uri` if we can't open uri
+    - 'a': if `uri` is in data format
+    - 'r': otherwise
+*/
+static int default_mode(const char *uri)
 {
-  json_t *value = json_object_get(obj, key);
-  if (json_is_integer(value))
-    json_integer_set(value, val);
-  else {
-    value = json_integer(val);
-    json_object_set_new(obj, key, value);
-  }
+  int mode;
+  JStore *js = jstore_open();
+  if (jstore_update_from_file(js, uri))
+    mode = 'w';
+  else
+    mode = (jstore_get(js, "properties")) ? 'r' : 'a';
+  jstore_close(js);
+  return mode;
 }
 
-void object_set_real(json_t *obj, const char *key, const double val)
-{
-  json_t *value = json_object_get(obj, key);
-  if (json_is_real(value))
-    json_real_set(value, val);
-  else {
-    value = json_real(val);
-    json_object_set_new(obj, key, value);
-  }
-}
 
 /**
   Returns an url to the metadata.
 
   Valid `options` are:
 
-  - mode : append | r | w
+  - mode : r | w | a
       Valid values are:
-      - append   Append to existing file or create new file (default)
-      - r        Open existing file for read-only
-      - w        Truncate existing file or create new file
-  - compact : yes | no
-      Whether to write output in compact format
-  - meta : yes | no
-      Whether to format output as metadata
-  - useid: translate | require | keep
-      How to use the ID
+      - r   Open existing file for read-only
+      - w   Truncate existing file or create new file
+      - a   Append to existing file or create new file (default)
+  - with-uuid : yes | no
+      Whether to write output in compact format.
+  - as-data : yes | no
+  - meta : yes | no (deprecated)
+      Whether to format output as metadata. Alias for `with-uuid`
+  - compact : yes | no (deprecated)
+      Whether to write output in compact format. Alias for `as-data`
+  - useid: translate | require | keep (deprecated)
+      How to use the ID.
  */
-DLiteStorage *
-dlite_json_open(const DLiteStoragePlugin *api, const char *uri,
-                const char *options)
+DLiteStorage *json_open(const DLiteStoragePlugin *api, const char *uri,
+                        const char *options)
 {
   DLiteJsonStorage *s;
   DLiteStorage *retval=NULL;
-  json_error_t error;
-  size_t n;
   char *mode_descr = "How to open storage.  Valid values are: "
-    "\"append\" (appends to existing storage or creates a new one); "
     "\"r\" (read-only); "
-    "\"w\" (truncate existing storage or create a new one)";
-  char *useid_descr = "How to use ID.  Valid values are: "
-    "\"translate\" (translate to UUID if ID is not a valid UUID); "
-    "\"require\" (require a valid UUID as ID); "
-    "\"keep\" (keep ID as it is)";
+    "\"w\" (truncate existing storage or create a new one); "
+    "\"a\" (appends to existing storage or creates a new one)";
   DLiteOpt opts[] = {
-    {'m', "mode",    "append", mode_descr},
-    {'c', "compact", "false",  "Whether to write output in compact format"},
-    {'M', "meta",    "false",  "Whether to format output as metadata"},
-    {'u', "useid",   "translate",  useid_descr},
+    {'m', "mode",      "", mode_descr},
+    {'u', "with-uuid", "false", "Whether to include uuid in output"},
+    {'d', "as-data",   "false", "Whether to write metadata as data"},
+    {'c', "compact",   "false", "Aliad for `as-data=false` (deprecated)"},
+    {'M', "meta",      "false", "Alias for `with-uuid` (deprecated)"},
+    {'U', "useid",     "",      "Unused (deprecated)"},
     {0, NULL, NULL, NULL}
   };
-  char *optcopy = (options) ? strdup(options) : NULL;
-  const char **mode = &opts[0].value;
-  int meta;
-  UNUSED(api);
-
-  if (!(s = calloc(1, sizeof(DLiteJsonStorage)))) FAIL0("allocation failure");
+  int load;  // whether to load uri
 
   /* parse options */
+  char *optcopy = (options) ? strdup(options) : NULL;
   if (dlite_option_parse(optcopy, opts, 1)) goto fail;
-  if (strcmp(*mode, "append") == 0) {  /* default */
-    FILE *fp;
-    if ((fp = fopen(uri, "r"))) {  /* `uri` exists */
-      fclose(fp);
-      s->root = json_load_file(uri, 0, &error);
-    } else {  /* `uri` doesn't exists */
-      s->root = json_object();
-    }
-    s->writable = 1;
-  } else if (strcmp(*mode, "r") == 0) {
-    s->root = json_load_file(uri, 0, &error);
+
+  char mode = *opts[0].value;
+  int withuuid = atob(opts[1].value);
+  int asdata = atob(opts[2].value);
+
+  if (!(s = calloc(1, sizeof(DLiteJsonStorage)))) FAIL("allocation failure");
+  s->api = api;
+
+  if (!(s->jstore = jstore_open())) goto fail;
+
+  if (!mode) mode = default_mode(uri);
+  switch (mode) {
+  case 'r':
+    load = 1;
     s->writable = 0;
-  } else if (strcmp(*mode, "w") == 0) {
-    s->root = json_object();
+    break;
+  case 'a':
+    load = 1;
+    s->writable = 1;
+    break;
+  case 'w':
+    load = 0;
     s->writable = 1;
-  } else {
-    FAIL1("invalid \"mode\" value: '%s'. Must be \"append\", \"r\" "
-          "(read-only) or \"w\" (write)", *mode);
+    break;
+  default:
+    FAIL1("invalid \"mode\" value: '%c'. Must be \"r\" (read-only), "
+          "\"w\" (write) or \"a\" (append)", mode);
   }
 
-  if ((s->compact = atob(opts[1].value)) == -1)
-    errx(1, "invalid boolean value: '%s'.  Assuming true.", opts[1].value);
-
-  if ((meta = atob(opts[2].value)) == 0)
-    s->fmt = fmtNormal;
-  else {
-    s->fmt = fmtMeta;
-    if (meta < 0)
-      errx(1, "invalid boolean value: '%s'.  Assuming true.", opts[2].value);
+  if (load) {
+    DLiteJsonFormat fmt = dlite_jstore_loadf(s->jstore, uri);
+    if (fmt < 0) goto fail;
+    if (fmt == dliteJsonMetaFormat) s->writable = 0;
+    dlite_storage_paths_append(uri);
   }
 
-  if (strcmp(opts[3].value, "require") == 0) {
-    s->idflag = dliteIDRequireUUID;
-  } else if (strcmp(opts[3].value, "keep") == 0) {
-    s->idflag = dliteIDKeepID;
-  } else {
-    s->idflag = dliteIDTranslateToUUID;
-  }
+  if (withuuid < 0) FAIL1("invalid boolean value for `with-uuid=%s`.",
+                          opts[1].value);
+  if (asdata < 0) FAIL1("invalid boolean value for `as-data=%s`.",
+                        opts[2].value);
+  if (atob(opts[3].value) > 0) asdata = 0;
+  if (atob(opts[4].value) > 0) withuuid = 1;
 
-  if (s->root == NULL) {
-    n = strlen(error.text);
-    if (n > 0)
-      err(1, "JSON parse error on line %d: %s\n", error.line, error.text);
-    else
-      err(1, "JSON parse error on line %d\n", error.line);
-    FAIL2("cannot open: '%s' with options '%s'", uri, options);
-  }
-  if (!json_is_object(s->root))
-    FAIL1("expected an object as root in json file: '%s'", uri);
+  if (withuuid) s->flags |= dliteJsonWithUuid;
+  if (asdata) s->flags |= dliteJsonMetaAsData;
 
   retval = (DLiteStorage *)s;
 
  fail:
   if (optcopy) free(optcopy);
-  if (!retval && s) {
-    if (s->root) json_decref(s->root);
-    free(s);
-  }
-  return retval;
-}
-
-/**
-  Closes data handle json. Returns non-zero on error.
- */
-int dlite_json_close(DLiteStorage *s)
-{
-  DLiteJsonStorage *storage = (DLiteJsonStorage *)s;
-  int nerr=0;
-  if (storage->writable == 1) {
-    size_t flags=0;
-    flags |= (storage->compact) ? JSON_COMPACT : JSON_INDENT(2);
-    json_dump_file(storage->root, storage->location, flags);
-  }
-  json_decref(storage->root);
-  return nerr;
-}
+  if (!retval && s) dlite_storage_close((DLiteStorage *)s);
 
-/**
-  Creates a new datamodel for json storage `s`.
-
-  If `id` exists in the root of `s`, the datamodel should describe the
-  corresponding instance.
-
-  Returns the new datamodel or NULL on error.
-*/
-DLiteDataModel *dlite_json_datamodel(const DLiteStorage *s, const char *id)
-{
-  DLiteJsonDataModel *d=NULL;
-  DLiteDataModel *retval=NULL;
-  DLiteJsonStorage *storage = (DLiteJsonStorage *)s;
-  char uuid[DLITE_UUID_LENGTH + 1];
-  json_t *data=NULL;
-  json_t *jname, *jver, *jns;
-
-  if (!(d = calloc(1, sizeof(DLiteJsonDataModel))))
-    FAIL0("allocation failure");
-
-  if (id) {
-    if (dlite_storage_get_idflag(s) == dliteIDKeepID) {
-      data = json_object_get(storage->root, id);
-    } else  {
-      if (dlite_get_uuid(uuid, id) < 0) goto fail;
-      data = json_object_get(storage->root, uuid);
-    };
-  }
-
-  if (data) {
-    /* Instance `id` exists - assign datamodel... */
-    if (!json_is_object(data))
-      FAIL2("expected a json object for instance '%s' in '%s'",
-            id, storage->location);
-    d->instance = data;
-    d->meta = json_object_get(data, "meta");
-    d->dimensions = json_object_get(data, "dimensions");
-    d->properties = json_object_get(data, "properties");
-    d->relations = json_object_get(data, "relations");
-    if (!d->properties) {
-      d->properties = data;
-    }
-
-  } else if ((jns = json_object_get(storage->root, "namespace")) &&
-             (jver = json_object_get(storage->root, "version")) &&
-             (jname = json_object_get(storage->root, "name"))) {
-
-    if (id && !storage->writable) {
-      char uuid2[DLITE_UUID_LENGTH + 1];
-      const char *name = json_string_value(jname);
-      const char *version = json_string_value(jver);
-      const char *namespace = json_string_value(jns);
-      char *uri2 = dlite_join_meta_uri(name, version, namespace);
-      if (dlite_get_uuid(uuid2, uri2) < 0) goto fail;
-      free(uri2);
-      if (strcmp(uuid2, uuid) != 0)
-	FAIL5("uri %s/%s/%s in storage %s doesn't match id: %s",
-	      namespace, version, name, storage->location, id);
-    }
-
-    /* Instance is a metadata definition */
-    data = storage->root;
-    d->instance = data;
-    //if (!(d->meta = json_object_get(data, "meta"))) d->fmt = fmtMeta;
-    d->fmt = fmtMeta;
-    d->meta = json_object_get(data, "meta");
-    d->dimensions = json_object_get(data, "dimensions");
-    d->properties = json_object_get(data, "properties");
-    d->relations = json_object_get(data, "relations");
-
-  } else if ((jns = json_object_get(storage->root, "schema_namespace")) &&
-	     (jver = json_object_get(storage->root, "schema_version")) &&
-             (jname = json_object_get(storage->root, "schema_name"))) {
-
-    if (id && !storage->writable) {
-      char uuid2[DLITE_UUID_LENGTH + 1];
-      const char *name = json_string_value(jname);
-      const char *version = json_string_value(jver);
-      const char *namespace = json_string_value(jns);
-      char *uri2 = dlite_join_meta_uri(name, version, namespace);
-      if (dlite_get_uuid(uuid2, uri2) < 0) goto fail;
-      free(uri2);
-      if (strcmp(uuid2, uuid) != 0)
-	FAIL5("uri %s/%s/%s in storage %s doesn't match id: %s",
-	      namespace, version, name, storage->location, id);
-    }
-
-    /* Instance is a meta-metadata definition (schema) */
-    data = storage->root;
-    d->instance = data;
-    //if (!(d->meta = json_object_get(data, "meta"))) d->fmt = fmtSchema;
-    d->fmt = fmtSchema;
-    d->meta = json_object_get(data, "meta");
-    d->dimensions = json_object_get(data, "schema_dimensions");
-    d->properties = json_object_get(data, "schema_properties");
-    d->relations = json_object_get(data, "schema_relations");
-
-  } else {
-    /* Instance `uuid` does not exists - create new instance and
-       assign the datamodel... */
-    if (!storage->writable)
-      FAIL2("cannot create new instance '%s' in read-only storage %s",
-            uuid, storage->location);
-    d->fmt = storage->fmt;
-    switch (d->fmt) {
-    case fmtNormal:
-      d->instance = json_object();
-      json_object_set_new(storage->root, uuid, d->instance);
-      d->meta = json_object();
-      json_object_set_new(d->instance, "meta", d->meta);
-      d->dimensions = json_object();
-      json_object_set_new(d->instance, "dimensions", d->dimensions);
-      d->properties = json_object();
-      json_object_set_new(d->instance, "properties", d->properties);
-      //d->relations = json_object();
-      //json_object_set_new(d->instance, "relations", d->properties);
-      break;
-    case fmtMeta:
-    case fmtSchema:
-      d->instance = d->properties = storage->root;
-      break;
-    }
-  }
-
-  retval = (DLiteDataModel *)d;
- fail:
-  if (!retval && d) free(d);
   return retval;
 }
 
-/**
-  Closes data handle. Returns non-zero on error.
- */
-int dlite_json_datamodel_free(DLiteDataModel *d)
-{
-  UNUSED(d);
-  /* All json data is owned by the root node, which is released in
-     dlite_json_close(). Nothing to free here... */
-  return 0;
-}
-
 
 /**
-  Returns pointer to (malloc'ed) metadata uri or NULL on error.
- */
-char *dlite_json_get_metadata(const DLiteDataModel *d)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  const char *name=NULL;
-  const char *version=NULL;
-  const char *space=NULL;
-
-  if (!data->meta) {
-    switch (data->fmt) {
-    case fmtMeta:     return strdup(DLITE_ENTITY_SCHEMA);
-    case fmtSchema:   return strdup(DLITE_BASIC_METADATA_SCHEMA);
-    default:
-      return err(1, "unexpected json format: %d", data->fmt), NULL;
-    }
-  } else if (json_is_string(data->meta)) {
-    return strdup(json_string_value(data->meta));
-  } else if (json_is_object(data->meta)) {
-    name = object_get_string(data->meta, "name");
-    version = object_get_string(data->meta, "version");
-    space = object_get_string(data->meta, "namespace");
-    return dlite_join_meta_uri(name, version, space);
-  } else {
-    return errx(1, "invalid \"meta\" value"), NULL;
-  }
-}
-
-void dlite_json_resolve_dimensions(DLiteDataModel *d, const DLiteMeta *meta)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  const DLiteProperty *prop;
-  const DLiteDimension *dim;
-  json_t *obj;
-  size_t i;
-  int j;
-  ivec_t *shape = NULL;
-  if (data->fmt == fmtNormal) {
-    if (meta->_ndimensions != json_object_size(data->dimensions)) {
-      if (!json_is_object(data->dimensions))
-        data->dimensions = json_object();
-      for(i=0; i < meta->_ndimensions; i++) {
-        dim = dlite_meta_get_dimension_by_index(meta, i);
-        obj = json_object_get(data->dimensions, dim->name);
-        if (!obj) {
-          json_object_set_new(data->dimensions, dim->name, json_integer(0));
-        }
-      }
-      for(i=0; i < meta->_nproperties; i++) {
-        prop = dlite_meta_get_property_by_index(meta, i);
-        if (prop->ndims > 0) {
-          obj = json_object_get(data->properties, prop->name);
-          shape = json_array_dimensions(obj);
-          if ((int)(ivec_size(shape)) == prop->ndims) {
-            for(j=0; j < prop->ndims; j++) {
-              obj = json_object_get(data->dimensions, prop->dims[j]);
-              json_integer_set(obj, shape->data[j]);
-            }
-          }
-        }
-      }
-    }
-  }
-  if (shape) ivec_free(shape);
-}
-
-/**
-  Returns the size of dimension `name` or -1 on error.
+  Closes data handle json. Returns non-zero on error.
  */
-int dlite_json_get_dimension_size(const DLiteDataModel *d, const char *name)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  json_t *value;
-  switch (data->fmt) {
-  case fmtNormal:
-    if (!(value = json_object_get(data->dimensions, name)))
-      return err(-1, "no dimension named '%s'", name);
-    if (!json_is_integer(value))
-      return err(-1, "value of dimension '%s' is not an integer", name);
-    return json_integer_value(value);
-
-  case fmtMeta:
-  case fmtSchema:
-    if (strcmp(name, "ndimensions") == 0)
-      return json_array_size(data->dimensions);
-    else if (strcmp(name, "nproperties") == 0)
-      return json_array_size(data->properties);
-    else if (strcmp(name, "nrelations") == 0)
-      return json_array_size(data->relations);
-    else
-      return err(-1, "expedted metadata dimension names are 'ndimensions', "
-                 "'nproperties' or 'nrelations'; got '%s'", name);
-  }
-  assert(0);  /* never reached */
-  return -1;
-}
-
-
-/* Recursive help function for handling n-dimensioal arrays */
-static int getdim(size_t d, const json_t *arr, void **pptr,
-                  DLiteType type, size_t size,
-                  size_t ndims, const size_t *dims,
-                  json_t *root)
+int json_close(DLiteStorage *s)
 {
-  size_t i;
-  if (d < ndims) {
-    if (json_array_size(arr) != dims[d])
-      return errx(1, "length of dimension %lu is %lu, expected %lu",
-                  (unsigned long)d,
-                  (unsigned long)json_array_size(arr),
-                  (unsigned long)dims[d]);
-    for (i=0; i<dims[d]; i++) {
-      const json_t *a = json_array_get(arr, i);
-      if (getdim(d+1, a, pptr, type, size, ndims, dims, root)) return 1;
-    }
-  } else {
-    if (dlite_json_get_value(*pptr, arr, type, size, root)) return 1;
-    *((char **)pptr) += size;
-  }
-  return 0;
+  DLiteJsonStorage *js = (DLiteJsonStorage *)s;
+  int stat=0;
+  if (js->writable && js->changed)
+    stat = jstore_to_file(js->jstore, js->location);
+  stat |= jstore_close(js->jstore);
+  return stat;
 }
 
 
 /**
-  Copies property `name` to memory pointed to by `ptr`.
-  Returns non-zero on error.
+  Load instance `id` from storage `s` and return it.
+  NULL is returned on error.
  */
-int dlite_json_get_property(const DLiteDataModel *d, const char *name,
-                            void *ptr, DLiteType type, size_t size,
-                            size_t ndims, const size_t *dims)
+DLiteInstance *json_load(const DLiteStorage *s, const char *id)
 {
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  json_t *value;
-
-  switch (data->fmt) {
-  case fmtNormal:
-    if (!(value = json_object_get(data->properties, name)))
-      return errx(1, "no such key in json data: %s", name);
-    break;
-  case fmtMeta:
-  case fmtSchema:
-    if (!(value = json_object_get(data->instance, name)))
-      return errx(1, "no such key in json data: %s", name);
-    break;
-  }
-
-  if (ndims) {
-    if (getdim(0, value, &ptr, type, size, ndims, dims, data->instance))
-      return 1;
-  } else {
-    if (dlite_json_get_value(ptr, value, type, size, data->instance))
-      return 1;
-  }
-
-  return 0;
-}
-
-
-/**
-  Sets metadata.  Returns non-zero on error.
-*/
-int dlite_json_set_metadata(DLiteDataModel *d, const char *metadata)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  char *name, *version, *namespace;
-
-  switch (data->fmt) {
-  case fmtNormal:
-    if (dlite_split_meta_uri(metadata, &name, &version, &namespace))
-      return 1;
-    object_set_string(data->meta, "name", name);
-    object_set_string(data->meta, "version", version);
-    object_set_string(data->meta, "namespace", namespace);
-    free(name);
-    free(version);
-    free(namespace);
-    break;
-  case fmtMeta:
-  case fmtSchema:
-    object_set_string(data->instance, "meta", metadata);
-    break;
-  }
-  return 0;
-}
-
-/**
-  Sets size of dimension `name`.  Returns non-zero on error.
-*/
-int dlite_json_set_dimension_size(DLiteDataModel *d, const char *name, size_t size)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  object_set_integer(data->dimensions, name, (int)size);
-  return 0;
-}
-
-
-/* A recursive help function to handle n-dimensional arrays. Args:
-     d : current dimension
-     arr : json array to fil out
-     pptr : pointer to pointer to current data point
-     size : size of each data point
-     ndims : number of dimensions
-     dims : length of each dimension (length: ndims)
-*/
-static json_t *setdim(size_t d, void **pptr,
-                      DLiteType type, size_t size,
-                      size_t ndims, const size_t *dims,
-                      json_t *root)
-{
-  int i;
-  json_t *item;
-  if (d < ndims) {
-    json_t *arr = json_array();
-    for (i=0; i<(int)dims[d]; i++) {
-      if (!(item = setdim(d + 1, pptr, type, size, ndims, dims, root)))
-        return NULL;
-      json_array_append_new(arr, item);
+  DLiteJsonStorage *js = (DLiteJsonStorage *)s;
+  const char *buf=NULL;
+  char uuid[DLITE_UUID_LENGTH+1];
+
+  if (!id || !*id) {
+    JStoreIter iter;
+    if (jstore_iter_init(js->jstore, &iter)) goto fail;
+    if (!(id = jstore_iter_next(&iter)))
+      FAIL1("cannot load instance from empty storage \"%s\"", s->location);
+    if (jstore_iter_next(&iter)) {
+      FAIL1("id is required when loading from storage with more "
+            "than one instance: %s", s->location);
     }
-    return arr;
-  }  else {
-    item = dlite_json_set_value(*pptr, type, size, root);
-    *((char **)pptr) += size;
-    return item;
+    if (jstore_iter_deinit(&iter)) goto fail;
+  } else if (dlite_get_uuid(uuid, id) == 5) {
+    buf = jstore_get(js->jstore, uuid);
   }
-  assert(0);
+  if (!buf && !(buf = jstore_get(js->jstore, id)))
+    FAIL2("no instance with id \"%s\" in storage \"%s\"", id, s->location);
+  return dlite_json_sscan(buf, id, NULL);
+ fail:
+  return NULL;
 }
 
 
 /**
-  Sets property `name` to the memory (of `size` bytes) pointed to by `ptr`.
-  Returns non-zero on error.
+  Saves instance `inst` to storage `s`.  Returns non-zero on error.
 */
-int dlite_json_set_property(DLiteDataModel *d, const char *name,
-                            const void *ptr,
-                            DLiteType type, size_t size,
-                            size_t ndims, const size_t *dims)
+int json_save(DLiteStorage *s, const DLiteInstance *inst)
 {
-  DLiteJsonDataModel *datamodel = (DLiteJsonDataModel *)d;
-  json_t *item;
-  if (ndims) {
-    if (!(item = setdim(0, (void **)&ptr, type, size, ndims, dims,
-                        datamodel->instance))) {
-      return 1;
-    }
-    json_object_set_new(datamodel->properties, name, item);
-  } else {
-    if (!(item = dlite_json_set_value(ptr, type, size, datamodel->instance))) {
-      return 1;
-    }
-    json_object_set_new(datamodel->properties, name, item);
-  }
+  DLiteJsonStorage *js = (DLiteJsonStorage *)s;
+  if (!s->writable)
+    return errx(1, "storage \"%s\" is not writable", s->location);
+  if (dlite_jstore_add(js->jstore, inst, js->flags)) return 1;
+  js->changed = 1;
   return 0;
 }
 
 
 /**
-  Returns a NULL-terminated array of string pointers to instance UUID's.
-  The caller is responsible to free the returned array.
-  Options is ignored.
-*/
-char **dlite_json_get_uuids(const DLiteStorage *s)
-{
-  DLiteJsonStorage *storage = (DLiteJsonStorage *)s;
-  int i, n;
-  char **names=NULL;
-  const char *key;
-  void *iter;
-  size_t len;
-  int ok = 1;
-  const char *namespace, *version, *name;
-  json_t *json;
-
-  if ((json = json_object_get(storage->root, "name")) &&
-      (name = json_string_value(json)) &&
-      (json = json_object_get(storage->root, "version")) &&
-      (version = json_string_value(json)) &&
-      (json = json_object_get(storage->root, "namespace")) &&
-      (namespace = json_string_value(json)) &&
-      (names = calloc(2, sizeof(char *))) &&
-      (names[0] = dlite_join_meta_uri(name, version, namespace)))
-    return names;
-
-  n = json_object_size(storage->root);
-  if (n > 0) {
-    if (!(names = malloc((n + 1)*sizeof(char *)))) {
-      ok = 0;
-    } else {
-      iter = json_object_iter(storage->root);
-      i = 0;
-      while(iter) {
-        key = json_object_iter_key(iter);
-        len = strlen(key) + 1;
-        if (!(names[i] = malloc(len))) {
-          ok = 0;
-          break;
-        }
-        memcpy(names[i], key, len);
-        i++;
-        iter = json_object_iter_next(storage->root, iter);
-      }
-      names[n] = NULL;
-    }
-  }
-
-  if (ok == 0) {
-    if (names) {
-      for(i=0; i < n; i++)
-        if (names[i])
-          free(names[i]);
-      free(names);
-      names = NULL;
-    }
-  }
-
-  return names;
-}
-
+  Creates and returns a new iterator used by dlite_json_iter_next().
 
-/**
-  Returns a positive value if dimension `name` is defined, zero if it
-  isn't and a negative value on error.
- */
-int dlite_json_has_dimension(const DLiteDataModel *d, const char *name)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  json_t *dim = json_object_get(data->dimensions, name);
-  return dim != NULL;
-}
+  If `metaid` is not NULL, dlite_json_iter_next() will only iterate over
+  instances whos metadata corresponds to this id.
 
-/**
-  Returns a positive value if property `name` is defined, zero if it
-  isn't and a negative value on error.
+  Returns new iterator or NULL on error.
  */
-int dlite_json_has_property(const DLiteDataModel *d, const char *name)
+void *json_iter_create(const DLiteStorage *s, const char *metaid)
 {
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  json_t *prop = json_object_get(data->properties, name);
-  return prop != NULL;
+  DLiteJsonStorage *js = (DLiteJsonStorage *)s;
+  return dlite_jstore_iter_create(js->jstore, metaid);
 }
 
-
 /**
-   If the uuid was generated from a unique name, return a pointer to a
-   newly malloc'ed string with this name.  Otherwise NULL is returned.
-*/
-char *dlite_json_get_dataname(const DLiteDataModel *d)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  return (char *)object_get_string(data->instance, "dataname");
-}
+  Writes the uuid of the next instance to `buf`, where `iter` is an
+  iterator returned by dlite_json_iter_create().
 
-/**
-  Gives the instance a name.  This function should only be called
-  if the uuid was generated from `name`.
-  Returns non-zero on error.
-*/
-int dlite_json_set_dataname(DLiteDataModel *d, const char *name)
-{
-  DLiteJsonDataModel *data = (DLiteJsonDataModel *)d;
-  object_set_string(data->instance, "dataname", name);
-  return 0;
-}
-
-
-char *dlite_json_uri(json_t *obj)
-{
-  const char *name=NULL;
-  const char *version=NULL;
-  const char *namespace=NULL;
-  const char *uri;
-  int n = 0;
-
-  if (json_is_object(obj)) {
-    if ((uri = object_get_string(obj, "uri")) && !str_is_whitespace(uri))
-      return (char *)uri;
-
-    name = object_get_string(obj, "name");
-    if (!str_is_whitespace(name))
-      n++;
-    version = object_get_string(obj, "version");
-    if (!str_is_whitespace(version))
-      n++;
-    namespace = object_get_string(obj, "namespace");
-    if (!str_is_whitespace(namespace))
-      n++;
-
-    if (n == 3)
-      return dlite_join_meta_uri(name, version, namespace);
-  }
-  return NULL;
-}
-
-/* Assigns DLiteDimension from a json object */
-int dlite_json_entity_dim(const json_t *obj, DLiteDimension *dim)
-{
-  if (!json_is_object(obj)) return 1;
-  dim->name = str_copy(json_string_value(json_object_get(obj, "name")));
-  dim->description =
-    str_copy(json_string_value(json_object_get(obj, "description")));
-  return 0;
-}
-
-/* Assigns DLiteProperty from a json object */
-int dlite_json_entity_prop(const json_t *obj, size_t ndim,
-                           const DLiteDimension *d, DLiteProperty *prop)
+  Returns zero on success, 1 if there are no more UUIDs to iterate
+  over and a negative number on other errors.
+ */
+int json_iter_next(void *iter, char *buf)
 {
-  const char *ptype;
-  json_t *dims;
-  json_t *item;
-  size_t i, size;
-  UNUSED(d);
-
-  if (!json_is_object(obj)) return 1;
-  prop->name = str_copy(json_string_value(json_object_get(obj, "name")));
-  ptype = json_string_value(json_object_get(obj, "type"));
-  dlite_type_set_dtype_and_size(ptype, &(prop->type), &(prop->size));
-  dims = json_object_get(obj, "dims");
-  size = json_array_size(dims);
-  assert(ndim == size);
-  prop->ndims = (int)size;
-  if (prop->ndims > 0) {
-    for(i = 0; i < size; i++) {
-      const char *s;
-      item = json_array_get(dims, i);
-      if (!(s = json_string_value(item)))
-        return err(1, "property dimensions should be strings");
-      prop->dims[i] = strdup(s);
-    }
-  }
-  prop->unit =
-    str_copy(json_string_value(json_object_get(obj, "unit")));
-  prop->iri =
-    str_copy(json_string_value(json_object_get(obj, "iri")));
-  prop->description =
-    str_copy(json_string_value(json_object_get(obj, "description")));
+  const char *id;
+  if (!(id = dlite_jstore_iter_next(iter))) return 1;
+  if (dlite_get_uuid(buf, id) < 0) return -1;
   return 0;
 }
 
-/* Creates a DLiteMeta from a json object */
-DLiteMeta *dlite_json_entity(json_t *obj)
+/**
+  Free's iterator created with dlite_json_iter_create().
+ */
+void json_iter_free(void *iter)
 {
-  DLiteMeta *entity = NULL;
-  char *uri=NULL;
-  char *iri=NULL;
-  char *desc=NULL;
-  int i, j, nprop, ndim;
-  json_t *jd;
-  json_t *jp;
-  json_t *item;
-  DLiteDimension *dims=NULL;
-  DLiteProperty *props=NULL;
-
-  if (json_is_object(obj)) {
-      uri = dlite_json_uri(obj);
-      if (uri) {
-        ndim = dlite_json_entity_dim_count(obj);
-        nprop = dlite_json_entity_prop_count(obj);
-
-        if ((nprop < 0) || (ndim < 0)) {
-          err(0, "errors in the definition of the entity %s", uri);
-        }
-        else if (nprop == 0) {
-          err(0, "no property for the entity %s", uri);
-        }
-        else if ((nprop > 0) && (ndim >= 0)) {
-          item = json_object_get(obj, "iri");
-          if (item) iri = str_copy(json_string_value(item));
-
-          item = json_object_get(obj, "description");
-          if (item) desc = str_copy(json_string_value(item));
-
-          if (ndim > 0) {
-            dims = calloc(ndim, sizeof(DLiteDimension));
-            jd = json_object_get(obj, "dimensions");
-            assert(json_array_size(jd) == (size_t)ndim);
-            for (i = 0; i < ndim; i++) {
-              item = json_array_get(jd, i);
-              dlite_json_entity_dim(item, dims + i);
-            }
-          }
-
-          props = calloc(nprop, sizeof(DLiteProperty));
-          jp = json_object_get(obj, "properties");
-          assert(json_array_size(jp) == (size_t)nprop);
-          for (i = 0; i < nprop; i++) {
-            item = json_array_get(jp, i);
-            dlite_json_entity_prop(item, ndim, dims, props + i);
-          }
-
-          entity = dlite_meta_create(uri, iri, desc,
-                                     ndim, dims,
-                                     nprop, props);
-        }
-      }
-      else
-        err(0, "name, version, and namespace must be given.");
-  }
-  if (uri) free(uri);
-  if (desc) free(desc);
-  if (dims) {
-    for (i=0; i<ndim; i++) {
-      free(dims[i].name);
-      free(dims[i].description);
-    }
-    free(dims);
-  }
-  if (props) {
-    for (i=0; i<nprop; i++) {
-      if (props[i].name) free(props[i].name);
-      for (j=0; j<props[i].ndims; j++)
-        if (props[i].dims[j]) free(props[i].dims[j]);
-      if (props[i].dims) free(props[i].dims);
-      if (props[i].description) free(props[i].description);
-    }
-    free(props);
-  }
-  return entity;
+  //dlite_json_iter_free(iter);
+  dlite_jstore_iter_free(iter);
 }
 
 
 static DLiteStoragePlugin dlite_json_plugin = {
   /* head */
-  "json",                          /* name */
-  NULL,                            /* freeapi */
+  "json",                   /* name */
+  NULL,                     /* freeapi */
 
   /* basic api */
-  dlite_json_open,                 /* open */
-  dlite_json_close,                /* close */
+  json_open,                /* open */
+  json_close,               /* close */
 
   /* queue api */
-  NULL,                            /* iterCreate */
-  NULL,                            /* iterNext */
-  NULL,                            /* iterFree */
-  dlite_json_get_uuids,            /* getUUIDs */
+  json_iter_create,         /* iterCreate */
+  json_iter_next,           /* iterNext */
+  json_iter_free,           /* iterFree */
+  NULL,                     /* getUUIDs */
 
   /* direct api */
-  NULL,                            /* loadInstance */
-  NULL,                            /* saveInstance */
+  json_load,                /* loadInstance */
+  json_save,                /* saveInstance */
 
   /* datamodel api */
-  dlite_json_datamodel,            /* dataModel */
-  dlite_json_datamodel_free,       /* dataModelFree */
+  NULL,                     /* dataModel */
+  NULL,                     /* dataModelFree */
 
-  dlite_json_get_metadata,         /* getMetaURI */
-  dlite_json_resolve_dimensions,   /* resolveDimensions */
-  dlite_json_get_dimension_size,   /* getDimensionSize */
-  dlite_json_get_property,         /* getProperty */
+  NULL,                     /* getMetaURI */
+  NULL,                     /* resolveDimensions */
+  NULL,                     /* getDimensionSize */
+  NULL,                     /* getProperty */
 
   /* -- datamodel api (optional) */
-  dlite_json_set_metadata,         /* setMetaURI */
-  dlite_json_set_dimension_size,   /* setDimensionSize */
-  dlite_json_set_property,         /* setProperty */
+  NULL,                     /* setMetaURI */
+  NULL,                     /* setDimensionSize */
+  NULL,                     /* setProperty */
 
-  dlite_json_has_dimension,        /* hasDimension */
-  dlite_json_has_property,         /* hasProperty */
+  NULL,                     /* hasDimension */
+  NULL,                     /* hasProperty */
 
-  dlite_json_get_dataname,         /* getDataName, obsolute */
-  dlite_json_set_dataname,         /* setDataName, obsolute */
+  NULL,                     /* getDataName, obsolute */
+  NULL,                     /* setDataName, obsolute */
 
   /* internal data */
-  NULL                             /* data */
+  NULL                      /* data */
 };
 
 
diff --git a/storages/json/json-utils.c b/storages/json/json-utils.c
deleted file mode 100644
index 5d73f5f75..000000000
--- a/storages/json/json-utils.c
+++ /dev/null
@@ -1,963 +0,0 @@
-/* json-utils.c */
-
-#include "config.h"
-
-//#include <assert.h>
-//#ifdef HAVE_INTTYPES_H
-//#include <inttypes.h>
-//#else
-//#define SCNx8 "hhx"
-//#endif
-
-#include "utils/err.h"
-#include "utils/boolean.h"
-#include "utils/floats.h"
-#include "str.h"
-#include "json-utils.h"
-#include "dlite-macros.h"
-#include "dlite-type.h"
-#include "dlite-entity.h"
-
-
-/* Returns the type of the json object as character:
- * x: undefined type
- * o: object
- * a: array
- * i: integer
- * r: real
- * s: string
- * b: boolean
- * n: null
- */
-char json_char_type(json_t *obj)
-{
-  json_type typ;
-  if (obj == NULL) {
-    return 'x';
-  } else {
-    typ = json_typeof(obj);
-    switch (typ) {
-      case JSON_OBJECT:
-        return 'o';
-      case JSON_ARRAY:
-        return 'a';
-      case JSON_STRING:
-        return 's';
-      case JSON_INTEGER:
-        return 'i';
-      case JSON_REAL:
-        return 'r';
-      case JSON_TRUE:
-        return 'b';
-      case JSON_FALSE:
-        return 'b';
-      case JSON_NULL:
-        return 'n';
-      default:
-        return 'x';
-    }
-  }
-}
-
-/* Combine the type an item (t1) with the next
- * item (t2) of a json array
- * return 'm' if the array contains different types
- * (e.g. the array contains a real and a string)
- */
-char json_merge_type(char t1, char t2) {
-  if (t1 == 'x')
-    return t2;
-  else if (t1 == t2)
-    return t2;
-  else if (t1 != t2) {
-    if ((t1 == 'i') && (t2 == 'r'))
-      return 'r';
-    else if ((t1 == 'r') && (t2 == 'i'))
-      return 'r';
-    else
-      return 'm';
-  }
-  else
-    return 'x';
-}
-
-/* Scan each item of the array and returns the type of the items:
- * i: the array contains only integer values
- * r: the array contains real values and/or integer values
- * s: the array contains only string values
- * m: the array contains different types (e.g. the array contains a real and a string)
- * x: undefined type
- */
-char json_array_type(json_t *obj)
-{
-  size_t i, size;
-  char cur = 'x';
-  char item_type = 'x';
-  json_t *item;
-  if (json_is_array(obj)) {
-    size = json_array_size(obj);
-    for(i=0; i < size; i++) {
-      item = json_array_get(obj, i);
-      cur = json_char_type(item);
-      if (cur == 'a')
-        item_type = json_array_type(item);
-      else
-        item_type = json_merge_type(item_type, cur);
-      if (item_type == 'm')
-        break;
-    }
-  }
-  return item_type;
-}
-
-int _merge_size(int s1, int s2)
-{
-  if (s1 == -2)
-    return s2;
-  else if (s1 == s2)
-    return s2;
-  else
-    return -1;
-}
-
-void _array_size(json_t *arr, int ndim, int *dims)
-{
-  int i, size;
-  json_t *item;
-
-  if (ndim >= NDIM_MAX)
-    return;
-
-  if (json_is_array(arr)) {
-    /*printf("array_size, dim:%i (%i, %i, %i)\n", ndim, dims[0], dims[1], dims[2]);*/
-    size = (int)(json_array_size(arr));
-    dims[ndim] = _merge_size(dims[ndim], size);
-    for(i=0; i < size; i++) {
-      item = json_array_get(arr, i);
-      _array_size(item, ndim + 1, dims);
-    }
-  }
-}
-
-/* Return the shape (dimensions) of the json value:
- *  - NULL: the json value is a scalar (real, integer, string, or object)
- *  - valid pointer of ivec_t: the json value is an array
- */
-ivec_t *json_array_dimensions(json_t *obj)
-{
-  int dims[NDIM_MAX];
-  int i, d;
-  ivec_t *ans = NULL;
-
-  for(i = 0; i < NDIM_MAX; i++)
-    dims[i] = -2;
-
-  _array_size(obj, 0, &dims[0]);
-
-  d = 0;
-  for(i = 0; i < NDIM_MAX; i++) {
-    if (dims[i] == -2) {
-      break;
-    } else if (dims[i] == -1) {
-      d = -1;
-      break;
-    }
-    else {
-      d++;
-    }
-  }
-  if (d > 0) {
-    ans = ivec();
-    for(i = 0; i < d; i++) {
-      ivec_add(ans, dims[i]);
-    }
-  }
-  return ans;
-}
-
-/* Convert the JSON value to a integer */
-int json_to_int(json_t *obj) {
-  if (json_is_integer(obj))
-    return json_integer_value(obj);
-  else if (json_is_true(obj))
-    return 1;
-  else if (json_is_false(obj))
-    return 0;
-  else if (json_is_real(obj))
-    return (int)(json_real_value(obj));
-  /*
-  else if (json_is_null(obj))
-    return (int)(nan);
-  */
-  else
-    return 0;
-}
-
-/* Recursive function to collapse an array of integer */
-void flatten_i(json_t *obj, ivec_t *arr)
-{
-  int i, size;
-  json_t *item;
-
-  if (json_is_array(obj)) {
-    size = (int)(json_array_size(obj));
-    for(i=0; i < size; i++) {
-      item = json_array_get(obj, i);
-      flatten_i(item, arr);
-    }
-  }
-  else {
-    ivec_add(arr, json_to_int(obj));
-  }
-}
-
-/* Return a copy of the json array collapsed
- * into one dimension (array of integer)
- */
-ivec_t *json_array_flatten_i(json_t *obj)
-{
-  ivec_t *arr = NULL;
-  if (json_is_array(obj)) {
-    arr = ivec();
-    flatten_i(obj, arr);
-  }
-  return arr;
-}
-
-/* Convert the JSON value to a real */
-double json_to_real(json_t *obj) {
-  if (json_is_real(obj))
-    return json_real_value(obj);
-  else if (json_is_integer(obj))
-    return (double)(json_integer_value(obj));
-  else if (json_is_true(obj))
-    return 1.0;
-  else if (json_is_false(obj))
-    return 0.0;
-  /*else if (json_is_null(obj))
-      return (int)(nan);*/
-  else
-    return 0;
-}
-
-/* Recursive function to collapse an array of real */
-void flatten_r(json_t *obj, vec_t *arr)
-{
-  int i, size;
-  json_t *item;
-
-  if (json_is_array(obj)) {
-    size = (int)(json_array_size(obj));
-    for(i=0; i < size; i++) {
-      item = json_array_get(obj, i);
-      flatten_r(item, arr);
-    }
-  }
-  else {
-    vec_add(arr, json_to_real(obj));
-  }
-}
-
-/* Return a copy of the json array collapsed
- * into one dimension (array of real)
- */
-vec_t *json_array_flatten_r(json_t *obj)
-{
-  vec_t *arr = NULL;
-  if (json_is_array(obj)) {
-    arr = vec();
-    flatten_r(obj, arr);
-  }
-  return arr;
-}
-
-/* Recursive function to collapse an array of string */
-void flatten_s(json_t *obj, str_list_t *arr)
-{
-  int i, size;
-  json_t *item;
-
-  if (json_is_array(obj)) {
-    size = (int)(json_array_size(obj));
-    for(i=0; i < size; i++) {
-      item = json_array_get(obj, i);
-      flatten_s(item, arr);
-    }
-  }
-  else {
-    str_list_add(arr, (char *)json_string_value(obj), true);
-  }
-}
-
-/* Return a copy of the json array collapsed
- * into one dimension (array of string)
- */
-str_list_t *json_array_flatten_s(json_t *obj)
-{
-  str_list_t *arr = NULL;
-  if (json_is_array(obj)) {
-    arr = str_list();
-    flatten_s(obj, arr);
-  }
-  return arr;
-}
-
-
-json_data_t *json_data()
-{
-  json_data_t *d;
-  d = (json_data_t*) calloc(1, sizeof(json_data_t));
-  d->dtype = 'x';
-  d->dims = NULL;
-  d->array_i = NULL;
-  d->array_r = NULL;
-  d->array_s = NULL;
-  return d;
-}
-
-void json_data_free(json_data_t *d)
-{
-  ivec_free(d->dims);
-  ivec_free(d->array_i);
-  vec_free(d->array_r);
-  str_list_free(d->array_s, false);
-  free(d);
-}
-
-
-json_data_t *json_get_data(json_t *obj)
-{
-  int ok;
-  json_data_t *data;
-
-  ok = 1;
-
-  data = json_data();
-  data->dtype = json_char_type(obj);
-
-  switch(data->dtype) {
-  case 'a':
-    data->dtype = json_array_type(obj);
-    data->dims = json_array_dimensions(obj);
-    if (data->dims != NULL) {
-      if (data->dtype == 'i')
-        data->array_i = json_array_flatten_i(obj);
-      else if (data->dtype == 'r')
-        data->array_r = json_array_flatten_r(obj);
-      else if (data->dtype == 's')
-        data->array_s = json_array_flatten_s(obj);
-      else {
-        /* mixed data type */
-        ok = 0;
-      }
-    }
-    break;
-  case 'i':
-    data->array_i = ivec();
-    ivec_add(data->array_i, json_integer_value(obj));
-    break;
-  case 'r':
-    data->array_r = vec();
-    vec_add(data->array_r, json_real_value(obj));
-    break;
-  case 'b':
-    data->array_i = ivec();
-    ivec_add(data->array_i, json_is_true(obj) ? 1 : 0);
-    break;
-  case 's':
-    data->array_s = str_list();
-    str_list_add(data->array_s, (char *)json_string_value(obj), true);
-    break;
-  case 'x':
-    ok = 0;
-    break;
-  }
-  if (ok == 0) {
-    json_data_free(data);
-    data = NULL;
-  }
-  return data;
-}
-
-/* Create a json array from an array of integer */
-json_t *json_array_int(ivec_t *data)
-{
-  json_t *arr = json_array();
-  size_t i, size;
-  size = ivec_size(data);
-  for(i=0; i<size; i++)
-    json_array_append(arr, json_integer(data->data[i]));
-  return arr;
-}
-
-/* Create a json array from an array of real */
-json_t *json_array_real(vec_t *data)
-{
-  json_t *arr = json_array();
-  size_t i, size;
-  size = vec_size(data);
-  for(i=0; i<size; i++)
-    json_array_append(arr, json_real(data->data[i]));
-  return arr;
-}
-
-/* Create a json array from an array of boolean */
-json_t *json_array_bool(ivec_t *data)
-{
-  json_t *arr = json_array();
-  size_t i, size;
-  size = ivec_size(data);
-  for(i=0; i<size; i++)
-    json_array_append(arr, data->data[i] ? json_true() : json_false());
-  return arr;
-}
-
-/* Create a json array from an array of string */
-json_t *json_array_string(str_list_t *data)
-{
-  json_t *arr = json_array();
-  size_t i, size;
-  size = str_list_size(data);
-  for(i=0; i < size; i++)
-    json_array_append(arr, json_string(data->data[i]));
-  return arr;
-}
-
-int json_set_data(json_t *obj, const char *name, const json_data_t *data)
-{
-  json_t *value;
-  if (json_is_object(obj) && data && (!str_is_whitespace(name))) {
-    switch (data->dtype) {
-    case 'i':
-      if (ivec_size(data->dims) > 0)
-        value = json_array_int(data->array_i);
-      else if (ivec_size(data->array_i) > 0)
-        value = json_integer(data->array_i->data[0]);
-      break;
-    case 'r':
-      if (ivec_size(data->dims) > 0)
-        value = json_array_real(data->array_r);
-      else if (vec_size(data->array_r) > 0)
-        value = json_real(data->array_r->data[0]);
-      break;
-    case 'b':
-      if (ivec_size(data->dims) > 0)
-        value = json_array_bool(data->array_i);
-      else if (ivec_size(data->array_i) > 0)
-        value = data->array_i->data[0] ? json_true() : json_false();
-      break;
-    case 's':
-      if (ivec_size(data->dims) > 0)
-        value = json_array_string(data->array_s);
-      else if (str_list_size(data->array_s) > 0)
-        value = json_string(data->array_s->data[0]);
-      break;
-    default:
-      value = json_null();
-      break;
-    }
-    json_object_set(obj, name, value);
-    return 0;
-  }
-  return 1;
-}
-
-int check_dimensions(const char *prop_name, json_t *prop_dims,
-                     json_t *entity_dims)
-{
-  size_t i, j, sp, se, k, found;
-  json_t *e;
-  json_t *p;
-  json_t *dname;
-
-  k = 0;
-  sp = json_array_size(prop_dims);
-  se = json_array_size(entity_dims);
-  for(i = 0; i < sp; i++) {
-    p = json_array_get(prop_dims, i);
-    found = 0;
-    for(j = 0; j < se; j++) {
-      e = json_array_get(entity_dims, j);
-      dname = json_object_get(e, "name");
-      if (str_equal(json_string_value(p), json_string_value(dname))) {
-        k++;
-        found++;
-      }
-    }
-    if (found == 0)
-      printf("error: the dimension \"%s\" of the property \"%s\" is not defined.\n", json_string_value(p), prop_name);
-  }
-  return (int)(k == sp);
-}
-
-
-int dlite_json_entity_dim_count(json_t *obj)
-{
-  int count = 0;
-  int nerr = 0;
-  size_t i, size;
-  json_t *dims;
-  json_t *item;
-  json_t *name;
-
-  if (json_is_object(obj)) {
-    dims = json_object_get(obj, "dimensions");
-    if (json_is_array(dims)) {
-      size = json_array_size(dims);
-      for (i = 0; i < size; i++) {
-        item = json_array_get(dims, i);
-        name = json_object_get(item, "name");
-        if (str_is_whitespace(json_string_value(name))) {
-          printf("error: the dimension [%d] has not a valid name.\n", (int)(i + 1));
-          nerr++;
-        }
-        else
-          count++;
-      }
-    }
-  }
-  return nerr > 0 ? -1 : count;
-}
-
-
-int dlite_json_entity_prop_count(json_t *obj)
-{
-  int count = 0;
-  int nerr = 0;
-  size_t i, size;
-  json_t *dims;
-  json_t *props;
-  json_t *item;
-  json_t *name;
-  json_t *ptype;
-
-  if (json_is_object(obj)) {
-    dims = json_object_get(obj, "dimensions");
-    props = json_object_get(obj, "properties");
-    if (json_is_array(props)) {
-      size = json_array_size(props);
-      for (i = 0; i < size; i++) {
-        item = json_array_get(props, i);
-        name = json_object_get(item, "name");
-        ptype = json_object_get(item, "type");
-        if (str_is_whitespace(json_string_value(name))) {
-          printf("error: the property [%d] has not a valid name.\n",
-                 (int)(i + 1));
-          nerr++;
-        }
-        else if (!dlite_is_type(json_string_value(ptype))) {
-          printf("error: the property [%d] \"%s\" has not a valid type.\n",
-                 (int)(i + 1), json_string_value(name));
-          nerr++;
-        }
-        else if (!check_dimensions(json_string_value(name),
-                                   json_object_get(item, "dims"), dims)) {
-          printf("error: the dimension of the property \"%s\" are not well "
-                 "defined.\n", json_string_value(name));
-          nerr++;
-        }
-        else
-          count++;
-      }
-    }
-  }
-  return nerr > 0 ? -1 : count;
-}
-
-
-/* Returns a json string containing the encoded binary blob `src` of
-   length `n`.  Returns NULL on error. */
-json_t *hex_encode(const uint8_t *src, size_t n)
-{
-  size_t i;
-  char *dest = malloc(2*n+1);
-  json_t *v=NULL;
-  for (i=0; i<n; i++)
-    if (snprintf(dest + 2*i, 3, "%02x", src[i]) != 2)
-      FAIL1("invalid hex string: '%s'", src);
-  v = json_string(dest);
- fail:
-  free(dest);
-  return v;
-}
-
-
-/* Decode hex string `src` and write the result to `dest`.  `n` is the
-   length of `dest`.  Returns non-zero on error. */
-int hex_decode(uint8_t *dest, const json_t *src, size_t n)
-{
-  size_t i;
-  const char *s;
-  if (!json_is_string(src)) return errx(1, "expected json string");
-  if (json_string_length(src) != 2*n)
-    return errx(1, "expected encoded blob length: %lu, got %lu",
-                (unsigned long)(2*n), (unsigned long)json_string_length(src));
-  s = json_string_value(src);
-  for (i=0; i<n; i++) {
-    unsigned int tmp;
-    if (sscanf(s + 2*i, "%2x", &tmp) != 1)
-      return err(-1, "invalid hex string: '%s'", s);
-    dest[i] = tmp;
-  }
-  return 0;
-}
-
-
-/* Returns a new json item with the data at `ptr` (which has type `type` and
- * size `size`).  Returns NULL on error.
-*/
-json_t *dlite_json_set_value(const void *ptr, DLiteType type, size_t size,
-                             const json_t *root)
-{
-  bool bval;
-  json_int_t ival;
-  double fval;
-  char *sval;
-
-  switch (type) {
-
-  case dliteBlob:
-    return hex_encode(ptr, size);
-
-  case dliteBool:
-    assert(size == sizeof(bool));
-    bval = *((bool *)ptr);
-    return json_boolean(bval);
-
-  case dliteInt:
-    switch (size) {
-    case 1:  ival = *((int8_t *)ptr);  break;
-    case 2:  ival = *((int16_t *)ptr); break;
-    case 4:  ival = *((int32_t *)ptr); break;
-    case 8:  ival = *((int64_t *)ptr); break;
-    default: return errx(-1, "invalid int size: %lu", (unsigned long)size),
-        NULL;
-    }
-    return json_integer(ival);
-
-  case dliteUInt:
-    switch (size) {
-    case 1:  ival = *((uint8_t *)ptr);  break;
-    case 2:  ival = *((uint16_t *)ptr); break;
-    case 4:  ival = *((uint32_t *)ptr); break;
-    case 8:  ival = *((uint64_t *)ptr); break;
-    default: return errx(-1, "invalid uint size: %lu", (unsigned long)size),
-        NULL;
-    }
-    return json_integer(ival);
-
-  case dliteFloat:
-    switch (size) {
-    case  4: fval = *((float32_t *)ptr);  break;
-    case  8: fval = *((float64_t *)ptr);  break;
-#ifdef HAVE_FLOAT80
-    case 10: fval = *((float80_t *)ptr);  break;
-#endif
-#ifdef HAVE_FLOAT128
-    case 16: fval = *((float128_t *)ptr); break;
-#endif
-    default: return errx(-1, "invalid float size: %lu", (unsigned long)size),
-        NULL;
-    }
-    return json_real(fval);
-
-  case dliteFixString: {
-    size_t len = strlen((char *)ptr);
-    sval = (char *)ptr;
-    return json_stringn(sval, (len < size) ? len : size);
-  }
-
-  case dliteStringPtr:
-    sval = *((char **)ptr);
-    return (sval) ? json_string(sval) : json_null();
-
-  case dliteDimension: {
-    const DLiteDimension *d = ptr;
-    json_t *obj = json_object();
-    json_object_set_new(obj, "name", json_string(d->name));
-    json_object_set_new(obj, "description", json_string(d->description));
-    return obj;
-  }
-
-  case dliteProperty: {
-    const DLiteProperty *p = ptr;
-    char typename[30];
-    json_t *obj = json_object();
-    dlite_type_set_typename(p->type, p->size, typename, sizeof(typename));
-
-    json_object_set_new(obj, "name", json_string(p->name));
-    json_object_set_new(obj, "type", json_string(typename));
-    if (p->ndims) {
-      int i;
-      json_t *arr = json_array();
-      json_t *dimensions = json_object_get(root, "dimensions");
-      if (!json_is_array(dimensions)) {
-        json_t *properties = json_object_get(root, "properties");
-        dimensions = json_object_get(properties, "dimensions");
-      }
-      if (!dimensions)
-        return errx(-1, "JSON storage: dimensions must be set before "
-                    "properties"), NULL;
-      if (!json_is_array(dimensions))
-        return errx(-1, "JSON storage: dimensions should be an array"), NULL;
-      for (i=0; i<p->ndims; i++) {
-        json_t *dimname = json_string(p->dims[i]);
-        json_array_append_new(arr, dimname);
-      }
-      json_object_set_new(obj, "dims", arr);
-    }
-    if (p->unit && *p->unit)
-      json_object_set_new(obj, "unit", json_string(p->unit));
-    if (p->iri && *p->iri)
-      json_object_set_new(obj, "iri", json_string(p->iri));
-    if (p->description)
-      json_object_set_new(obj, "description", json_string(p->description));
-    return obj;
-  }
-
-  case dliteRelation: {
-    const DLiteRelation *r = ptr;
-    json_t *obj = json_object();
-    json_object_set_new(obj, "s", json_string(r->s));
-    json_object_set_new(obj, "p", json_string(r->p));
-    json_object_set_new(obj, "o", json_string(r->o));
-    if (r->id) json_object_set_new(obj, "id", json_string(r->id));
-    return obj;
-  }
-
-  default:
-    return errx(-1, "JSON storage, unsupported type number: %d", type), NULL;
-  }
-  assert(0);  /* should never be reached */
-}
-
-
-int parse_property(void *ptr, const json_t *item, const json_t *root);
-
-
-
-
-/* Copies the value of JSON item `item` to memory pointed to by `ptr`
-   (which should be large enough to hold `size` bytes).  `type` and
-   `size` is the destination type and size.  Returns non-zero on error.
-
-   The `jdims` should be the json root for dimensions.  It allow properties
-   to inspect dimension names.
-*/
-int dlite_json_get_value(void *ptr, const json_t *item,
-                         DLiteType type, size_t size,
-                         const json_t *root)
-{
-  DLiteDimension dimension;
-  DLiteRelation relation;
-  json_t *str;
-  const char *s;
-
-  switch (type) {
-  case dliteBlob:
-    if (hex_decode(ptr, item, size)) return 1;
-    break;
-
-  case dliteBool:
-    if (!json_is_boolean(item)) return errx(1, "expected json boolean");
-    *((bool *)ptr) = json_boolean_value(item);
-    break;
-
-  case dliteInt:
-    if (!json_is_integer(item)) return errx(1, "expected json integer");
-    switch (size) {
-    case 1:  *((int8_t *)ptr)  = json_integer_value(item);  break;
-    case 2:  *((int16_t *)ptr) = json_integer_value(item);  break;
-    case 4:  *((int32_t *)ptr) = json_integer_value(item);  break;
-    case 8:  *((int64_t *)ptr) = json_integer_value(item);  break;
-    default: return errx(1, "invalid int size: %lu", (unsigned long)size);
-    }
-    break;
-
-  case dliteUInt:
-    if (!json_is_integer(item)) return errx(1, "expected json integer");
-    if (json_integer_value(item) < 0) return errx(1, "expected unsigned int");
-    switch (size) {
-    case 1:  *((uint8_t *)ptr)  = json_integer_value(item);  break;
-    case 2:  *((uint16_t *)ptr) = json_integer_value(item);  break;
-    case 4:  *((uint32_t *)ptr) = json_integer_value(item);  break;
-    case 8:  *((uint64_t *)ptr) = json_integer_value(item);  break;
-    default: return errx(1, "invalid uint size: %lu", (unsigned long)size);
-    }
-    break;
-
-  case dliteFloat:
-    if (!json_is_real(item)) return errx(1, "expected json real");
-    switch (size) {
-    case  4: *((float32_t *)ptr)  = (float32_t)json_real_value(item);  break;
-    case  8: *((float64_t *)ptr)  = json_real_value(item);  break;
-#ifdef HAVE_FLOAT80
-    case 10: *((float80_t *)ptr)  = json_real_value(item);  break;
-#endif
-#ifdef HAVE_FLOAT128
-    case 16: *((float128_t *)ptr) = json_real_value(item);  break;
-#endif
-    default: return errx(1, "invalid float size: %lu", (unsigned long)size);
-    }
-    break;
-
-  case dliteFixString:
-    if (!json_is_string(item)) return errx(1, "expected json string");
-    if (json_string_length(item) > size)
-      return errx(1, "length of JSON string (%lu), exceeds buffer size (%lu)",
-                  (unsigned long)json_string_length(item), (unsigned long)size);
-    strncpy(ptr, json_string_value(item), size);
-    if (size > 0) ((char *)ptr)[size-1] = '\0';
-    break;
-
-  case dliteStringPtr:
-    if (json_is_null(item)) {
-      *((void **)ptr) = NULL;
-    } else if (json_is_string(item)) {
-      if (!(*((char **)ptr) = strdup(json_string_value(item))))
-        return errx(1, "allocation error");
-    } else {
-      return errx(1, "expected json string");
-    }
-    break;
-
-  case dliteDimension:
-    memset(&dimension, 0, sizeof(DLiteDimension));
-
-    if (!json_is_object(item))
-      FAIL("expected json dimension object");
-
-    if (!(str = json_object_get(item, "name")) ||
-        !(s = json_string_value(str)))
-      FAIL("expected json object with dimension name");
-    if (!(dimension.name = strdup(s))) FAIL("allocation failure");
-
-    if (!(str = json_object_get(item, "description")) ||
-        !(s = json_string_value(str)))
-      FAIL("expected json object with dimension description");
-    if (!(dimension.description = strdup(s))) FAIL("allocation failure");
-
-    memcpy(ptr, &dimension, sizeof(dimension));
-    break;
-
-  case dliteProperty:
-    if (parse_property(ptr, item, root)) goto fail;
-    break;
-
-  case dliteRelation:
-    memset(&relation, 0, sizeof(relation));
-    if (!json_is_object(item)) FAIL("expected json relation object");
-
-    if (!(str = json_object_get(item, "s")) ||
-        !(s = json_string_value(str)))
-      FAIL("expected relation subject (s)");
-    if (!(relation.s = strdup(s))) FAIL("allocation failure");
-
-    if (!(str = json_object_get(item, "p")) ||
-        !(s = json_string_value(str)))
-      FAIL("expected relation predicate (p)");
-    if (!(relation.p = strdup(s))) FAIL("allocation failure");
-
-    if (!(str = json_object_get(item, "o")) ||
-        !(s = json_string_value(str)))
-      FAIL("expected relation object (o)");
-    if (!(relation.o = strdup(s))) FAIL("allocation failure");
-
-    if ((str = json_object_get(item, "id")) &&
-        (s = json_string_value(str)) &&
-        !(relation.id = strdup(s))) FAIL("allocation failure");
-
-    memcpy(ptr, &relation, sizeof(relation));
-    break;
-
-  default:
-    return errx(1, "reading JSON data of type '%s' is not yet supported",
-                dlite_type_get_dtypename(type));
-  }
-  return 0;
-
- fail:
-  return 1;
-}
-
-
-/* help function for dlite_json_get_value() */
-int parse_property(void *ptr, const json_t *item, const json_t *root)
-{
-  DLiteProperty property;
-  json_t *str, *arr;
-  const char *s;
-  char **dims=NULL;
-  int j;
-  int ndims=0;
-
-  UNUSED(root);
-  memset(&property, 0, sizeof(DLiteProperty));
-
-  if (!json_is_object(item)) FAIL("expected json property object");
-
-  if (!(str = json_object_get(item, "name")) || !(s = json_string_value(str)))
-    FAIL("expected property name");
-  if (!(property.name = strdup(s))) FAIL("allocation failure");
-
-  if (!(str = json_object_get(item, "type")) || !(s = json_string_value(str)))
-    FAIL("expected property type");
-  if (dlite_type_set_dtype_and_size(s, &property.type, &property.size))
-    goto fail;
-
-  if ((arr = json_object_get(item, "dims"))) {
-    if (!json_is_array(arr)) FAIL("expected 'dims' to be a json array");
-    ndims = json_array_size(arr);
-    if (!(dims = calloc(ndims, sizeof(char *)))) FAIL("allocation failure");
-
-    for (j=0; j<ndims; j++) {
-      const char *s;
-      json_t *jdim  = json_array_get(arr, j);
-      assert(jdim);
-      if (!(s = json_string_value(jdim)))
-        FAIL("expected property dimension elements to be strings");
-      dims[j] = strdup(s);
-    }
-    property.ndims = ndims;
-    property.dims = dims;
-  }
-
-  if ((str = json_object_get(item, "unit"))) {
-    if (!(s = json_string_value(str))) FAIL("unit must be a string");
-    if (!(property.unit = strdup(s))) FAIL("allocation failure");
-  }
-
-  if ((str = json_object_get(item, "iri"))) {
-    if (!(s = json_string_value(str))) FAIL("iri must be a string");
-    if (!(property.iri = strdup(s))) FAIL("allocation failure");
-  }
-
-  if ((str = json_object_get(item, "description"))) {
-    if (!(s = json_string_value(str))) FAIL("description must be a string");
-    if (!(property.description = strdup(s))) FAIL("allocation failure");
-  }
-
-  /*
-  printf("\n");
-  printf("--- name: '%s'\n", property.name);
-  printf("--- type: %d\n", property.type);
-  printf("--- size: %lu\n", property.size);
-  printf("--- ndims: %d\n", property.ndims);
-  printf("--- dims: ");
-  for (i=0; i<ndims; i++) printf("  %d", dims[i]);
-  printf("\n");
-  printf("--- unit: '%s'\n", property.unit);
-  printf("--- iri: '%s'\n", property.iri);
-  printf("--- description: '%s'\n", property.description);
-  */
-
-  memcpy(ptr, &property, sizeof(property));
-  return 0;
- fail:
-  if (dims) {
-    for (j=0; j<ndims; j++)
-      if (dims[j]) free(dims[j]);
-    free(dims);
-  }
-  return 1;
-}
diff --git a/storages/json/json-utils.h b/storages/json/json-utils.h
deleted file mode 100644
index 80a961aa5..000000000
--- a/storages/json/json-utils.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* json-utils.h */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include <jansson.h>
-
-#include "utils/integers.h"
-
-#include "vector.h"
-#include "str.h"
-
-#define NDIM_MAX 50
-
-typedef struct {
-  /* Type of the data: i, r, b, s */
-  char dtype;
-  /* Dimensions of the data (=NULL if data is a scalar) */
-  ivec_t *dims;
-  /* Array data for integer (dtype==i and dtype==b) */
-  ivec_t *array_i;
-  /* Array data for real (dtype==r) */
-  vec_t *array_r;
-  /* Array data for real (dtype==s) */
-  str_list_t *array_s;
-} json_data_t;
-
-char json_char_type(json_t *obj);
-char json_array_type(json_t *obj);
-ivec_t *json_array_dimensions(json_t *obj);
-
-json_data_t *json_data();
-json_data_t *json_get_data(json_t *obj);
-int json_set_data(json_t *obj, const char *name, const json_data_t *data);
-void json_data_free(json_data_t *d);
-
-/* Return true (=1) if each dimension of the property is
-   defined in the list of dimension of the entity
- */
-int check_dimensions(const char *prop_name, json_t *prop_dims,
-                     json_t *entity_dims);
-
-
-/* Returns a json string containing the encoded binary blob `src` of
-   length `n`.  Returns NULL on error. */
-json_t *hex_encode(const uint8_t *src, size_t n);
-
-/* Decode hex string `src` and write the result to `dest`.  `n` is the
-   length of `dest`.  Returns non-zero on error. */
-int hex_decode(uint8_t *dest, const json_t *src, size_t n);
-
-
-/* Count the number of valid dimensions in the json object.
- * a dimension must have a name (not null, not empty, not only white space)
- */
-int dlite_json_entity_dim_count(json_t *obj);
-
-/* Count the number of valid properties in the json object.
- * a property must have a name and a valid type.
- */
-int dlite_json_entity_prop_count(json_t *obj);
-
-/* Copies the value of JSON item `item` to memory pointed to by `ptr`
- * (which should be large enough to hold `size` bytes).  `type` and
- * `size` is the destination type and size.  Returns non-zero on error.
- */
-int dlite_json_get_value(void *ptr, const json_t *item,
-                         DLiteType type, size_t size, const json_t *root);
-
-/* Returns a new json item with the data at `ptr` (which has type `type` and
- * size `size`).  Returns NULL on error.
- */
-json_t *dlite_json_set_value(const void *ptr, DLiteType type, size_t size,
-                             const json_t *root);
diff --git a/storages/json/str.c b/storages/json/str.c
deleted file mode 100644
index 76d39b3ef..000000000
--- a/storages/json/str.c
+++ /dev/null
@@ -1,160 +0,0 @@
-
-#include "str.h"
-
-bool str_is_null(const char* s)
-{
-  return s == NULL;
-}
-
-bool str_is_empty(const char* s)
-{
-  return str_size(s) == 0;
-}
-
-bool str_is_whitespace(const char* s)
-{
-  size_t i, n, size;
-  size = str_size(s);
-  n = 0;
-  for (i = 0; i < size; i++) {
-    if (isspace(s[i]))
-      n++;
-  }
-  return n == size;
-}
-
-size_t str_size(const char* s)
-{
-  if (s)
-    return strlen(s);
-  else
-    return 0;
-}
-
-char *str_copy(const char* s)
-{
-  char *d = NULL;
-  size_t i, size;
-
-  size = str_size(s);
-  if (size > 0) {
-    d = malloc(sizeof(char) * (size + 1));
-    for (i = 0; i < size; i++) {
-        d[i] = s[i];
-    }
-    d[size] = '\0';
-  }
-  return d;
-}
-
-bool str_equal(const char* a, const char* b)
-{
-  size_t i, na, nb;
-  na = str_size(a);
-  nb = str_size(b);
-
-  if (na == nb) {
-    for (i = 0; i < na; i++) {
-      if (a[i] != b[i])
-        return false;
-    }
-  }
-  else {
-    return false;
-  }
-
-  return true;
-}
-
-str_list_t *str_list()
-{
-  str_list_t *v;
-  v = (str_list_t *) malloc(sizeof(str_list_t));
-  v->capacity = 0;
-  v->size = 0;
-  v->data = NULL;
-  return v;
-}
-
-str_list_t *str_list1(char *x, bool copy)
-{
-  str_list_t *v = str_list();
-  str_list_reserve(v, 1);
-  str_list_add(v, x, copy);
-  return v;
-}
-
-str_list_t *str_list2(char *x, char *y, bool copy)
-{
-  str_list_t *v = str_list();
-  str_list_reserve(v, 2);
-  str_list_add(v, x, copy);
-  str_list_add(v, y, copy);
-  return v;
-}
-
-void str_list_add(str_list_t *v, char *value, bool copy)
-{
-  if (v->size >= v->capacity)
-    str_list_reserve(v, 2 * v->size);
-  v->data[v->size] = copy ? str_copy(value) : value;
-  v->size++;
-}
-
-size_t str_list_size(str_list_t *v)
-{
-  return v ? v->size : 0;
-}
-
-void str_list_resize(str_list_t *v, size_t size)
-{
-  v->size = size;
-  if (v->size >= v->capacity)
-    str_list_reserve(v, 2 * v->size);
-}
-
-void str_list_reserve(str_list_t *v, size_t capacity)
-{
-  if (capacity == 0)
-    capacity = 10;
-  if (v->capacity < capacity) {
-    if (v->data == NULL)
-      v->data = (char **)malloc(capacity * sizeof(char*));
-    else
-      v->data = (char **)realloc(v->data, capacity * sizeof(char*));
-    v->capacity = capacity;
-  }
-}
-
-void str_list_free(str_list_t *v, bool free_items)
-{
-  size_t i;
-  if (v) {
-    if (v->data) {
-      if (free_items) {
-        for(i=0; i < v->size; i++)
-          if (v->data[i])
-            free(v->data[i]);
-      }
-      free(v->data);
-    }
-    free(v);
-  }
-}
-
-void str_list_print(str_list_t *v, char* name)
-{
-  size_t i, last;
-  if (v == NULL)
-    printf("%s = NULL\n", name);
-  else {
-    printf("%s = [", name);
-    last = v->size - 1;
-    for(i=0; i < v->size; i++)
-      if (i == last)
-        printf("%s", v->data[i] ? v->data[i] : "NULL");
-      else
-        printf("%s, ", v->data[i] ? v->data[i] : "NULL");
-    printf("]%c", '\n');
-  }
-}
diff --git a/storages/json/str.h b/storages/json/str.h
deleted file mode 100644
index 5180fa416..000000000
--- a/storages/json/str.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* str.h */
-
-#ifndef STR_H
-#define STR_H
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include "boolean.h"
-
-typedef struct {
-  char **data;
-  size_t capacity;
-  size_t size;
-} str_list_t;
-
-bool str_is_null(const char* s);
-bool str_is_empty(const char* s);
-bool str_is_whitespace(const char* s);
-
-size_t str_size(const char* s);
-char *str_copy(const char* s);
-
-bool str_equal(const char* a, const char* b);
-
-str_list_t *str_list();
-str_list_t *str_list1(char *x, bool copy);
-str_list_t *str_list2(char *x, char *y, bool copy);
-
-size_t str_list_size(str_list_t *v);
-void str_list_add(str_list_t *v, char *value, bool copy);
-void str_list_resize(str_list_t *v, size_t size);
-void str_list_reserve(str_list_t *v, size_t capacity);
-void str_list_free(str_list_t *v, bool free_items);
-void str_list_print(str_list_t *v, char* name);
-
-
-#endif /* STR_H */
diff --git a/storages/json/vector.c b/storages/json/vector.c
deleted file mode 100644
index 898baa5e5..000000000
--- a/storages/json/vector.c
+++ /dev/null
@@ -1,501 +0,0 @@
-
-#include "utils/boolean.h"
-#include "utils/floats.h"
-#include "utils/integers.h"
-#include "vector.h"
-
-ivec_t *ivec()
-{
-  ivec_t *v;
-  v = (ivec_t*) malloc(sizeof(ivec_t));
-  v->capacity = 0;
-  v->size = 0;
-  v->data = NULL;
-  return v;
-}
-
-ivec_t *ivec1(int x)
-{
-  ivec_t *v = ivec();
-  ivec_reserve(v, 1);
-  ivec_add(v, x);
-  return v;
-}
-
-ivec_t *ivec2(int x, int y)
-{
-  ivec_t *v = ivec();
-  ivec_reserve(v, 2);
-  ivec_add(v, x);
-  ivec_add(v, y);
-  return v;
-}
-
-ivec_t *ivec3(int x, int y, int z)
-{
-  ivec_t *v = ivec();
-  ivec_reserve(v, 3);
-  ivec_add(v, x);
-  ivec_add(v, y);
-  ivec_add(v, z);
-  return v;
-}
-
-ivec_t *ivecn(size_t n, int init)
-{
-  ivec_t *v = ivec();
-  ivec_resize(v, n);
-  ivec_fill(v, init);
-  return v;
-}
-
-
-void ivec_fill(ivec_t *v, int value)
-{
-  size_t i;
-  for(i=0; i < v->size; i++)
-    v->data[i] = value;
-}
-
-void ivec_add(ivec_t *v, int value)
-{
-  if (v->size >= v->capacity)
-    ivec_reserve(v, 2 * v->size);
-  v->data[v->size] = value;
-  v->size++;
-}
-
-size_t ivec_size(ivec_t *v)
-{
-  return v ? v->size : 0;
-}
-
-void ivec_resize(ivec_t *v, size_t size)
-{
-  v->size = size;
-  if (v->size >= v->capacity)
-    ivec_reserve(v, 2 * v->size);
-}
-
-void ivec_reserve(ivec_t *v, size_t capacity)
-{
-  if (capacity == 0)
-    capacity = 10;
-  if (v->capacity < capacity) {
-    if (v->data == NULL)
-      v->data = (int *)malloc(capacity * sizeof(int));
-    else
-      v->data = (int *)realloc(v->data, capacity * sizeof(int));
-    v->capacity = capacity;
-  }
-}
-
-void ivec_free(ivec_t *v)
-{
-  if (v) {
-    if (v->data)
-      free(v->data);
-    free(v);
-  }
-}
-
-void ivec_print(ivec_t *v, char* name)
-{
-  size_t i, last;
-  if (v == NULL)
-    printf("%s = NULL\n", name);
-  else {
-    printf("%s = [", name);
-    last = v->size - 1;
-    for(i=0; i < v->size; i++)
-      if (i == last)
-        printf("%d", v->data[i]);
-      else
-        printf("%d, ", v->data[i]);
-    printf("]%c", '\n');
-  }
-}
-
-int ivec_cumprod(ivec_t *v)
-{
-  int p = 0;
-  size_t i;
-  size_t size = ivec_size(v);
-  if (size > 0) {
-    p = 1;
-    for(i=0; i < size; i++)
-      p *= v->data[i];
-  }
-  return p;
-}
-
-int ivec_cumsum(ivec_t *v)
-{
-  int s = 0;
-  size_t i;
-  size_t size = ivec_size(v);
-  if (size > 0) {
-    for(i=0; i < size; i++)
-      s += v->data[i];
-  }
-  return s;
-}
-
-void ivec_copy_cast(ivec_t *v, DLiteType dtype, size_t dsize, void *dst)
-{
-  size_t vsize = ivec_size(v);
-  size_t i=0;
-
-  int *dst0;
-  int8_t *dst1;
-  int16_t *dst2;
-  int32_t *dst4;
-  int64_t *dst8;
-
-  unsigned int *dst0u;
-  uint8_t *dst1u;
-  uint16_t *dst2u;
-  uint32_t *dst4u;
-  uint64_t *dst8u;
-
-  bool *dstb;
-
-  if (vsize > 0) {
-    if (dtype == dliteInt) {
-      if (dsize == sizeof(int)) {
-        dst0 = (int*)dst;
-        for(i=0; i < vsize; i++)
-          dst0[i] = v->data[i];
-      }
-      else if (dsize == 1) {
-        dst1 = (int8_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst1[i] = (int8_t)(v->data[i]);
-      }
-      else if (dsize == 2) {
-        dst2 = (int16_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst2[i] = (int16_t)(v->data[i]);
-      }
-      else if (dsize == 4) {
-        dst4 = (int32_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst4[i] = (int32_t)(v->data[i]);
-      }
-      else if (dsize == 8) {
-        dst8 = (int64_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst8[i] = (int64_t)(v->data[i]);
-      }
-    }
-    else if (dtype == dliteUInt) {
-      if (dsize == sizeof(unsigned int)) {
-        dst0u = (unsigned int*)dst;
-        for(i=0; i < vsize; i++)
-          dst0u[i] = (unsigned int)(v->data[i]);
-      }
-      else if (dsize == 1) {
-        dst1u = (uint8_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst1u[i] = (uint8_t)(v->data[i]);
-      }
-      else if (dsize == 2) {
-        dst2u = (uint16_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst2u[i] = (uint16_t)(v->data[i]);
-      }
-      else if (dsize == 4) {
-        dst4u = (uint32_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst4u[i] = (uint32_t)(v->data[i]);
-      }
-      else if (dsize == 8) {
-        dst8u = (uint64_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst8u[i] = (uint64_t)(v->data[i]);
-      }
-    }
-    else if (dtype == dliteBool) {
-      dstb = (bool*)dst;
-      for(i=0; i < vsize; i++)
-        dstb[i] = (bool)(v->data[i]);
-    }
-  }
-}
-
-ivec_t *ivec_create(DLiteType dtype, size_t dsize, size_t num, const void *src)
-{
-  ivec_t *v = NULL;
-  size_t i;
-
-  int *src0;
-  int8_t *src1;
-  int16_t *src2;
-  int32_t *src4;
-  int64_t *src8;
-
-  unsigned int *src0u;
-  uint8_t *src1u;
-  uint16_t *src2u;
-  uint32_t *src4u;
-  uint64_t *src8u;
-
-  bool *srcb;
-
-  if (num > 0) {
-
-    v = ivec();
-    ivec_resize(v, num);
-
-    if (dtype == dliteInt) {
-      if (dsize == sizeof(int)) {
-        src0 = (int*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src0[i];
-      }
-      else if (dsize == 1) {
-        src1 = (int8_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src1[i];
-      }
-      else if (dsize == 2) {
-        src2 = (int16_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src2[i];
-      }
-      else if (dsize == 4) {
-        src4 = (int32_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src4[i];
-      }
-      else if (dsize == 8) {
-        src8 = (int64_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src8[i];
-      }
-    }
-    else if (dtype == dliteUInt) {
-      if (dsize == sizeof(unsigned int)) {
-        src0u = (unsigned int*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src0u[i];
-      }
-      else if (dsize == 1) {
-        src1u = (uint8_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src1u[i];
-      }
-      else if (dsize == 2) {
-        src2u = (uint16_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src2u[i];
-      }
-      else if (dsize == 4) {
-        src4u = (uint32_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src4u[i];
-      }
-      else if (dsize == 8) {
-        src8u = (uint64_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (int)src8u[i];
-      }
-    }
-    else if (dtype == dliteBool) {
-      srcb = (bool*)src;
-      for(i=0; i < num; i++)
-        v->data[i] = (int)srcb[i];
-    }
-  }
-  return v;
-}
-
-/* vec_t - vector of double */
-
-vec_t *vec()
-{
-  vec_t *v;
-  v = (vec_t*) malloc(sizeof(vec_t));
-  v->capacity = 0;
-  v->size = 0;
-  v->data = NULL;
-  return v;
-}
-
-
-vec_t *vec1(double x)
-{
-  vec_t *v = vec();
-  vec_reserve(v, 1);
-  vec_add(v, x);
-  return v;
-}
-
-vec_t *vec2(double x, double y)
-{
-  vec_t *v = vec();
-  vec_reserve(v, 2);
-  vec_add(v, x);
-  vec_add(v, y);
-  return v;
-}
-
-vec_t *vec3(double x, double y, double z)
-{
-  vec_t *v = vec();
-  vec_reserve(v, 3);
-  vec_add(v, x);
-  vec_add(v, y);
-  vec_add(v, z);
-  return v;
-}
-
-vec_t *vecn(size_t n, double init)
-{
-  vec_t *v = vec();
-  vec_resize(v, n);
-  vec_fill(v, init);
-  return v;
-}
-
-void vec_fill(vec_t *v, double value)
-{
-  size_t i;
-  for(i=0; i < v->size; i++)
-    v->data[i] = value;
-}
-
-void vec_add(vec_t *v, double value)
-{
-  if (v->size >= v->capacity)
-    vec_reserve(v, 2 * v->size);
-  v->data[v->size] = value;
-  v->size++;
-}
-
-size_t vec_size(vec_t *v)
-{
-  return v ? v->size : 0;
-}
-
-void vec_resize(vec_t *v, size_t size)
-{
-  v->size = size;
-  if (v->size >= v->capacity)
-    vec_reserve(v, 2 * v->size);
-}
-
-void vec_reserve(vec_t *v, size_t capacity)
-{
-  if (capacity == 0)
-    capacity = 10;
-  if (v->capacity < capacity) {
-    if (v->data == NULL)
-      v->data = (double *)malloc(capacity * sizeof(double));
-    else
-      v->data = (double *)realloc(v->data, capacity * sizeof(double));
-    v->capacity = capacity;
-  }
-}
-
-void vec_free(vec_t *v)
-{
-  if (v) {
-    if (v->data)
-      free(v->data);
-    free(v);
-  }
-}
-
-void vec_copy_cast(vec_t *v, DLiteType dtype, size_t dsize, void *dst)
-{
-  size_t vsize = vec_size(v);
-  size_t i=0;
-
-  float *dstf;
-  double *dstd;
-  float32_t *dst4;
-  float64_t *dst8;
-
-  if (vsize > 0) {
-    if (dtype == dliteFloat) {
-      if (dsize == sizeof(float)) {
-        dstf = (float*)dst;
-        for(i=0; i < vsize; i++)
-          dstf[i] = (float)(v->data[i]);
-      }
-      else if (dsize == sizeof(double)) {
-        dstd = (double*)dst;
-        for(i=0; i < vsize; i++)
-          dstd[i] = v->data[i];
-      }
-      else if (dsize == 4) {
-        dst4 = (float32_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst4[i] = (float32_t)(v->data[i]);
-      }
-      else if (dsize == 8) {
-        dst8 = (float64_t*)dst;
-        for(i=0; i < vsize; i++)
-          dst8[i] = (float64_t)(v->data[i]);
-      }
-    }
-  }
-}
-
-vec_t *vec_create(DLiteType dtype, size_t dsize, size_t num, const void *src)
-{
-  vec_t *v = NULL;
-  size_t i;
-
-  float *srcf;
-  double *srcd;
-  float32_t *src4;
-  float64_t *src8;
-
-  if (num > 0) {
-    v = vec();
-    vec_resize(v, num);
-
-    if (dtype == dliteFloat) {
-      if (dsize == sizeof(float)) {
-        srcf = (float*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (double)srcf[i];
-      }
-      else if (dsize == sizeof(double)) {
-        srcd = (double*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (double)srcd[i];
-      }
-      else if (dsize == 4) {
-        src4 = (float32_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (double)src4[i];
-      }
-      else if (dsize == 8) {
-        src8 = (float64_t*)src;
-        for(i=0; i < num; i++)
-          v->data[i] = (double)src8[i];
-      }
-    }
-  }
-  return v;
-}
-
-void vec_print(vec_t *v, char* name)
-{
-  size_t i, last;
-  if (v == NULL)
-    printf("%s = NULL\n", name);
-  else {
-    printf("%s = [", name);
-    last = v->size - 1;
-    for(i=0; i < v->size; i++)
-      if (i == last)
-        printf("%f", v->data[i]);
-      else
-        printf("%f, ", v->data[i]);
-    printf("]%c", '\n');
-  }
-}
diff --git a/storages/json/vector.h b/storages/json/vector.h
deleted file mode 100644
index af54c77cd..000000000
--- a/storages/json/vector.h
+++ /dev/null
@@ -1,63 +0,0 @@
-
-/* vector.h */
-
-#ifndef VECTOR_H
-#define VECTOR_H
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "dlite-type.h"
-
-typedef struct {
-  int *data;
-  size_t capacity;
-  size_t size;
-} ivec_t;
-
-typedef struct {
-  double *data;
-  size_t capacity;
-  size_t size;
-} vec_t;
-
-
-ivec_t *ivec();
-ivec_t *ivec1(int x);
-ivec_t *ivec2(int x, int y);
-ivec_t *ivec3(int x, int y, int z);
-ivec_t *ivecn(size_t n, int init);
-
-size_t ivec_size(ivec_t *v);
-void ivec_fill(ivec_t *v, int value);
-void ivec_add(ivec_t *v, int value);
-void ivec_resize(ivec_t *v, size_t size);
-void ivec_reserve(ivec_t *v, size_t capacity);
-void ivec_free(ivec_t *v);
-void ivec_print(ivec_t *v, char* name);
-int ivec_cumprod(ivec_t *v);
-int ivec_cumsum(ivec_t *v);
-
-void ivec_copy_cast(ivec_t *v, DLiteType dtype, size_t dsize, void *dst);
-ivec_t *ivec_create(DLiteType dtype, size_t dsize, size_t num, const void *src);
-
-vec_t *vec();
-vec_t *vec1(double x);
-vec_t *vec2(double x, double y);
-vec_t *vec3(double x, double y, double z);
-vec_t *vecn(size_t n, double init);
-
-size_t vec_size(vec_t *v);
-void vec_fill(vec_t *v, double value);
-void vec_add(vec_t *v, double value);
-void vec_resize(vec_t *v, size_t size);
-void vec_reserve(vec_t *v, size_t capacity);
-void vec_free(vec_t *v);
-void vec_print(vec_t *v, char* name);
-
-void vec_copy_cast(vec_t *v, DLiteType dtype, size_t dsize, void *dst);
-vec_t *vec_create(DLiteType dtype, size_t dsize, size_t num, const void *src);
-
-
-#endif /* VECTOR_H */
diff --git a/tools/dlite-env.c b/tools/dlite-env.c
index f4cc617e8..ac6a413c7 100644
--- a/tools/dlite-env.c
+++ b/tools/dlite-env.c
@@ -115,7 +115,7 @@ int main(int argc, char *argv[])
     int c = getopt_long(argc, argv, "behipP:v:V", longopts, &longindex);
     if (c == -1) break;
     switch (c) {
-    case 'b':  with_build = 1; break;
+    case 'b':  with_build=1; with_install=0; break;
     case 'e':  with_env = 0; break;
     case 'h':  help(stdout); exit(0);
     case 'i':  with_install = 0; break;


Test patch:
diff --git a/bindings/python/tests/CMakeLists.txt b/bindings/python/tests/CMakeLists.txt
index 369e95fa0..1566abf59 100644
--- a/bindings/python/tests/CMakeLists.txt
+++ b/bindings/python/tests/CMakeLists.txt
@@ -9,6 +9,7 @@ set(tests
   test_misc
   test_python_storage
   test_storage
+  test_paths
   )
 
 foreach(test ${tests})
diff --git a/bindings/python/tests/MyEntity.json b/bindings/python/tests/MyEntity.json
index 406ad4327..31d7d1a70 100644
--- a/bindings/python/tests/MyEntity.json
+++ b/bindings/python/tests/MyEntity.json
@@ -111,6 +111,5 @@
       ],
       "description": "A relation array"
     }
-  ],
-  "dataname": "http://onto-ns.com/meta/0.1/MyEntity"
-}
\ No newline at end of file
+  ]
+}
diff --git a/bindings/python/tests/Person.json b/bindings/python/tests/Person.json
index 329d13508..7e0dab81f 100644
--- a/bindings/python/tests/Person.json
+++ b/bindings/python/tests/Person.json
@@ -30,6 +30,5 @@
       ],
       "description": "List of skills."
     }
-  ],
-  "dataname": "http://onto-ns.com/meta/0.1/Person"
-}
\ No newline at end of file
+  ]
+}
diff --git a/bindings/python/tests/test_entity.py b/bindings/python/tests/test_entity.py
index f00d9d5e9..99aa17b74 100755
--- a/bindings/python/tests/test_entity.py
+++ b/bindings/python/tests/test_entity.py
@@ -24,7 +24,10 @@
 assert not myentity.is_metameta
 
 # Store the entity to a new file
-myentity.save('json://xxx.json')
+myentity.save('json://xxx.json?mode=w')
+
+# Save again, but without mode
+myentity.save('json://xxx2.json')
 
 # Create an instance of `myentity` with dimensions 2, 3
 # For convinience, we give it an unique label "myid" that can be used
@@ -60,11 +63,11 @@
 for i in range(len(inst)):
     print('prop%d:' % i, inst[i])
 
-# Strin representation (as json)
+# String representation (as json)
 print(inst)
 
 # Check save and load
-inst.save('json://inst.json')
+inst.save('json://inst.json?mode=w')
 inst2 = Instance('json://inst.json')
 
 # Check pickling
@@ -114,7 +117,6 @@
 else:
     inst.save('yaml://yyy.yaml')
 
-
 del inst
 del e2
 del e3
diff --git a/bindings/python/tests/test_mapping.py b/bindings/python/tests/test_mapping.py
old mode 100644
new mode 100755
index 801cf44ad..ccb626f2b
--- a/bindings/python/tests/test_mapping.py
+++ b/bindings/python/tests/test_mapping.py
@@ -3,11 +3,10 @@
 
 import dlite
 
-
+# Configure search paths
 thisdir = Path(__file__).parent.absolute()
 dlite.storage_path.append(f'{thisdir}/*.json')
-dlite.python_mapping_path.append(f'{thisdir}/python-mapping-plugins')
-
+dlite.python_mapping_plugin_path.append(f'{thisdir}/python-mapping-plugins')
 
 # Create an instance of Person
 Person = dlite.Instance(f'json:{thisdir}/Person.json?mode=r')
diff --git a/bindings/python/tests/test_paths.py b/bindings/python/tests/test_paths.py
new file mode 100644
index 000000000..a350f1c65
--- /dev/null
+++ b/bindings/python/tests/test_paths.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+from pathlib import Path
+
+import dlite
+
+
+print('dlite storage paths:')
+for path in dlite.storage_path:
+    print('- ' + path)
+
+
+print()
+print('append path with glob pattern:')
+thisdir = Path(__file__).parent.absolute()
+dlite.storage_path.append(f'{thisdir}/*.json')
+for path in dlite.storage_path:
+    print('- ' + path)
+
+print()
+print('delete second last path:')
+del dlite.storage_path[-2]
+for path in dlite.storage_path:
+    print('- ' + path)
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 4ea78a0c5..8495e1e92 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -7,7 +7,7 @@ set(tests
   test_storage
   test_entity
   test_property
-  test_print
+  test_json
   test_metamodel
   test_store
   test_triplestore
@@ -64,5 +64,7 @@ foreach(test ${tests})
     ENVIRONMENT "LD_LIBRARY_PATH=${dlite_LD_LIBRARY_PATH_NATIVE}")
   set_property(TEST ${test} APPEND PROPERTY
     ENVIRONMENT "DLITE_USE_BUILD_ROOT=YES")
+  set_property(TEST ${test} APPEND PROPERTY
+    ENVIRONMENT "DLITE_STORAGES='${CMAKE_CURRENT_SOURCE_DIR}/*.json'")
 
 endforeach()
diff --git a/src/tests/coll.json b/src/tests/coll.json
new file mode 100644
index 000000000..14b99e915
--- /dev/null
+++ b/src/tests/coll.json
@@ -0,0 +1,71 @@
+{
+  "6dd2f36a-f1e7-4a3e-a918-05bb9cd541ed": {
+    "meta": {
+      "name": "Collection",
+      "version": "0.1",
+      "namespace": "http://onto-ns.com/meta"
+    },
+    "dimensions": {
+      "nrelations": 4
+    },
+    "properties": {
+      "relations": [
+        {
+          "s": "inst",
+          "p": "_is-a",
+          "o": "Instance"
+        },
+        {
+          "s": "inst",
+          "p": "_has-uuid",
+          "o": "3ba1a32c-9536-4f16-8da9-564e6fb62d6c"
+        },
+        {
+          "s": "inst",
+          "p": "_has-meta",
+          "o": "http://onto-ns.com/meta/0.1/test-entity"
+        },
+        {
+          "s": "inst",
+          "p": "has_descr",
+          "o": "test instance"
+        }
+      ]
+    }
+  },
+  "3ba1a32c-9536-4f16-8da9-564e6fb62d6c": {
+    "meta": {
+      "name": "test-entity",
+      "version": "0.1",
+      "namespace": "http://onto-ns.com/meta"
+    },
+    "dimensions": {
+      "L": 2,
+      "M": 1,
+      "N": 3
+    },
+    "properties": {
+      "myblob": "0a0010",
+      "mydouble": 3.1400000000000001,
+      "myfixstring": "Al",
+      "mystring": "allocated string...",
+      "myshort": 17,
+      "myarray": [
+        [
+          [
+            1,
+            2,
+            3
+          ]
+        ],
+        [
+          [
+            4,
+            5,
+            6
+          ]
+        ]
+      ]
+    }
+  }
+}
diff --git a/src/tests/test-data.json b/src/tests/test-data.json
index 232f82b40..b59bf3ffe 100644
--- a/src/tests/test-data.json
+++ b/src/tests/test-data.json
@@ -1,24 +1,28 @@
 {
-  "e076a856-e36e-5335-967e-2f2fd153c17d": {
+  "204b05b2-4c89-43f4-93db-fd1cb70f54ef": {
     "meta": "http://onto-ns.com/meta/0.1/test-entity",
-    "dataname": "my_test_instance",
     "dimensions": {
       "L": 2,
-      "M": 1,
+      "M": 2,
       "N": 3
     },
     "properties": {
-      "myblob": "0a0010",
-      "mydouble": 3.1400000000000001,
-      "myfixstring": "Al",
-      "mystring": "allocated string...",
-      "myshort": 17,
+      "myblob": "0a0120",
+      "mydouble": 2.72,
+      "myfixstring": "Si",
+      "mystring": "...",
+      "myshort": 13,
       "myarray": [
         [
           [
             1,
             2,
             3
+          ],
+          [
+            3,
+            2,
+            1
           ]
         ],
         [
@@ -26,35 +30,36 @@
             4,
             5,
             6
+          ],
+          [
+            6,
+            5,
+            4
           ]
         ]
       ]
     }
   },
-  "204b05b2-4c89-43f4-93db-fd1cb70f54ef": {
+  "e076a856-e36e-5335-967e-2f2fd153c17d": {
     "meta": "http://onto-ns.com/meta/0.1/test-entity",
+    "uri": "my_test_instance",
     "dimensions": {
       "L": 2,
-      "M": 2,
+      "M": 1,
       "N": 3
     },
     "properties": {
-      "myblob": "0a0120",
-      "mydouble": 2.7200000000000002,
-      "myfixstring": "Si",
-      "mystring": "...",
-      "myshort": 13,
+      "myblob": "0a0010",
+      "mydouble": 3.14,
+      "myfixstring": "Al",
+      "mystring": "allocated string...",
+      "myshort": 17,
       "myarray": [
         [
           [
             1,
             2,
             3
-          ],
-          [
-            3,
-            2,
-            1
           ]
         ],
         [
@@ -62,14 +67,9 @@
             4,
             5,
             6
-          ],
-          [
-            6,
-            5,
-            4
           ]
         ]
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/tests/test-entity.json b/src/tests/test-entity.json
index 188b669e6..302c1446b 100644
--- a/src/tests/test-entity.json
+++ b/src/tests/test-entity.json
@@ -54,6 +54,5 @@
       ],
       "description": "An array string pointer."
     }
-  ],
-  "dataname": "http://onto-ns.com/meta/0.1/test-entity"
-}
\ No newline at end of file
+  ]
+}
diff --git a/src/tests/test-read-data.json b/src/tests/test-read-data.json
index 9ddf0193a..a9a3b2068 100644
--- a/src/tests/test-read-data.json
+++ b/src/tests/test-read-data.json
@@ -1,8 +1,36 @@
 {
-  "empty": {
-    "meta": {},
-    "dimensions": {},
-    "properties": {}
+  "0a75c038-1ea1-5916-bbd4-6d0298894f4c": {
+    "uri": "dlite/1/A",
+    "meta": "http://onto-ns.com/meta/0.3/EntitySchema",
+    "description": "dummy entity",
+    "dimensions": [
+      {"name": "N", "description": "length of P2"}
+    ],
+    "properties": [
+      {
+        "name": "P1",
+        "type": "int"
+      },
+      {
+        "name": "P2",
+        "type": "float",
+        "dims": ["N"],
+        "description": "array"
+      }
+    ]
+  },
+  "dlite/1/test-c": {
+    "meta": "http://onto-ns.com/meta/0.3/EntitySchema",
+    "dimensions": [],
+    "properties": []
+  },
+  "dlite/1/empty": {
+    "name": "empty",
+    "version": "1",
+    "namespace": "dlite",
+    "meta": "http://onto-ns.com/meta/0.3/EntitySchema",
+    "dimensions": [],
+    "properties": []
   },
   "dbd9d597-16b4-58f5-b10f-7e49cf85084b": {
     "uri": "test-a",
@@ -25,35 +53,24 @@
       ]
     }
   },
-  "b": {
-    "uri": "test-b",
-    "meta": "dlite/1.0/B",
-    "dimensions": {},
-    "properties": {}
-  },
-  "c": {
-    "uri": "test-c",
-    "meta": {},
-    "dimensions": {},
-    "properties": {}
+  "2f4ae7b7-247a-5cc2-b6c5-5ac0ccd8cc5c": {
+    "meta": "dlite/1/A",
+    "dimensions": {
+      "N": 0
+    },
+    "properties": {
+      "P1": 1,
+      "P2": []
+    }
   },
-  "dlite/1/A": {
-    "uri": "dlite/1/A",
-    "description": "dummy entity",
-    "dimensions": [
-      {"name": "N", "description": "length of P2"}
-    ],
-    "properties": [
-      {
-        "name": "P1",
-        "type": "int"
-      },
-      {
-        "name": "P2",
-        "type": "int",
-        "dims": ["N"],
-        "description": "array"
-      }
-    ]
+  "data3": {
+    "meta": "dlite/1/A",
+    "dimensions": {
+      "N": 1
+    },
+    "properties": {
+      "P1": 2,
+      "P2": [3.14]
+    }
   }
 }
diff --git a/src/tests/test_collection.c b/src/tests/test_collection.c
index 457667869..a963babdd 100644
--- a/src/tests/test_collection.c
+++ b/src/tests/test_collection.c
@@ -14,9 +14,6 @@
 DLiteCollection *coll = NULL;
 
 
-/***************************************************************
- * Test collection
- ***************************************************************/
 
 MU_TEST(test_collection_create)
 {
@@ -111,6 +108,7 @@ MU_TEST(test_collection_get)
   const DLiteInstance *inst;
   mu_check((inst = dlite_collection_get(coll, "inst")));
   mu_check(!dlite_collection_get(coll, "XXX"));
+  dlite_errclr();
 }
 
 
@@ -161,18 +159,37 @@ MU_TEST(test_collection_save)
   mu_check(dlite_storage_close(s) == 0);
 }
 
+
+
 MU_TEST(test_collection_load)
 {
-  DLiteStorage *s;
   DLiteCollection *coll2;
-  mu_check((s = dlite_storage_open("json", "coll1.json", "mode=r")));
-  mu_check((coll2 = (DLiteCollection *)dlite_instance_load(s, coll->uuid)));
-  mu_check(dlite_storage_close(s) == 0);
-
-  mu_check((s = dlite_storage_open("json", "coll2.json", "mode=w")));
-  mu_check(dlite_instance_save(s, (DLiteInstance *)coll2) == 0);
-  mu_check(dlite_storage_close(s) == 0);
-
+  char *collpath = STRINGIFY(dlite_SOURCE_DIR) "/src/tests/coll.json";
+  DLiteStoragePathIter *iter;
+  const char *path;
+
+  dlite_storage_paths_append(STRINGIFY(dlite_SOURCE_DIR) "/src/tests/*.json");
+
+  printf("\n\nStorage paths:\n");
+  iter = dlite_storage_paths_iter_start();
+  while ((path = dlite_storage_paths_iter_next(iter)))
+    printf("  - %s\n", path);
+  printf("\n");
+  dlite_storage_paths_iter_stop(iter);
+
+  FILE *fp = fopen(collpath, "r");
+  coll2 = (DLiteCollection *)
+    dlite_json_fscan(fp, NULL, "http://onto-ns.com/meta/0.1/Collection");
+  fclose(fp);
+  printf("\n\n--- coll2: %p ---\n", (void *)coll2);
+  dlite_json_print((DLiteInstance *)coll2);
+  printf("----------------------\n");
+  const DLiteInstance *inst = dlite_collection_get(coll2, "inst");
+  printf("\n--- inst: %p ---\n", (void *)inst);
+  dlite_json_print((DLiteInstance *)inst);
+  printf("----------------------\n");
+
+  dlite_instance_decref((DLiteInstance *)inst);
   dlite_collection_decref(coll2);
 }
 
@@ -188,6 +205,9 @@ MU_TEST(test_collection_free)
 
 MU_TEST_SUITE(test_suite)
 {
+  //MU_RUN_TEST(test1);
+
+
   MU_RUN_TEST(test_collection_create);     /* setup */
 
   MU_RUN_TEST(test_collection_add_relation);
diff --git a/src/tests/test_print.c b/src/tests/test_json.c
similarity index 64%
rename from src/tests/test_print.c
rename to src/tests/test_json.c
index 26acda062..89c45252b 100644
--- a/src/tests/test_print.c
+++ b/src/tests/test_json.c
@@ -36,10 +36,10 @@ 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(1066, m);
+  mu_assert_int_eq(1065, m);
 
   m = dlite_json_sprint(buf, sizeof(buf), (DLiteInstance *)meta, 2,
-                  dliteJsonUuid | dliteJsonMetaAsData);
+                        dliteJsonWithUuid | dliteJsonMetaAsData);
   printf("\n--------------------------------------------------------\n");
   printf("%s\n", buf);
   mu_assert_int_eq(1152, m);
@@ -47,12 +47,42 @@ MU_TEST(test_sprint)
   printf("\n========================================================\n");
   m = dlite_json_sprint(buf, sizeof(buf), inst, 4, 0);
   printf("%s\n", buf);
-  mu_assert_int_eq(371, m);
+  mu_assert_int_eq(404, m);
   printf("\n--------------------------------------------------------\n");
 
   m = dlite_json_sprint(buf, 80, inst, 4, 0);
-  mu_assert_int_eq(371, m);
+  mu_assert_int_eq(404, m);
+}
+
 
+int append(const char *str)
+{
+  char *s = strdup(str);
+  size_t size = strlen(s) + 1;
+  int m, retval=0;
+  printf("\n--- append: '%s' ---\n", str);
+  m = dlite_json_append(&s, &size, inst, 0);
+  dlite_errclr();
+  if (m < 0) retval=m;
+  printf("%s", s);
+  free(s);
+  return retval;
+}
+
+MU_TEST(test_append)
+{
+  mu_assert_int_eq(0, append("{}"));
+  mu_assert_int_eq(0, append("{ \t}"));
+  mu_assert_int_eq(0, append("{\"a\": 1, \"b\": [2, 3]}"));
+  mu_assert_int_eq(0, append("{\"a\": 1, \"b\": [2, 3] }"));
+  mu_assert_int_eq(0, append("{\"a\": 1, }"));  // be forgiving
+  mu_assert_int_eq(-1, append(""));
+  mu_assert_int_eq(-1, append(" "));
+  mu_assert_int_eq(-1, append("1"));
+  mu_assert_int_eq(-1, append("[1, ]"));
+  mu_assert_int_eq(-1, append(","));
+  mu_assert_int_eq(-1, append("{"));
+  mu_assert_int_eq(-1, append("[ "));
 }
 
 
@@ -70,8 +100,11 @@ MU_TEST(test_sscan)
   DLiteInstance *inst;
   char *path = STRINGIFY(dlite_SOURCE_DIR) "/src/tests/test-read-data.json";
   FILE *fp = fopen(path, "r");
+  int stat;
   mu_check(fp);
-  inst = dlite_json_fscan(fp, "dbd9d597-16b4-58f5-b10f-7e49cf85084b");
+  stat = dlite_storage_paths_append(path);
+  mu_check(stat > 0);
+  inst = dlite_json_fscan(fp, "dbd9d597-16b4-58f5-b10f-7e49cf85084b", NULL);
   fclose(fp);
   mu_check(inst);
 
@@ -79,17 +112,7 @@ MU_TEST(test_sscan)
   dlite_json_fprint(stdout, inst, 0, 0);
 
   /* cleanup */
-  int refcount;
-  const DLiteMeta *meta = inst->meta;
-  dlite_meta_incref((DLiteMeta *)meta);
-  do {
-    refcount = inst->_refcount;
-    dlite_instance_decref(inst);
-  } while (refcount > 1);
-  do {
-    refcount = meta->_refcount;
-    dlite_meta_decref((DLiteMeta *)meta);
-  } while (refcount > 1);
+  dlite_instance_decref(inst);
 }
 
 
@@ -101,6 +124,7 @@ MU_TEST_SUITE(test_suite)
 {
   MU_RUN_TEST(test_load);
   MU_RUN_TEST(test_sprint);
+  MU_RUN_TEST(test_append);
   MU_RUN_TEST(test_decref);
   MU_RUN_TEST(test_sscan);
 }
diff --git a/src/tests/test_mapping.c b/src/tests/test_mapping.c
index 224ebf645..c760d6215 100644
--- a/src/tests/test_mapping.c
+++ b/src/tests/test_mapping.c
@@ -28,6 +28,7 @@ MU_TEST(test_create_from_id)
   inst = dlite_instance_create_from_id("http://onto-ns.com/meta/0.1/ent2",
                                        NULL, NULL);
   mu_check(inst);
+
   dlite_instance_set_property(inst, "b", &b);
   p = dlite_instance_get_property(inst, "b");
   mu_assert_int_eq(-13, *p);
diff --git a/src/tests/test_misc.c b/src/tests/test_misc.c
index d8ea5119f..0bd0c121d 100644
--- a/src/tests/test_misc.c
+++ b/src/tests/test_misc.c
@@ -29,6 +29,40 @@ MU_TEST(test_get_uuid)
   mu_assert_string_eq("a839938d-1d30-5b2a-af5c-2a23d436abdc", buff);
 }
 
+MU_TEST(test_get_uuidn)
+{
+  char *id, buff[37];
+
+  mu_assert_int_eq(4, dlite_get_uuidn(buff, NULL, 0));
+  mu_assert_int_eq(4, dlite_get_uuidn(buff, NULL, 1));
+  mu_assert_int_eq(4, dlite_get_uuidn(buff, "abc", 0));
+  mu_assert_int_eq(4, dlite_get_uuidn(buff, "", 20));
+
+  mu_assert_int_eq(5, dlite_get_uuidn(buff, "abc", 3));
+  mu_assert_string_eq("6cb8e707-0fc5-5f55-88d4-d4fed43e64a8", buff);
+
+  mu_assert_int_eq(5, dlite_get_uuidn(buff, "abc", 2));
+  mu_assert_string_eq("710a586f-e1aa-54ec-93a9-85a85aa0b725", buff);
+
+  mu_assert_int_eq(5, dlite_get_uuidn(buff, "abc", 4));
+  mu_assert_string_eq("aa02945d-3cd6-5aec-82f9-0a8f51980d11", buff);
+
+
+  id = "a839938d-1d30-5b2a-af5c-2a23d436abdc";
+  mu_assert_int_eq(0, dlite_get_uuidn(buff, id, 36));
+  mu_assert_string_eq("a839938d-1d30-5b2a-af5c-2a23d436abdc", buff);
+
+  id = "a839938d-1d30-5b2a-af5c-2a23d436abdcXXX";
+  mu_assert_int_eq(0, dlite_get_uuidn(buff, id, 36));
+  mu_assert_string_eq("a839938d-1d30-5b2a-af5c-2a23d436abdc", buff);
+
+  id = "A839938D-1D30-5B2A-AF5C-2A23D436ABDC";
+  mu_assert_int_eq(0, dlite_get_uuidn(buff, id, 36));
+  mu_assert_string_eq("a839938d-1d30-5b2a-af5c-2a23d436abdc", buff);
+}
+
+
+
 
 MU_TEST(test_join_split_metadata)
 {
@@ -130,6 +164,7 @@ MU_TEST(test_split_url)
 MU_TEST_SUITE(test_suite)
 {
   MU_RUN_TEST(test_get_uuid);
+  MU_RUN_TEST(test_get_uuidn);
   MU_RUN_TEST(test_join_split_metadata);
   MU_RUN_TEST(test_option_parse);
   MU_RUN_TEST(test_join_url);
diff --git a/src/tests/test_property.c b/src/tests/test_property.c
index 4b5638eac..b740d431d 100644
--- a/src/tests/test_property.c
+++ b/src/tests/test_property.c
@@ -114,10 +114,7 @@ MU_TEST(test_print_arr)
   prop.type = dliteBool;
   prop.size = sizeof(bool);
   prop.ndims = 2;
-  printf("\n=======================\n");
   m = dlite_property_aprint(&s, &size, n, b, &prop, dims, 0, -2, 0);
-  printf("m=%d\n", m);
-  printf("\n=======================\n");
   mu_assert_int_eq(43, m);
   mu_assert_string_eq("[[true, false, true], [false, false, true]]", s);
 
diff --git a/src/tests/test_store.c b/src/tests/test_store.c
index db875b5d0..e8554af28 100644
--- a/src/tests/test_store.c
+++ b/src/tests/test_store.c
@@ -27,7 +27,7 @@ int count_uuids(DLiteStore *store) {
   return n;
 }
 
-/* Counts number adds to store */
+/* Counts number of adds to store */
 /*
 int count_adds(DLiteStore *store) {
   int n=0;
diff --git a/src/utils/tests/CMakeLists.txt b/src/utils/tests/CMakeLists.txt
index ab358bb30..3147a17b2 100644
--- a/src/utils/tests/CMakeLists.txt
+++ b/src/utils/tests/CMakeLists.txt
@@ -21,7 +21,8 @@ set(tests
   test_uuid2
   test_uuid4
   test_infixcalc
-  test_jsmn
+  test_jsmnx
+  test_jstore
 
   tgen_example
   )
diff --git a/src/utils/tests/test_fileutils.c b/src/utils/tests/test_fileutils.c
index 4ee30cc16..60164cd51 100644
--- a/src/utils/tests/test_fileutils.c
+++ b/src/utils/tests/test_fileutils.c
@@ -327,7 +327,7 @@ MU_TEST(test_fu_paths)
   mu_assert_string_eq("/c/users/path2", paths.paths[1]);
   mu_assert_string_eq(NULL,    paths.paths[2]);
 
-  mu_assert_int_eq(0, fu_paths_remove(&paths, 1));
+  mu_assert_int_eq(0, fu_paths_delete(&paths, 1));
   mu_assert_int_eq(1, paths.n);
   mu_assert_int_eq(1, count_paths(&paths));
   mu_assert_string_eq(NULL,    paths.paths[1]);
@@ -347,6 +347,12 @@ MU_TEST(test_fu_paths)
   mu_assert_string_eq("/c/users/path2", paths.paths[2]);
   mu_assert_string_eq(NULL,    paths.paths[3]);
 
+  mu_assert_int_eq(0, fu_paths_index(&paths, "path0"));
+  mu_assert_int_eq(1, fu_paths_index(&paths, "/var/path1"));
+  mu_assert_int_eq(2, fu_paths_index(&paths, "/c/users/path2"));
+  mu_assert_int_eq(-1, fu_paths_index(&paths, "non-existing-path"));
+  mu_assert_int_eq(-1, fu_paths_index(&paths, ""));
+
   mu_assert_int_eq(1, fu_paths_insert(&paths, "new", -2));
   mu_assert_int_eq(4, paths.n);
   mu_assert_string_eq("path0", paths.paths[0]);
diff --git a/src/utils/tests/test_jsmn.c b/src/utils/tests/test_jsmnx.c
similarity index 83%
rename from src/utils/tests/test_jsmn.c
rename to src/utils/tests/test_jsmnx.c
index e553521bf..7c6f54b66 100644
--- a/src/utils/tests/test_jsmn.c
+++ b/src/utils/tests/test_jsmnx.c
@@ -1,9 +1,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define JSMN_HEADER
-#include "jsmn.h"
-
+#include "jsmnx.h"
 #include "minunit/minunit.h"
 
 
@@ -18,13 +16,9 @@ MU_TEST(test_jsmn)
     "}";
 
   jsmn_parser p;
-  jsmntok_t tokens[128], *t; /* We expect no more than 128 JSON tokens */
+  jsmntok_t tokens[128];
+  const jsmntok_t *t; /* We expect no more than 128 JSON tokens */
   int r;
-  //char *name;
-  //char *type;
-  //char *dims;
-  //char *unit;
-
 
   jsmn_init(&p);
   r = jsmn_parse(&p, js, strlen(js), tokens, 128);
@@ -44,6 +38,8 @@ MU_TEST(test_jsmn)
 
 
 
+
+
 /***********************************************************************/
 
 MU_TEST_SUITE(test_suite)
diff --git a/src/utils/tests/test_jstore.c b/src/utils/tests/test_jstore.c
new file mode 100644
index 000000000..9fe953bbe
--- /dev/null
+++ b/src/utils/tests/test_jstore.c
@@ -0,0 +1,152 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "jstore.h"
+
+#include "minunit/minunit.h"
+
+
+JStore *js=NULL;
+
+
+MU_TEST(test_open)
+{
+  js = jstore_open();
+  mu_check(js);
+}
+
+
+MU_TEST(test_add)
+{
+  jstore_add(js, "pi", "3.14");
+  jstore_add(js, "arr", "[1, 2, 3]");
+  jstore_add(js, "str", "\"En af dem der red med fane\"");
+  jstore_add(js, "bool", "true");
+}
+
+MU_TEST(test_addn)
+{
+  char *src = "{\"key\": \"a truncated string\", ...}";
+  jstore_addn(js, src+2, 3, src+8, 20);
+}
+
+MU_TEST(test_addstolen)
+{
+  char *v = strdup("\"a stolen value\"");
+  jstore_addstolen(js, "str", v);  // replaces previous value
+}
+
+MU_TEST(test_get)
+{
+  mu_assert_string_eq("3.14", jstore_get(js, "pi"));
+  mu_assert_string_eq("[1, 2, 3]", jstore_get(js, "arr"));
+  mu_assert_string_eq("\"a truncated string\"", jstore_get(js, "key"));
+  mu_assert_string_eq("\"a stolen value\"", jstore_get(js, "str"));
+  mu_assert_string_eq(NULL, jstore_get(js, "xxx"));
+}
+
+MU_TEST(test_remove)
+{
+  jstore_remove(js, "pi");
+  jstore_remove(js, "xxx");
+  mu_assert_string_eq(NULL, jstore_get(js, "pi"));
+}
+
+MU_TEST(test_update)
+{
+  JStore *js2 = jstore_open();
+  jstore_update(js2, js);
+  mu_assert_string_eq("[1, 2, 3]", jstore_get(js2, "arr"));
+  jstore_close(js2);
+}
+
+MU_TEST(test_update_from_jsmn)
+{
+  char *src =
+    "{"
+    "  \"a\": 1.2,"
+    "  \"bool\": false,"
+    "  \"dict\": {\"k\": \"v\"},"
+    "  \"arr\": [4, \"a\", 3.14]"
+    "}";
+  jsmn_parser parser;
+  jsmntok_t *tokens=NULL;
+  unsigned int ntokens=0;
+  int stat;
+  jsmn_init(&parser);
+  stat = jsmn_parse_alloc(&parser, src, strlen(src), &tokens, &ntokens);
+  mu_check(stat > 0);
+  jstore_update_from_jsmn(js, src, tokens);
+  free(tokens);
+}
+
+MU_TEST(test_to_string)
+{
+  char *buf = jstore_to_string(js);
+  mu_check(buf);
+  printf("\n\n");
+  printf("-------------------------------------\n");
+  printf("%s", buf);
+  printf("-------------------------------------\n");
+  free(buf);
+}
+
+MU_TEST(test_to_file)
+{
+  int stat = jstore_to_file(js, "jstore.json");
+  mu_assert_int_eq(0, stat);
+}
+
+MU_TEST(test_update_file)
+{
+  int stat;
+  stat = jstore_add(js, "key", "\"new value\"");
+  mu_assert_int_eq(0, stat);
+  stat = jstore_update_file(js, "jstore.json");
+  mu_assert_int_eq(0, stat);
+}
+
+MU_TEST(test_iter)
+{
+  JStoreIter iter;
+  const char *key;
+  jstore_iter_init(js, &iter);
+  printf("\n\nJSON store:\n");
+  while ((key = jstore_iter_next(&iter)))
+    printf("%6s : %s\n", key, jstore_get(js, key));
+  jstore_iter_deinit(&iter);
+}
+
+MU_TEST(test_close)
+{
+  jstore_close(js);
+}
+
+
+
+/***********************************************************************/
+
+MU_TEST_SUITE(test_suite)
+{
+  MU_RUN_TEST(test_open);
+  MU_RUN_TEST(test_add);
+  MU_RUN_TEST(test_addn);
+  MU_RUN_TEST(test_addstolen);
+  MU_RUN_TEST(test_get);
+  MU_RUN_TEST(test_remove);
+  MU_RUN_TEST(test_update);
+  MU_RUN_TEST(test_update_from_jsmn);
+  MU_RUN_TEST(test_to_string);
+  MU_RUN_TEST(test_to_file);
+  MU_RUN_TEST(test_update_file);
+  MU_RUN_TEST(test_iter);
+  MU_RUN_TEST(test_close);
+}
+
+
+int main()
+{
+  MU_RUN_SUITE(test_suite);
+  MU_REPORT();
+  return (minunit_fail) ? 1 : 0;
+}
diff --git a/src/utils/tests/test_uuid.c b/src/utils/tests/test_uuid.c
index 2660fb21e..a53ac6d6c 100644
--- a/src/utils/tests/test_uuid.c
+++ b/src/utils/tests/test_uuid.c
@@ -120,7 +120,7 @@ int test_uuid_as_string()
 int test_uuid_from_string()
 {
   uuid_s u;
-  if (uuid_from_string(&u, "6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
+  if (uuid_from_string(&u, "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36))
     return 1;
   return uuid_compare(&u, &NameSpace_URL);
 }
diff --git a/storages/json/tests/CMakeLists.txt b/storages/json/tests/CMakeLists.txt
index 5c0f2fdd1..f731aeef7 100644
--- a/storages/json/tests/CMakeLists.txt
+++ b/storages/json/tests/CMakeLists.txt
@@ -2,7 +2,6 @@
 #
 
 set(tests
-  test_json_utils
   test_json_storage
   )
 
diff --git a/storages/json/tests/test_json_storage.c b/storages/json/tests/test_json_storage.c
index 12c8c1b48..25c41a4f5 100644
--- a/storages/json/tests/test_json_storage.c
+++ b/storages/json/tests/test_json_storage.c
@@ -10,159 +10,171 @@
 #include "dlite-macros.h"
 #include "dlite-datamodel.h"
 
-#include "str.h"
-#include "vector.h"
-
 #include "config.h"
 
 
+/* Forward declarations */
+DLiteInstance *json_load(const DLiteStorage *s, const char *id);
+int json_save(DLiteStorage *s, const DLiteInstance *inst);
+void *json_iter_create(const DLiteStorage *s, const char *metaid);
+int json_iter_next(void *iter, char *buf);
+void json_iter_free(void *iter);
+
+
+DLiteInstance *inst, *data3;
 
-/***************************************************************
- * Test JSON storage
- ***************************************************************/
 
-MU_TEST(test_read)
+
+MU_TEST(test_load)
 {
-  char *dbname = STRINGIFY(DLITE_ROOT) "/src/tests/test-read-data.json";
-  DLiteStorage *db = NULL;
-  DLiteDataModel *d = NULL;
-  char *s;
-  size_t dim;
-  int p1;
-  int8_t p1_i8;
-  vec_t *p2;
-  char **ids;
-  size_t i, n;
-
-  db = dlite_storage_open("json", dbname, "mode=r");
-  mu_check(db);
-
-  printf("\n");
-  ids = dlite_storage_uuids(db, NULL);
-  n = 0;
-  for(i=0; ids[i]; i++) {
-    printf("%d: %s\n", (int)i, ids[i]);
-    n++;
-  }
-  /*printf("n=%d\n", n);*/
-  mu_assert_int_eq(5, n);
-  dlite_storage_uuids_free(ids);
-
-  d = dlite_datamodel(db, "unknown");
-  mu_check(d == NULL);
-/*
-  d = dlite_datamodel(db, "empty");
-  mu_check(d);
-  mu_check(dlite_datamodel_free(d) == 0);
-*/
-  d = dlite_datamodel(db, "dbd9d597-16b4-58f5-b10f-7e49cf85084b");
-  mu_check(d);
-  s = dlite_datamodel_get_meta_uri(d);
-  mu_check(str_equal(s, "dlite/1/A"));
-  free(s);
-
-  mu_check(dlite_datamodel_has_dimension(d, "N"));
-  dim = dlite_datamodel_get_dimension_size(d, "N");
-  mu_check(dim == 5);
-  mu_assert_int_eq(0, dlite_datamodel_has_dimension(d, "M"));
-  dim = dlite_datamodel_get_dimension_size(d, "M");
-  mu_assert_int_eq(-1, dim);
-
-  mu_check(dlite_datamodel_has_property(d, "P1"));
-  mu_check(dlite_datamodel_has_property(d, "P2"));
-  mu_check(!dlite_datamodel_has_property(d, "P3"));
-
-  dlite_datamodel_get_property(d, "P1", (void*)(&p1), dliteInt,
-                               sizeof(int), 0, NULL);
-  mu_check(p1 == 24);
-  dlite_datamodel_get_property(d, "P1", (void*)(&p1_i8), dliteInt,
-                               1, 0, NULL);
-  mu_check(p1_i8 == 24);
-
-  dim = dlite_datamodel_get_dimension_size(d, "N");
-  p2 = vecn(dim, 0.0);
-  dlite_datamodel_get_property(d, "P2", (void*)(p2->data), dliteFloat,
-                               sizeof(double), 1, &dim);
-  mu_check(p2->data[0] == 1.0);
-  mu_check(p2->data[1] == 2.0);
-  mu_check(p2->data[4] == 5.5);
-  vec_free(p2);
-
-  mu_check(dlite_datamodel_free(d) == 0);
-
-  mu_check(dlite_storage_close(db) == 0);
+  char *filename = STRINGIFY(DLITE_ROOT) "/src/tests/test-read-data.json";
+  DLiteStorage *s = NULL;
+  int r;
+
+  s = dlite_storage_open("json", filename, "mode=r");
+  mu_check(s);
+
+  inst = json_load(s, "dbd9d597-16b4-58f5-b10f-7e49cf85084b");
+  mu_check(inst);
+  printf("\n--- test_load: dbd9d597-16b4-58f5-b10f-7e49cf85084b ---\n");
+  dlite_json_print(inst);
+
+  r = dlite_storage_close(s);
+  mu_assert_int_eq(0, r);
+}
+
+MU_TEST(test_load2)
+{
+  int stat;
+  char *url = STRINGIFY(DLITE_ROOT) "/src/tests/test-read-data.json"
+    "#dlite/1/test-c";
+  DLiteInstance *inst2 = dlite_instance_load_url(url);
+  mu_check(inst2);
+  stat = dlite_instance_decref(inst2);
+  mu_assert_int_eq(1, stat);  // store
+}
+
+MU_TEST(test_load3)
+{
+  int stat;
+  char *url = "json://" STRINGIFY(DLITE_ROOT) "/src/tests/test-read-data.json"
+    "#4cd9ed73-cb8d-5b98-8f3c-db5c916c53a5";
+  DLiteInstance *inst2 = dlite_instance_load_url(url);
+  mu_check(inst2);
+  stat = dlite_instance_decref(inst2);
+  mu_assert_int_eq(1, stat);  // store
+}
+
+MU_TEST(test_load4)
+{
+  int stat;
+  char *url = STRINGIFY(DLITE_ROOT) "/src/tests/test-read-data.json#dlite/1/A";
+  DLiteInstance *inst2 = dlite_instance_load_url(url);
+  mu_check(inst2);
+  stat = dlite_instance_decref(inst2);
+  mu_assert_int_eq(2, stat);  // store + inst->meta
+}
+
+MU_TEST(test_load_data3)
+{
+  char *url = STRINGIFY(DLITE_ROOT) "/src/tests/test-read-data.json#data3";
+  data3 = dlite_instance_load_url(url);
+  mu_check(data3);
+  printf("\n--- test_load_data3 ---\n");
+  dlite_json_print(data3);
 }
 
+
+
 MU_TEST(test_write)
 {
-  DLiteStorage *s=NULL;
-  DLiteDataModel *d;
+  int stat;
+  DLiteStorage *s;
+  assert(inst);
 
-  double v = 45.3;
-  int i = 11;
-  char *fix = "ab\0cd\0ef\0";
-  vec_t *ar;
-  ivec_t *ai, *dim1, *dim2;
-  size_t dims[3] = {0, 0, 0};
+  s = dlite_storage_open("json", "test-json-write.json", "mode=w");
+  mu_check(s);
 
-  mu_check((s = dlite_storage_open("json", "test-json-write.json", "mode=w")));
+  stat = json_save(s, inst);
+  mu_assert_int_eq(0, stat);
 
-  mu_check((d = dlite_datamodel(s, NULL)));
-  mu_check(dlite_datamodel_set_meta_uri(d, "dlite/1.0/xx") == 0);
-  mu_check(dlite_datamodel_set_property(d, "x", &v, dliteFloat,
-                                        sizeof(double), 0, NULL) == 0);
-  mu_check(dlite_datamodel_set_property(d, "y", &i, dliteInt,
-                                        sizeof(int), 0, NULL) == 0);
-  mu_check(dlite_datamodel_free(d) == 0);
+  stat = dlite_storage_close(s);
+  mu_assert_int_eq(0, stat);
 
-  dim1 = ivec1(5);
-  ar = vecn(ivec_cumprod(dim1), 23.0);
-  ar->data[2] = -2.0;
+  stat = dlite_instance_decref(inst);
+  mu_assert_int_eq(0, stat);
+}
 
-  dim2 = ivec2(3, 4);
-  ai = ivecn(ivec_cumprod(dim2), 22);
-  ai->data[2] = -2;
 
-  mu_check((d = dlite_datamodel(s, NULL)));
-  mu_check(dlite_datamodel_set_meta_uri(d, "dlite/1.0/yy") == 0);
+MU_TEST(test_append)
+{
+  DLiteStorage *s;
+  int stat;
 
-  ivec_copy_cast(dim1, dliteUInt, sizeof(size_t), &dims[0]);
-  mu_check(dlite_datamodel_set_dimension_size(d, "a", dims[0]) == 0);
-  mu_check(dlite_datamodel_set_property(d, "x", ar->data, dliteFloat,
-                                        sizeof(double), 1, &dims[0]) == 0);
+  s = dlite_storage_open("json", "test-json-write.json", "mode=a");
+  mu_check(s);
 
-  ivec_copy_cast(dim2, dliteUInt, sizeof(size_t), &dims[0]);
-  mu_check(dlite_datamodel_set_dimension_size(d, "b", dims[0]) == 0);
-  mu_check(dlite_datamodel_set_dimension_size(d, "c", dims[1]) == 0);
-  mu_check(dlite_datamodel_set_property(d, "y", ai->data, dliteInt,
-                                        sizeof(int), 2, &dims[0]) == 0);
+  stat = json_save(s, data3);
+  mu_assert_int_eq(0, stat);
 
+  stat = dlite_storage_close(s);
+  mu_assert_int_eq(0, stat);
 
-  dims[0] = 3;
-  mu_check(dlite_datamodel_set_property(d, "z", fix, dliteFixString,
-                                        2, 1, &dims[0]) == 0);
+  stat = dlite_instance_decref(data3);
+  mu_assert_int_eq(0, stat);
+}
 
-  mu_check(dlite_datamodel_free(d) == 0);
 
-  vec_free(ar);
-  ivec_free(ai);
-  ivec_free(dim1);
-  ivec_free(dim2);
+MU_TEST(test_iter)
+{
+  char *filename = STRINGIFY(DLITE_ROOT) "/src/tests/test-read-data.json";
+  DLiteStorage *s=NULL;
+  void *iter;
+  char uuid[DLITE_UUID_LENGTH+1];
+  int r, n=0;
+
+  s = dlite_storage_open("json", filename, "mode=r");
+  mu_check(s);
+
+  printf("\n--- test_iter ---\n");
+  iter = json_iter_create(s, NULL);
+  while ((r = json_iter_next(iter, uuid)) == 0) {
+    dlite_errclr();
+    printf("\nuuid: %s\n", uuid);
+    DLiteInstance *inst2 = dlite_instance_load(s, uuid);
+    mu_check(inst2);
+    dlite_json_print(inst2);
+    dlite_instance_decref(inst2);
+    n++;
+  }
+  mu_assert_int_eq(1, r);
+  mu_assert_int_eq(6, n);
+
+  json_iter_free(iter);
 
-  dlite_storage_close(s);
+  r = dlite_storage_close(s);
+  mu_assert_int_eq(0, r);
 }
 
+
+
+
+
 /***********************************************************************/
 
 MU_TEST_SUITE(test_suite)
 {
-  MU_RUN_TEST(test_read);
-
+  MU_RUN_TEST(test_load);
+  MU_RUN_TEST(test_load2);
+  MU_RUN_TEST(test_load3);
+  MU_RUN_TEST(test_load4);
+  MU_RUN_TEST(test_load_data3);
   MU_RUN_TEST(test_write);
+  MU_RUN_TEST(test_append);
+  MU_RUN_TEST(test_iter);
 }
 
 
-
 int main()
 {
   MU_RUN_SUITE(test_suite);
diff --git a/storages/json/tests/test_json_utils.c b/storages/json/tests/test_json_utils.c
deleted file mode 100644
index 50097ed2a..000000000
--- a/storages/json/tests/test_json_utils.c
+++ /dev/null
@@ -1,200 +0,0 @@
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "config.h"
-
-#include "minunit/minunit.h"
-#include "utils/integers.h"
-#include "utils/boolean.h"
-#include "json-utils.h"
-#include "str.h"
-
-#define STRINGIFY(s) _STRINGIFY(s)
-#define _STRINGIFY(s) # s
-
-/* Default path to json input file. */
-char *jsonfile1 = STRINGIFY(DLITE_ROOT) "/src/tests/array.json";
-char *jsonfile2 = STRINGIFY(DLITE_ROOT) "/tools/tests/Chemistry-0.1.json";
-
-MU_TEST(test_vector)
-{
-  ivec_t *v = ivec();
-  ivec_add(v, 1);
-  ivec_add(v, 2);
-  ivec_add(v, 3);
-  mu_check(ivec_size(v) == 3);
-  mu_check(v->capacity == 10);
-  mu_check(v->data[0] == 1);
-  mu_check(v->data[1] == 2);
-  mu_check(v->data[2] == 3);
-  ivec_fill(v, 5);
-  mu_check(v->data[0] == 5);
-  mu_check(v->data[1] == 5);
-  mu_check(v->data[2] == 5);
-  ivec_free(v);
-}
-
-
-MU_TEST(test_json_array)
-{
-  json_error_t error;
-  ivec_t *dims = NULL;
-  json_data_t *data = NULL;
-
-  json_t *root = json_load_file(jsonfile1, 0, &error);
-  mu_check(json_char_type(root) == 'o');
-  mu_check(json_array_type(json_object_get(root, "i1")) == 'i');
-  mu_check(json_array_type(json_object_get(root, "i2")) == 'i');
-
-  mu_check(json_array_type(json_object_get(root, "s1")) == 's');
-
-  mu_check(json_array_type(json_object_get(root, "r1")) == 'm');
-  mu_check(json_array_type(json_object_get(root, "r2")) == 'r');
-  mu_check(json_array_type(json_object_get(root, "r3")) == 'r');
-  mu_check(json_array_type(json_object_get(root, "r4")) == 'r');
-  mu_check(json_array_type(json_object_get(root, "r5")) == 'x');
-
-  mu_check(json_array_type(json_object_get(root, "o1")) == 'o');
-
-  dims = json_array_dimensions(json_object_get(root, "r4"));
-  mu_check(dims->size == 2);
-  mu_check(dims->data[0] == 3);
-  mu_check(dims->data[1] == 3);
-  ivec_free(dims);
-
-  dims = json_array_dimensions(json_object_get(root, "i1"));
-  mu_check(dims->size == 1);
-  mu_check(dims->data[0] == 9);
-  ivec_free(dims);
-
-  dims = json_array_dimensions(json_object_get(root, "i2"));
-  mu_check(dims->size == 2);
-  mu_check(dims->data[0] == 5);
-  mu_check(dims->data[1] == 3);
-  ivec_free(dims);
-
-  dims = json_array_dimensions(json_object_get(root, "i3"));
-  mu_check(dims == NULL);
-
-  data = json_get_data(json_object_get(root, "i1"));
-  /*ivec_print(data->array_i, "i1");*/
-  mu_check(data->dtype == 'i');
-  mu_check(data->array_i->size == 9);
-  mu_check(data->array_i->data[0] == 1);
-  mu_check(data->array_i->data[2] == 3);
-  mu_check(data->array_i->data[3] == 1);
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "i2"));
-  /*ivec_print(data->array_i, "i2");*/
-  mu_check(data->dtype == 'i');
-  mu_check(data->array_i->size == 15);
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "r4"));
-  /*vec_print(data->array_r, "r4");*/
-  mu_check(data->dtype == 'r');
-  mu_check(data->array_r->size == 9);
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "v-int"));
-  mu_check(data->dtype == 'i');
-  mu_check(data->dims == NULL);
-  mu_check(data->array_i->data[0] == 1);
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "v-real"));
-  mu_check(data->dtype == 'r');
-  mu_check(data->dims == NULL);
-  mu_check(data->array_r->data[0] == 2.0);
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "v-true"));
-  mu_check(data->dtype == 'b');
-  mu_check(data->dims == NULL);
-  mu_check(data->array_i->data[0] == 1);
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "v-false"));
-  mu_check(data->dtype == 'b');
-  mu_check(data->dims == NULL);
-  mu_check(data->array_i->data[0] == 0);
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "v-str"));
-  mu_check(data->dtype == 's');
-  mu_check(data->dims == NULL);
-  mu_check(str_equal(data->array_s->data[0], "Hello"));
-  str_list_free(data->array_s, true);
-  data->array_s = NULL;
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "s1"));
-  /*str_list_print(data->array_s, "s1");*/
-  mu_check(data->dtype == 's');
-  mu_check(str_list_size(data->array_s) == 3);
-  mu_check(str_equal(data->array_s->data[0], "a"));
-  mu_check(str_equal(data->array_s->data[1], "ab"));
-  mu_check(str_equal(data->array_s->data[2], "abc"));
-  str_list_free(data->array_s, true);
-  data->array_s = NULL;
-  json_data_free(data);
-
-  data = json_get_data(json_object_get(root, "s2"));
-  /*str_list_print(data->array_s, "s2");*/
-  mu_check(data->dtype == 's');
-  mu_check(str_list_size(data->array_s) == 6);
-  mu_check(str_equal(data->array_s->data[0], "a"));
-  mu_check(str_equal(data->array_s->data[1], "ab"));
-  mu_check(str_equal(data->array_s->data[2], "abc"));
-  mu_check(str_equal(data->array_s->data[3], "d"));
-  mu_check(str_equal(data->array_s->data[4], "de"));
-  mu_check(str_equal(data->array_s->data[5], "def"));
-  str_list_free(data->array_s, true);
-  data->array_s = NULL;
-  json_data_free(data);
-
-  json_decref(root);
-}
-
-MU_TEST(test_json_entity)
-{
-  json_error_t error;
-  json_t *dims;
-  json_t *prop;
-
-  json_t *root = json_load_file(jsonfile2, 0, &error);
-  mu_check(json_is_object(root));
-
-  dims = json_object_get(root, "dimensions");
-  prop = json_object_get(root, "properties");
-
-  mu_check(check_dimensions("alloy", json_array_get(prop, 0), dims) == 1);
-  mu_check(check_dimensions("elements", json_array_get(prop, 1), dims) == 1);
-  //mu_assert_int_eq(2, dlite_json_entity_dim_count(root));
-  //mu_assert_int_eq(8, dlite_json_entity_prop_count(root));
-
-  json_decref(root);
-}
-
-/***********************************************************************/
-
-MU_TEST_SUITE(test_suite)
-{
-  MU_RUN_TEST(test_vector);
-  MU_RUN_TEST(test_json_array);
-  MU_RUN_TEST(test_json_entity);
-}
-
-
-
-int main(int argc, char *argv[])
-{
-  if (argc > 1) jsonfile2 = argv[1];
-
-  MU_RUN_SUITE(test_suite);
-  MU_REPORT();
-  return (minunit_fail) ? 1 : 0;
-}
diff --git a/tools/tests/Chemistry-0.1.json b/tools/tests/Chemistry-0.1.json
index 32c722e04..ef79a4e6b 100644
--- a/tools/tests/Chemistry-0.1.json
+++ b/tools/tests/Chemistry-0.1.json
@@ -81,6 +81,5 @@
       ],
       "description": "Average volume per atom for each phase."
     }
-  ],
-  "dataname": "http://sintef.no/calm/0.1/Chemistry"
-}
\ No newline at end of file
+  ]
+}