diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20f2498a09e5599933c18606febf8f160594a3c8..33142766ee95866ec4e3052ed3d7437fae0d2dd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## [Unreleased] ##
 
 ### Added ###
+
 - convenience functions `value_matches_versionid`, `get_id_from_versionid` and `get_versionid`
+- Parameter for high level API serialization to output a plain JSON.
 
 ### Changed ###
 
diff --git a/src/linkahead/apiutils.py b/src/linkahead/apiutils.py
index b2a612faea1616c64b7e78575156abccfdb29e61..f0fe2719a80d6a14e994cc2485baf5b3e0a5a65b 100644
--- a/src/linkahead/apiutils.py
+++ b/src/linkahead/apiutils.py
@@ -291,9 +291,10 @@ def compare_entities(entity0: Optional[Entity] = None,
     if entity0 is entity1:
         return diff
 
+    # FIXME Why not simply return a diff which says that the types are different?
     if type(entity0) is not type(entity1):
-        raise ValueError(
-            "Comparison of different Entity types is not supported.")
+        diff[0]["type"] = type(entity0)
+        diff[1]["type"] = type(entity1)
 
     # compare special attributes
     for attr in SPECIAL_ATTRIBUTES:
diff --git a/src/linkahead/common/models.py b/src/linkahead/common/models.py
index 8b8141bea1a3dc5dba28257136fe03fdea2f6ba0..6de271535a04f82dcf65492d810d43eca7496e1d 100644
--- a/src/linkahead/common/models.py
+++ b/src/linkahead/common/models.py
@@ -1738,8 +1738,8 @@ def _parse_value(datatype, value):
             return ret
 
     # This is for a special case, where the xml parser could not differentiate
-    # between single values and lists with one element. As
-    if hasattr(value, "__len__") and len(value) == 1:
+    # between single values and lists with one element.
+    if hasattr(value, "__len__") and not isinstance(value, str) and len(value) == 1:
         return _parse_value(datatype, value[0])
 
     # deal with references
diff --git a/src/linkahead/high_level_api.py b/src/linkahead/high_level_api.py
index 9aa59fb9187ff47e71c412568af50e1031c42fb7..a0137dacf048238fe7f72dcc13469c77829c1a28 100644
--- a/src/linkahead/high_level_api.py
+++ b/src/linkahead/high_level_api.py
@@ -475,8 +475,7 @@ class CaosDBPythonEntity(object):
 
         if isinstance(att, list):
             return att
-        else:
-            return [att]
+        return [att]
 
     def add_parent(self, parent: Union[
             CaosDBPythonUnresolvedParent, "CaosDBPythonRecordType", str]):
@@ -683,53 +682,78 @@ class CaosDBPythonEntity(object):
 
         return entity
 
-    def serialize(self, without_metadata: bool = False, visited: dict = None):
-        """
-        Serialize necessary information into a dict.
+    def serialize(self, without_metadata: bool = None, plain_json: bool = False,
+                  visited: dict = None) -> dict:
+        """Serialize necessary information into a dict.
+
+        Parameters
+        ----------
+
+        without_metadata: bool, optional
+          If True don't set the metadata field in order to increase
+          readability. Not recommended if deserialization is needed.
+
+        plain_json: bool, optional
+          If True, serialize to a plain dict without any additional information besides the property values,
+          name and id.  This should conform to the format as specified by the json schema generated by the
+          advanced user tools.  It also sets all properties as top level items of the resulting dict.  This
+          implies ``without_metadata = True
+
+        Returns
+        -------
 
-        without_metadata: bool
-                          If True don't set the metadata field in order to increase
-                          readability. Not recommended if deserialization is needed.
+        out: dict
+          A dict corresponding to this entity.
+        ``.
         """
+        if plain_json:
+            if without_metadata is None:
+                without_metadata = True
+            if not without_metadata:
+                raise ValueError("`plain_json` implies `without_metadata`.")
+        if without_metadata is None:
+            without_metadata = False
 
         if visited is None:
-            visited = dict()
+            visited = {}
 
         if self in visited:
             return visited[self]
 
-        metadata: Dict[str, Any] = dict()
-        properties = dict()
-        parents = list()
+        metadata: Dict[str, Any] = {}
+        properties = {}
+        parents = []
 
         # The full information to be returned:
-        fulldict = dict()
+        fulldict = {}
         visited[self] = fulldict
 
-        # Add CaosDB role:
-        fulldict["role"] = standard_type_for_high_level_type(self, True)
-
         for parent in self._parents:
             if isinstance(parent, CaosDBPythonEntity):
-                parents.append(parent.serialize(without_metadata, visited))
+                parents.append(parent.serialize(without_metadata=without_metadata,
+                                                plain_json=plain_json,
+                                                visited=visited))
             elif isinstance(parent, CaosDBPythonUnresolvedParent):
                 parents.append({"name": parent.name, "id": parent.id,
                                 "unresolved": True})
             else:
                 raise RuntimeError("Incompatible class used as parent.")
 
-        for baseprop in ("name", "id", "description", "version"):
-            val = self.__getattribute__(baseprop)
-            if val is not None:
-                fulldict[baseprop] = val
+        if not plain_json:
+            # Add LinkAhead role:
+            fulldict["role"] = standard_type_for_high_level_type(self, True)
+            for baseprop in ("name", "id", "description", "version"):
+                val = self.__getattribute__(baseprop)
+                if val is not None:
+                    fulldict[baseprop] = val
 
-        if type(self) == CaosDBPythonFile:
-            fulldict["file"] = self.file
-            fulldict["path"] = self.path
+            if isinstance(self, CaosDBPythonFile):
+                fulldict["file"] = self.file
+                fulldict["path"] = self.path
 
         for p in self.get_properties():
             m = self.get_property_metadata(p)
-            metadata[p] = dict()
+            metadata[p] = {}
             for f in fields(m):
                 val = m.__getattribute__(f.name)
                 if val is not None:
@@ -739,30 +763,37 @@ class CaosDBPythonEntity(object):
             if isinstance(val, CaosDBPythonUnresolvedReference):
                 properties[p] = {"id": val.id, "unresolved": True}
             elif isinstance(val, CaosDBPythonEntity):
-                properties[p] = val.serialize(without_metadata, visited)
+                properties[p] = val.serialize(without_metadata=without_metadata,
+                                              plain_json=plain_json,
+                                              visited=visited)
             elif isinstance(val, list):
                 serializedelements = []
                 for element in val:
                     if isinstance(element, CaosDBPythonUnresolvedReference):
-                        elm = dict()
+                        elm = {}
                         elm["id"] = element.id
                         elm["unresolved"] = True
                         serializedelements.append(elm)
                     elif isinstance(element, CaosDBPythonEntity):
                         serializedelements.append(
-                            element.serialize(without_metadata,
-                                              visited))
+                            element.serialize(without_metadata=without_metadata,
+                                              plain_json=plain_json,
+                                              visited=visited))
                     else:
                         serializedelements.append(element)
                 properties[p] = serializedelements
             else:
                 properties[p] = val
 
-        fulldict["properties"] = properties
-        fulldict["parents"] = parents
-
-        if not without_metadata:
-            fulldict["metadata"] = metadata
+        if plain_json:
+            fulldict["id"] = getattr(self, "id")
+            fulldict["name"] = getattr(self, "name")
+            fulldict.update(properties)
+        else:
+            fulldict["properties"] = properties
+            fulldict["parents"] = parents
+            if not without_metadata:
+                fulldict["metadata"] = metadata
         return fulldict
 
     def __str__(self):
diff --git a/unittests/test_high_level_api.py b/unittests/test_high_level_api.py
index 82c1a5caf0f0719b5946ecd6749b4079bb6794bc..e35dc678f7d0f44d1bb8fa763cf8dfc8225e3aee 100644
--- a/unittests/test_high_level_api.py
+++ b/unittests/test_high_level_api.py
@@ -322,6 +322,7 @@ def test_wrong_entity_for_file():
 
 
 def test_serialization():
+    # With ID
     r = db.Record(id=5, name="test", description="ok")
     r.add_property(name="v", value=15, datatype=db.INTEGER, unit="kpx",
                    importance="RECOMMENDED")
@@ -333,6 +334,22 @@ def test_serialization():
     for teststr in teststrs:
         assert teststr in text
 
+    serialized = convert_to_python_object(r).serialize()
+    assert serialized == {'role': 'Record',
+                          'name': 'test',
+                          'id': 5,
+                          'description': 'ok',
+                          'properties': {'v': 15},
+                          'parents': [],
+                          'metadata': {'v': {'unit': 'kpx',
+                                             'datatype': 'INTEGER',
+                                             'importance': 'RECOMMENDED'}}}
+
+    serialized_plain = convert_to_python_object(r).serialize(plain_json=True)
+    assert serialized_plain == {'id': 5, 'name': 'test', 'v': 15}
+
+    # Without ID
+
     r = db.Record(description="ok")
     r.add_property(name="v", value=15, datatype=db.INTEGER, unit="kpx",
                    importance="RECOMMENDED")
@@ -341,6 +358,18 @@ def test_serialization():
     assert "name" not in text
     assert "id" not in text
 
+    serialized = convert_to_python_object(r).serialize()
+    assert serialized == {'role': 'Record',
+                          'description': 'ok',
+                          'properties': {'v': 15},
+                          'parents': [],
+                          'metadata': {'v': {'unit': 'kpx',
+                                             'datatype': 'INTEGER',
+                                             'importance': 'RECOMMENDED'}}}
+
+    serialized_plain = convert_to_python_object(r).serialize(plain_json=True)
+    assert serialized_plain == {'id': None, 'name': None, 'v': 15}
+
 
 def test_files():
     # empty file:
diff --git a/unittests/test_issues.py b/unittests/test_issues.py
index 3b0117b28c1300ea1eb0919fce02e3881c2ab025..ed125df9103c8f9c9a69fe8632265d3f38c377dc 100644
--- a/unittests/test_issues.py
+++ b/unittests/test_issues.py
@@ -93,6 +93,12 @@ def test_issue_128():
     assert prop_list.value == [now, now]
 
 
+def test_parse_datatype():
+    """No infinite recursion."""
+    from linkahead.common.models import _parse_value
+    assert 1 == _parse_value("labels0", "1")
+
+
 def test_issue_73():
     """
     Test to_xml infinite recursion handling with cross- and self-references.