Repository: Informasjonsforvaltning/fdk-rdf-parser

Issue:
Legge til parsing av enkelte resurser isteden for å bruke parseResources
- [ ] parseDataset
- [ ] parseDataService
- [ ] parseConcept
- [ ] parseInformationModel
- [ ] parseService
- [ ] parseEvent

Resultatet skal være root til objektet, for eks:
```
{
  "identifier": "xxxx",
  "publisher": {}
}
```

Discussion:
github-actions[bot]: ```
Warning: poetry-plugin-export will not be installed by default in a future version of Poetry.
In order to avoid a breaking change and make your automation forward-compatible, please install poetry-plugin-export explicitly. See https://python-poetry.org/docs/plugins/#using-plugins for details on how to install a plugin.
To disable this warning run 'poetry config warnings.export false'.
The `--dev` option is deprecated, use the `--with dev` notation instead.
+==============================================================================+

                                   /$$$$$$            /$$
                                  /$$__  $$          | $$
               /$$$$$$$  /$$$$$$ | $$  \__//$$$$$$  /$$$$$$   /$$   /$$
              /$$_____/ |____  $$| $$$$   /$$__  $$|_  $$_/  | $$  | $$
             |  $$$$$$   /$$$$$$$| $$_/  | $$$$$$$$  | $$    | $$  | $$
              \____  $$ /$$__  $$| $$    | $$_____/  | $$ /$$| $$  | $$
              /$$$$$$$/|  $$$$$$$| $$    |  $$$$$$$  |  $$$$/|  $$$$$$$
             |_______/  \_______/|__/     \_______/   \___/   \____  $$
                                                              /$$  | $$
                                                             |  $$$$$$/
      by safetycli.com                                        \______/

+==============================================================================+

 REPORT 

  Safety is using PyUp's free open-source vulnerability database. This
data is 30 days old and limited. 
  For real-time enhanced vulnerability data, fix recommendations, severity
reporting, cybersecurity support, team and project policy management and more
sign up at https://pyup.io or email sales@pyup.io

  Safety v3.0.1 is scanning for Vulnerabilities...
  Scanning dependencies in your files:

  -> /tmp/tmp4cchi0qh

  Using open-source vulnerability database
  Found and scanned 61 packages
  Timestamp 2024-03-12 15:43:21
  0 vulnerabilities reported
  0 vulnerabilities ignored

+==============================================================================+

 No known security vulnerabilities found.

+==============================================================================+

  Safety is using PyUp's free open-source vulnerability database. This
data is 30 days old and limited. 
  For real-time enhanced vulnerability data, fix recommendations, severity
reporting, cybersecurity support, team and project policy management and more
sign up at https://pyup.io or email sales@pyup.io

+==============================================================================+
```
<!-- Sticky Pull Request CommentSafety result -->
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Informasjonsforvaltning/fdk-rdf-parser/pull/279?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Informasjonsforvaltning) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 100.00%. Comparing base [(`f49c936`)](https://app.codecov.io/gh/Informasjonsforvaltning/fdk-rdf-parser/commit/f49c93611bfc6c9dd5596990e67d746390fff74d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Informasjonsforvaltning) to head [(`2aac5c7`)](https://app.codecov.io/gh/Informasjonsforvaltning/fdk-rdf-parser/pull/279?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Informasjonsforvaltning).


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##              main      #279   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           87        87           
  Lines         2290      2341   +51     
  Branches       255       265   +10     
=========================================
+ Hits          2290      2341   +51     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Informasjonsforvaltning/fdk-rdf-parser/pull/279?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Informasjonsforvaltning).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Informasjonsforvaltning).


Patch:
diff --git a/src/fdk_rdf_parser/__init__.py b/src/fdk_rdf_parser/__init__.py
index 8034a3e..c49ac37 100644
--- a/src/fdk_rdf_parser/__init__.py
+++ b/src/fdk_rdf_parser/__init__.py
@@ -1,8 +1,20 @@
 from .fdk_rdf_parser import (
+    parse_concept,
+    parse_concept_as_dict,
     parse_concepts,
+    parse_data_service,
     parse_data_services,
+    parse_dataservice_as_dict,
+    parse_dataset,
+    parse_dataset_as_dict,
     parse_datasets,
+    parse_event,
+    parse_event_as_dict,
     parse_events,
+    parse_information_model,
+    parse_information_model_as_dict,
     parse_information_models,
     parse_public_services,
+    parse_service,
+    parse_service_as_dict,
 )
diff --git a/src/fdk_rdf_parser/classes/exceptions.py b/src/fdk_rdf_parser/classes/exceptions.py
new file mode 100644
index 0000000..09384ca
--- /dev/null
+++ b/src/fdk_rdf_parser/classes/exceptions.py
@@ -0,0 +1,15 @@
+from rdflib.exceptions import ParserError as RdflibParserError
+
+
+class ParserError(RdflibParserError):
+    def __init__(self: "ParserError", msg: str = "Parser error") -> None:
+        RdflibParserError.__init__(self, msg)
+        self.msg: str = msg
+
+
+class MultipleResourcesError(ValueError):
+    pass
+
+
+class MissingResourceError(ValueError):
+    pass
diff --git a/src/fdk_rdf_parser/classes/types.py b/src/fdk_rdf_parser/classes/types.py
new file mode 100644
index 0000000..02fe439
--- /dev/null
+++ b/src/fdk_rdf_parser/classes/types.py
@@ -0,0 +1,16 @@
+from typing import (
+    TypeVar,
+    Union,
+)
+
+from fdk_rdf_parser.classes.concept import Concept
+from fdk_rdf_parser.classes.cpsvno_service import Service
+from fdk_rdf_parser.classes.dataservice import DataService
+from fdk_rdf_parser.classes.dataset import Dataset
+from fdk_rdf_parser.classes.event import Event
+from fdk_rdf_parser.classes.info_model import InformationModel
+
+ResourceType = TypeVar(
+    "ResourceType",
+    bound=Union[Dataset, DataService, Concept, InformationModel, Event, Service],
+)
diff --git a/src/fdk_rdf_parser/fdk_rdf_parser.py b/src/fdk_rdf_parser/fdk_rdf_parser.py
index f058163..0c0dbbb 100644
--- a/src/fdk_rdf_parser/fdk_rdf_parser.py
+++ b/src/fdk_rdf_parser/fdk_rdf_parser.py
@@ -1,15 +1,25 @@
+from dataclasses import asdict
 from typing import (
+    Any,
+    Callable,
     Dict,
-    Optional,
+    Union,
 )
 
 from rdflib import Graph
+from rdflib.exceptions import ParserError as RdflibParserError
 from rdflib.namespace import (
     FOAF,
     RDF,
     SKOS,
 )
 
+from fdk_rdf_parser.classes.exceptions import (
+    MissingResourceError,
+    MultipleResourcesError,
+    ParserError,
+)
+from fdk_rdf_parser.classes.types import ResourceType
 from .classes import (
     Concept,
     DataService,
@@ -19,13 +29,13 @@ from .classes import (
     Service,
 )
 from .parse_functions import (
-    parse_concept,
-    parse_cpsvno_service,
-    parse_data_service,
-    parse_dataset,
-    parse_dataset_series,
-    parse_event,
-    parse_information_model,
+    _parse_concept,
+    _parse_cpsvno_service,
+    _parse_data_service,
+    _parse_dataset,
+    _parse_dataset_series,
+    _parse_event,
+    _parse_information_model,
 )
 from .rdf_utils import (
     cpsv_uri,
@@ -52,7 +62,7 @@ def parse_data_services(
         if primary_topic_uri and is_type(
             dcat_uri("DataService"), data_services_graph, primary_topic_uri
         ):
-            data_service = parse_data_service(
+            data_service = _parse_data_service(
                 data_services_graph, record_uri, primary_topic_uri
             )
 
@@ -74,7 +84,7 @@ def parse_datasets(rdf_data: str, rdf_format: str = "turtle") -> Dict[str, Datas
             is_type(dcat_uri("Dataset"), datasets_graph, primary_topic_uri)
             or is_type(dcat_uri("DatasetSeries"), datasets_graph, primary_topic_uri)
         ):
-            partial_dataset = parse_dataset(
+            partial_dataset = _parse_dataset(
                 datasets_graph, record_uri, primary_topic_uri
             )
 
@@ -82,7 +92,7 @@ def parse_datasets(rdf_data: str, rdf_format: str = "turtle") -> Dict[str, Datas
             dataset.add_values_from_partial(values=partial_dataset)
 
             if is_type(dcat_uri("DatasetSeries"), datasets_graph, primary_topic_uri):
-                series = parse_dataset_series(datasets_graph, primary_topic_uri)
+                series = _parse_dataset_series(datasets_graph, primary_topic_uri)
                 series.add_values_from_dataset(values=dataset)
                 datasets[primary_topic_uri.toPython()] = series
             else:
@@ -108,7 +118,7 @@ def parse_information_models(
             info_models_graph,
             primary_topic_uri,
         ):
-            info_model = parse_information_model(
+            info_model = _parse_information_model(
                 info_models_graph, record_uri, primary_topic_uri
             )
 
@@ -143,7 +153,7 @@ def parse_public_services(
                 primary_topic_uri,
             )
         ):
-            cpsvno_service = parse_cpsvno_service(
+            cpsvno_service = _parse_cpsvno_service(
                 cpsvno_services_graph, catalog_record_uri, primary_topic_uri
             )
 
@@ -152,10 +162,8 @@ def parse_public_services(
     return cpsvno_services
 
 
-def parse_events(
-    event_rdf: str, rdf_format: str = "turtle"
-) -> Dict[str, Optional[Event]]:
-    events: Dict[str, Optional[Event]] = {}
+def parse_events(event_rdf: str, rdf_format: str = "turtle") -> Dict[str, Event]:
+    events: Dict[str, Event] = {}
     graph = Graph().parse(data=event_rdf, format=rdf_format)
 
     for catalog_record_uri in graph.subjects(
@@ -180,7 +188,7 @@ def parse_events(
                 primary_topic_uri,
             )
         ):
-            event = parse_event(graph, catalog_record_uri, primary_topic_uri)
+            event = _parse_event(graph, catalog_record_uri, primary_topic_uri)
 
             events[primary_topic_uri.toPython()] = event
 
@@ -201,8 +209,80 @@ def parse_concepts(concepts_rdf: str, rdf_format: str = "turtle") -> Dict[str, C
             concepts_graph,
             primary_topic_uri,
         ):
-            concept = parse_concept(concepts_graph, record_uri, primary_topic_uri)
+            concept = _parse_concept(concepts_graph, record_uri, primary_topic_uri)
 
             concepts[primary_topic_uri.toPython()] = concept
 
     return concepts
+
+
+def _parse_resource(
+    graph: str,
+    rdf_format: str,
+    parse_func: Union[
+        Callable[
+            [Graph, str],
+            Dict[str, ResourceType],
+        ]
+    ],
+) -> ResourceType:
+    try:
+        parse_result = parse_func(graph, rdf_format)
+    except (RdflibParserError, SyntaxError) as err:
+        raise ParserError() from err
+    if parse_result is None or len(parse_result) == 0:
+        raise MissingResourceError()
+    elif len(parse_result) > 1:
+        raise MultipleResourcesError()
+    resource = list(parse_result.values())[0]
+    return resource
+
+
+def parse_dataset(graph: str, rdf_format: str = "turtle") -> Dataset:
+    return _parse_resource(graph, rdf_format, parse_datasets)
+
+
+def parse_dataset_as_dict(graph: str, rdf_format: str = "turtle") -> Dict[str, Any]:
+    return asdict(parse_dataset(graph, rdf_format))
+
+
+def parse_concept(graph: str, rdf_format: str = "turtle") -> Concept:
+    return _parse_resource(graph, rdf_format, parse_concepts)
+
+
+def parse_concept_as_dict(graph: str, rdf_format: str = "turtle") -> Dict[str, Any]:
+    return asdict(parse_concept(graph, rdf_format))
+
+
+def parse_data_service(graph: str, rdf_format: str = "turtle") -> DataService:
+    return _parse_resource(graph, rdf_format, parse_data_services)
+
+
+def parse_dataservice_as_dict(graph: str, rdf_format: str = "turtle") -> Dict[str, Any]:
+    return asdict(parse_data_service(graph, rdf_format))
+
+
+def parse_information_model(graph: str, rdf_format: str = "turtle") -> InformationModel:
+    return _parse_resource(graph, rdf_format, parse_information_models)
+
+
+def parse_information_model_as_dict(
+    graph: str, rdf_format: str = "turtle"
+) -> Dict[str, Any]:
+    return asdict(parse_information_model(graph, rdf_format))
+
+
+def parse_service(graph: str, rdf_format: str = "turtle") -> Service:
+    return _parse_resource(graph, rdf_format, parse_public_services)
+
+
+def parse_service_as_dict(graph: str, rdf_format: str = "turtle") -> Dict[str, Any]:
+    return asdict(parse_service(graph, rdf_format))
+
+
+def parse_event(graph: str, rdf_format: str = "turtle") -> Event:
+    return _parse_resource(graph, rdf_format, parse_events)
+
+
+def parse_event_as_dict(graph: str, rdf_format: str = "turtle") -> Dict[str, Any]:
+    return asdict(parse_event(graph, rdf_format))
diff --git a/src/fdk_rdf_parser/parse_functions/__init__.py b/src/fdk_rdf_parser/parse_functions/__init__.py
index 90786ed..02e4389 100644
--- a/src/fdk_rdf_parser/parse_functions/__init__.py
+++ b/src/fdk_rdf_parser/parse_functions/__init__.py
@@ -1,24 +1,24 @@
 from .agent import extract_participating_agents
 from .catalog import parse_catalog
 from .channel import extract_channels
-from .concept import parse_concept
+from .concept import _parse_concept
 from .conforms_to import extract_conforms_to
 from .contactpoint import extract_contact_points
 from .cost import extract_costs
-from .cpsvno_service import parse_cpsvno_service
-from .dataservice import parse_data_service
+from .cpsvno_service import _parse_cpsvno_service
+from .dataservice import _parse_data_service
 from .dataset import (
-    parse_dataset,
-    parse_dataset_series,
+    _parse_dataset,
+    _parse_dataset_series,
 )
 from .dcat_resource import parse_dcat_resource
 from .dct_standard import extract_dct_standard_list
 from .distribution import extract_distributions
-from .event import parse_event
+from .event import _parse_event
 from .evidence import extract_evidences
 from .format import extract_formats
 from .harvest_meta_data import extract_meta_data
-from .info_model import parse_information_model
+from .info_model import _parse_information_model
 from .legal_resource import extract_legal_resources
 from .output import extract_outputs
 from .participation import parse_participation
diff --git a/src/fdk_rdf_parser/parse_functions/concept.py b/src/fdk_rdf_parser/parse_functions/concept.py
index 03ff583..4d448c8 100644
--- a/src/fdk_rdf_parser/parse_functions/concept.py
+++ b/src/fdk_rdf_parser/parse_functions/concept.py
@@ -284,7 +284,9 @@ def parse_label_set(
     return values if len(values) > 0 else None
 
 
-def parse_concept(graph: Graph, fdk_record_uri: URIRef, concept_uri: URIRef) -> Concept:
+def _parse_concept(
+    graph: Graph, fdk_record_uri: URIRef, concept_uri: URIRef
+) -> Concept:
     concept_temporal_list = extract_temporal(graph, concept_uri)
     concept_temporal = concept_temporal_list[0] if concept_temporal_list else Temporal()
     contact_points = extract_contact_points(graph, concept_uri)
diff --git a/src/fdk_rdf_parser/parse_functions/cpsvno_service.py b/src/fdk_rdf_parser/parse_functions/cpsvno_service.py
index 5dad533..027a45c 100644
--- a/src/fdk_rdf_parser/parse_functions/cpsvno_service.py
+++ b/src/fdk_rdf_parser/parse_functions/cpsvno_service.py
@@ -71,7 +71,7 @@ def extract_cpsvno_services(
     return values if len(values) > 0 else None
 
 
-def parse_cpsvno_service(
+def _parse_cpsvno_service(
     services_graph: Graph, catalog_record_uri: URIRef, cpsvno_service_uri: URIRef
 ) -> Service:
     theme_refs = split_theme_refs(
diff --git a/src/fdk_rdf_parser/parse_functions/dataservice.py b/src/fdk_rdf_parser/parse_functions/dataservice.py
index b6ed32d..c1ed570 100644
--- a/src/fdk_rdf_parser/parse_functions/dataservice.py
+++ b/src/fdk_rdf_parser/parse_functions/dataservice.py
@@ -21,7 +21,7 @@ from .media_type import extract_fdk_format
 from .skos_concept import extract_skos_concept
 
 
-def parse_data_service(
+def _parse_data_service(
     data_services_graph: Graph, record_uri: URIRef, data_service_uri: URIRef
 ) -> DataService:
     data_service = DataService(
diff --git a/src/fdk_rdf_parser/parse_functions/dataset.py b/src/fdk_rdf_parser/parse_functions/dataset.py
index 8ff2847..3831478 100644
--- a/src/fdk_rdf_parser/parse_functions/dataset.py
+++ b/src/fdk_rdf_parser/parse_functions/dataset.py
@@ -54,7 +54,7 @@ from .subject import extract_subjects
 from .temporal import extract_temporal
 
 
-def parse_dataset(
+def _parse_dataset(
     datasets_graph: Graph, record_uri: URIRef, dataset_uri: URIRef
 ) -> PartialDataset:
     quality_annotations = extract_quality_annotation(datasets_graph, dataset_uri)
@@ -152,7 +152,7 @@ def parse_dataset(
     return dataset
 
 
-def parse_dataset_series(datasets_graph: Graph, dataset_uri: URIRef) -> DatasetSeries:
+def _parse_dataset_series(datasets_graph: Graph, dataset_uri: URIRef) -> DatasetSeries:
     prev_dataset_uri = object_value(datasets_graph, dataset_uri, dcat_uri("last"))
     return DatasetSeries(
         last=prev_dataset_uri,
diff --git a/src/fdk_rdf_parser/parse_functions/event.py b/src/fdk_rdf_parser/parse_functions/event.py
index c27d6be..28f3adf 100644
--- a/src/fdk_rdf_parser/parse_functions/event.py
+++ b/src/fdk_rdf_parser/parse_functions/event.py
@@ -71,9 +71,7 @@ def extract_broader_types(graph: Graph, event_subject: URIRef) -> Optional[List[
     )
 
 
-def parse_event(
-    graph: Graph, catalog_record_uri: URIRef, subject: URIRef
-) -> Optional[Event]:
+def _parse_event(graph: Graph, catalog_record_uri: URIRef, subject: URIRef) -> Event:
     event = Event(
         id=object_value(graph, catalog_record_uri, DCTERMS.identifier),
         uri=subject.toPython(),
diff --git a/src/fdk_rdf_parser/parse_functions/info_model.py b/src/fdk_rdf_parser/parse_functions/info_model.py
index 0470290..0092e62 100644
--- a/src/fdk_rdf_parser/parse_functions/info_model.py
+++ b/src/fdk_rdf_parser/parse_functions/info_model.py
@@ -1,4 +1,7 @@
-from typing import List, Optional
+from typing import (
+    List,
+    Optional,
+)
 
 from rdflib import (
     BNode,
@@ -39,7 +42,7 @@ from .spatial import extract_dct_spatial_list
 from .temporal import extract_temporal
 
 
-def parse_information_model(
+def _parse_information_model(
     graph: Graph, fdk_record_uri: URIRef, info_model_uri: URIRef
 ) -> InformationModel:
     subjects = value_set(graph, info_model_uri, DCTERMS.subject)
diff --git a/src/fdk_rdf_parser/parse_functions/media_type.py b/src/fdk_rdf_parser/parse_functions/media_type.py
index faaab2a..5b1e52a 100644
--- a/src/fdk_rdf_parser/parse_functions/media_type.py
+++ b/src/fdk_rdf_parser/parse_functions/media_type.py
@@ -12,7 +12,10 @@ from rdflib import (
     URIRef,
 )
 
-from fdk_rdf_parser.classes import MediaTypeOrExtent, MediaTypeOrExtentType
+from fdk_rdf_parser.classes import (
+    MediaTypeOrExtent,
+    MediaTypeOrExtentType,
+)
 from fdk_rdf_parser.rdf_utils import (
     dcat_uri,
     dct_uri,


Test patch:
diff --git a/tests/test_concept.py b/tests/test_concept.py
index 88bc261..5b2f7b6 100644
--- a/tests/test_concept.py
+++ b/tests/test_concept.py
@@ -1,3 +1,4 @@
+from dataclasses import asdict
 from unittest.mock import Mock
 
 from rdflib import (
@@ -21,7 +22,11 @@ from fdk_rdf_parser.classes.concept import (
     Subject,
     TextAndURI,
 )
-from fdk_rdf_parser.parse_functions import parse_concept
+from fdk_rdf_parser.fdk_rdf_parser import (
+    parse_concept,
+    parse_concept_as_dict,
+)
+from fdk_rdf_parser.parse_functions import _parse_concept
 
 
 def test_parse_concepts(mock_reference_data_client: Mock) -> None:
@@ -505,7 +510,7 @@ def test_parse_concept_handles_wrong_collection_type(
         "https://concepts.staging.fellesdatakatalog.digdir.no/concepts/55a38009-e114-301f-aa7c-8b5f09529f0f"
     )
 
-    result = parse_concept(graph, record_uri, concept_uri)
+    result = _parse_concept(graph, record_uri, concept_uri)
 
     assert result == expected
 
@@ -576,6 +581,131 @@ def test_parse_concept_with_old_skosno(
         "https://concepts.staging.fellesdatakatalog.digdir.no/concepts/35367473-a4c0-3f55-bbdb-fcdbffb6f67a"
     )
 
-    result = parse_concept(graph, record_uri, concept_uri)
+    result = _parse_concept(graph, record_uri, concept_uri)
+
+    assert result == expected
+
+
+def test_parse_concept(mock_reference_data_client: Mock) -> None:
+    src = """
+    @prefix br:    <https://raw.githubusercontent.com/Informasjonsforvaltning/organization-catalog/main/src/main/resources/ontology/organization-catalog.owl#> .
+    @prefix orgtype:   <https://raw.githubusercontent.com/Informasjonsforvaltning/organization-catalog/main/src/main/resources/ontology/org-type.ttl#> .
+    @prefix rov:   <http://www.w3.org/ns/regorg#> .
+    @prefix skosxl: <http://www.w3.org/2008/05/skos-xl#> .
+    @prefix skosno: <https://data.norge.no/vocabulary/skosno#> .
+    @prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+    @prefix skos:  <http://www.w3.org/2004/02/skos/core#> .
+    @prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
+    @prefix vcard: <http://www.w3.org/2006/vcard/ns#> .
+    @prefix dct:   <http://purl.org/dc/terms/> .
+    @prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+    @prefix dcat:  <http://www.w3.org/ns/dcat#> .
+    @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+    @prefix xkos:  <http://rdf-vocabulary.ddialliance.org/xkos#> .
+    @prefix euvoc:  <http://publications.europa.eu/ontology/euvoc#> .
+    @prefix uneskos: <http://purl.org/umu/uneskos#> .
+
+    <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028>
+            a               skos:Collection ;
+            rdfs:label      "Concept collection belonging to 910258028" ;
+            dct:identifier  "https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028" ;
+            dct:publisher   <https://data.brreg.no/enhetsregisteret/api/enheter/910258028> ;
+            skos:member     <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0> .
+
+    <https://concepts.staging.fellesdatakatalog.digdir.no/concepts/55a38009-e114-301f-aa7c-8b5f09529f0f>
+            a                  dcat:CatalogRecord ;
+            dct:identifier     "55a38009-e114-301f-aa7c-8b5f09529f0f" ;
+            dct:isPartOf       <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662> ;
+            dct:issued         "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            dct:modified       "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            foaf:primaryTopic  <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0> .
+
+    <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0>
+            a                   skos:Concept ;
+            dct:created        "2023-02-17"^^xsd:date ;
+            dct:identifier      "1843b048-f9af-4665-8e53-3c001d0166c0" ;
+            dct:modified        "2019-12-16"^^xsd:date ;
+            euvoc:status        <http://publications.europa.eu/resource/authority/concept-status/CURRENT> ;
+            dct:publisher       <https://data.brreg.no/enhetsregisteret/api/enheter/910258028> ;
+            skos:exactMatch     <http://begrepskatalogen/begrep/20b2e2ab-9fe1-11e5-a9f8-e4115b280940> ;
+            skos:closeMatch     <http://begrepskatalogen/begrep/20b2e2aa-9fe1-11e5-a9f8-e4115b280940> ;
+            uneskos:memberOf    <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028> ;
+            skosxl:altLabel     [ a                   skosxl:Label ;
+                                skosxl:literalForm  "w"@nb
+                                ] ;
+            skosxl:hiddenLabel  [ ] ;
+            skosxl:prefLabel    [ a                   skosxl:Label ;
+                                skosxl:literalForm  "to"@nb
+                                ] ;
+            skosno:definisjon   [ a           skosno:Definisjon ;
+                                rdfs:label  "dfgfg"@nb ;
+                                dct:audience skosno:blabla ;
+                                skosno:forholdTilKilde  skosno:blabla
+                                ] .
+
+
+    <http://publications.europa.eu/resource/authority/concept-status/CURRENT>
+            skos:prefLabel  "current"@en , "gjeldende"@no , "gjeldande"@nn , "gjeldende"@nb .
+
+
+    <https://data.brreg.no/enhetsregisteret/api/enheter/987654321>
+            a                      rov:RegisteredOrganization ;
+            dct:identifier         "987654321" ;
+            rov:legalName          "Testdirektoratet" ;
+            foaf:name              "Testdirektoratet"@nb ;
+            rov:orgType            orgtype:STAT ;
+            br:orgPath             "/STAT/987654321" .
+
+    <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662>
+            a                  dcat:CatalogRecord ;
+            dct:identifier     "5e08611a-4e94-3d8f-9d9f-d3a292ec1662" ;
+            dct:issued         "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            dct:modified       "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            foaf:primaryTopic  <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028> ."""
+
+    expected = Concept(
+        id="55a38009-e114-301f-aa7c-8b5f09529f0f",
+        uri="https://concepts.staging.fellesdatakatalog.digdir.no/concepts/55a38009-e114-301f-aa7c-8b5f09529f0f",
+        created="2023-02-17",
+        identifier="https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0",
+        harvest=HarvestMetaData(
+            firstHarvested="2021-02-17T09:39:13Z", changed=["2021-02-17T09:39:13Z"]
+        ),
+        collection=Collection(
+            id="5e08611a-4e94-3d8f-9d9f-d3a292ec1662",
+            uri="https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028",
+            label={"nb": "Concept collection belonging to 910258028"},
+            publisher=Publisher(
+                uri="https://data.brreg.no/enhetsregisteret/api/enheter/910258028",
+            ),
+        ),
+        publisher=Publisher(
+            uri="https://data.brreg.no/enhetsregisteret/api/enheter/910258028",
+        ),
+        exactMatch={
+            "http://begrepskatalogen/begrep/20b2e2ab-9fe1-11e5-a9f8-e4115b280940"
+        },
+        closeMatch={
+            "http://begrepskatalogen/begrep/20b2e2aa-9fe1-11e5-a9f8-e4115b280940"
+        },
+        memberOf={
+            "https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028"
+        },
+        prefLabel={"nb": "to"},
+        status={
+            "en": "current",
+            "no": "gjeldende",
+            "nn": "gjeldande",
+            "nb": "gjeldende",
+        },
+        altLabel=[{"nb": "w"}],
+        definition=Definition(text={"nb": "dfgfg"}),
+        type="concept",
+    )
+
+    result = parse_concept(src)
 
     assert result == expected
+
+    result_json_serializable = parse_concept_as_dict(src)
+    assert result_json_serializable == asdict(expected)
diff --git a/tests/test_data_service.py b/tests/test_data_service.py
index 5ebe0a2..ed8fff3 100644
--- a/tests/test_data_service.py
+++ b/tests/test_data_service.py
@@ -1,6 +1,6 @@
+from dataclasses import asdict
 from unittest.mock import Mock
 
-from fdk_rdf_parser import parse_data_services
 from fdk_rdf_parser.classes import (
     Catalog,
     DataService,
@@ -11,6 +11,11 @@ from fdk_rdf_parser.classes import (
     Publisher,
     SkosConcept,
 )
+from fdk_rdf_parser.fdk_rdf_parser import (
+    parse_data_service,
+    parse_data_services,
+    parse_dataservice_as_dict,
+)
 
 
 def test_parse_multiple_data_services(
@@ -152,3 +157,68 @@ def test_parse_multiple_data_services(
 
     with open("tests/test_data/dataservice0.ttl", "r") as src:
         assert parse_data_services(src.read()) == expected
+
+
+def test_parse_single_data_service(
+    mock_reference_data_client: Mock,
+) -> None:
+    graph = """
+        @prefix br:    <https://raw.githubusercontent.com/Informasjonsforvaltning/organization-catalog/main/src/main/resources/ontology/organization-catalog.owl#> .
+        @prefix orgtype:   <https://raw.githubusercontent.com/Informasjonsforvaltning/organization-catalog/main/src/main/resources/ontology/org-type.ttl#> .
+        @prefix dct:   <http://purl.org/dc/terms/> .
+        @prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+        @prefix vcard: <http://www.w3.org/2006/vcard/ns#> .
+        @prefix dcat:  <http://www.w3.org/ns/dcat#> .
+        @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+        @prefix rov:   <http://www.w3.org/ns/regorg#> .
+
+        <https://testutgiver.no/dataservices/0>
+                a                         dcat:DataService ;
+                dct:publisher             <https://organization-catalog.fellesdatakatalog.brreg.no/organizations/987654321> ;
+                dcat:endpointDescription  <https://raw.githubusercontent.com/Informasjonsforvaltning/fdk-api-harvester/master/src/main/resources/specification/fdk-api-harvester.yaml> .
+
+        <https://testutgiver.no/catalogs/987654321>
+                a              dcat:Catalog ;
+                dct:publisher  <https://organization-catalog.fellesdatakatalog.brreg.no/organizations/987654321> ;
+                dct:title      "Dataservicekatalog2 for Digitaliseringsdirektoratet"@nb ;
+                dcat:service   <https://testutgiver.no/dataservices/0> .
+
+        <https://testdirektoratet.no/catalogs/321>
+                a                  dcat:CatalogRecord ;
+                dct:identifier     "d6199127-8835-33e1-9108-233cd81e92f9" ;
+                dct:issued         "2020-06-22T13:39:27.334Z"^^xsd:dateTime ;
+                dct:modified       "2020-06-22T13:39:27.334Z"^^xsd:dateTime ;
+                foaf:primaryTopic  <https://testutgiver.no/catalogs/987654321> .
+
+        <https://testdirektoratet.no/dataservices/000>
+                a                  dcat:CatalogRecord ;
+                dct:identifier     "d1d698ef-267a-3d57-949f-b2bc44657f3e" ;
+                dct:isPartOf       <https://testdirektoratet.no/catalogs/321> ;
+                dct:issued         "2020-06-22T13:39:27.353Z"^^xsd:dateTime ;
+                dct:modified       "2020-06-22T13:39:27.353Z"^^xsd:dateTime ;
+                foaf:primaryTopic  <https://testutgiver.no/dataservices/0> .
+    """
+    expected = DataService(
+        publisher=Publisher(
+            uri="https://organization-catalog.fellesdatakatalog.brreg.no/organizations/987654321",
+        ),
+        uri="https://testutgiver.no/dataservices/0",
+        id="d1d698ef-267a-3d57-949f-b2bc44657f3e",
+        harvest=HarvestMetaData(
+            firstHarvested="2020-06-22T13:39:27Z", changed=["2020-06-22T13:39:27Z"]
+        ),
+        endpointDescription={
+            "https://raw.githubusercontent.com/Informasjonsforvaltning/fdk-api-harvester/master/src/main/resources/specification/fdk-api-harvester.yaml"
+        },
+        catalog=Catalog(
+            id="d6199127-8835-33e1-9108-233cd81e92f9",
+            uri="https://testutgiver.no/catalogs/987654321",
+            title={"nb": "Dataservicekatalog2 for Digitaliseringsdirektoratet"},
+            publisher=Publisher(
+                uri="https://organization-catalog.fellesdatakatalog.brreg.no/organizations/987654321",
+            ),
+        ),
+    )
+
+    assert parse_data_service(graph) == expected
+    assert parse_dataservice_as_dict(graph) == asdict(expected)
diff --git a/tests/test_dataset.py b/tests/test_dataset.py
index 797f517..fc3a911 100644
--- a/tests/test_dataset.py
+++ b/tests/test_dataset.py
@@ -1,3 +1,4 @@
+from dataclasses import asdict
 from typing import Dict
 from unittest.mock import Mock
 
@@ -24,7 +25,11 @@ from fdk_rdf_parser.classes import (
     SkosConcept,
     Subject,
 )
-from fdk_rdf_parser.parse_functions import parse_dataset
+from fdk_rdf_parser.fdk_rdf_parser import (
+    parse_dataset,
+    parse_dataset_as_dict,
+)
+from fdk_rdf_parser.parse_functions import _parse_dataset
 
 
 def test_parse_multiple_datasets(mock_reference_data_client: Mock) -> None:
@@ -352,7 +357,7 @@ def test_parse_dataset() -> None:
         "https://datasets.fellesdatakatalog.digdir.no/datasets/a1c680ca"
     )
 
-    assert parse_dataset(datasets_graph, record_uri, dataset_uri) == expected
+    assert _parse_dataset(datasets_graph, record_uri, dataset_uri) == expected
 
 
 def test_dataset_has_quality_annotations() -> None:
@@ -418,7 +423,7 @@ def test_dataset_has_quality_annotations() -> None:
     graph = Graph().parse(data=src, format="turtle")
     subject = URIRef("https://testdirektoratet.no/model/dataset/quality")
 
-    assert parse_dataset(graph, URIRef("record"), subject) == expected
+    assert _parse_dataset(graph, URIRef("record"), subject) == expected
 
 
 def test_legal_basis_fields() -> None:
@@ -475,7 +480,7 @@ def test_legal_basis_fields() -> None:
     graph = Graph().parse(data=src, format="turtle")
     subject = URIRef("https://testdirektoratet.no/model/dataset/0")
 
-    assert parse_dataset(graph, URIRef("record"), subject) == expected
+    assert _parse_dataset(graph, URIRef("record"), subject) == expected
 
 
 def test_informationmodel_and_conformsto() -> None:
@@ -519,7 +524,7 @@ def test_informationmodel_and_conformsto() -> None:
     graph = Graph().parse(data=src, format="turtle")
     subject = URIRef("https://testdirektoratet.no/model/dataset/0")
 
-    assert parse_dataset(graph, URIRef("record"), subject) == expected
+    assert _parse_dataset(graph, URIRef("record"), subject) == expected
 
 
 def test_distribution_and_sample() -> None:
@@ -611,7 +616,7 @@ def test_distribution_and_sample() -> None:
     graph = Graph().parse(data=src, format="turtle")
     subject = URIRef("https://testdirektoratet.no/model/dataset/0")
 
-    assert parse_dataset(graph, URIRef("record"), subject) == expected
+    assert _parse_dataset(graph, URIRef("record"), subject) == expected
 
 
 def test_qualified_attributions() -> None:
@@ -913,22 +918,22 @@ def test_extra_media_types(mock_reference_data_client: Mock) -> None:
             dcat:compressFormat  "text/unknown" ;
             dcat:packageFormat   [ ] .
 
-<https://www.iana.org/assignments/media-types/application/json>
-        a               dct:MediaType;
-        dct:identifier  "application/json";
-        dct:title       "json" .
-
-<https://www.iana.org/assignments/media-types/text/html>
-        a               dct:MediaType;
-        dct:identifier  "text/html";
-        dct:title       "html" .
-
-<http://publications.europa.eu/resource/authority/file-type/7Z>
-        a                   euvoc:FileType;
-        euvoc:xlNotation    [
-            euvoc:xlCodification  "application/x-7z-compressed";
-            dct:type        <http://publications.europa.eu/resource/authority/notation-type/IANA_MT> ];
-        dc:identifier       "7Z" .
+        <https://www.iana.org/assignments/media-types/application/json>
+                a               dct:MediaType;
+                dct:identifier  "application/json";
+                dct:title       "json" .
+
+        <https://www.iana.org/assignments/media-types/text/html>
+                a               dct:MediaType;
+                dct:identifier  "text/html";
+                dct:title       "html" .
+
+        <http://publications.europa.eu/resource/authority/file-type/7Z>
+                a                   euvoc:FileType;
+                euvoc:xlNotation    [
+                    euvoc:xlCodification  "application/x-7z-compressed";
+                    dct:type        <http://publications.europa.eu/resource/authority/notation-type/IANA_MT> ];
+                dc:identifier       "7Z" .
     """
     expected = {
         "https://testdirektoratet.no/model/dataset/0": Dataset(
@@ -988,3 +993,86 @@ def test_extra_media_types(mock_reference_data_client: Mock) -> None:
         ),
     }
     assert parse_datasets(src) == expected
+
+
+def test_parse_single_dataset() -> None:
+    src = """
+        @prefix dct: <http://purl.org/dc/terms/> .
+        @prefix dc:   <http://purl.org/dc/elements/1.1/> .
+        @prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+        @prefix dcat:  <http://www.w3.org/ns/dcat#> .
+        @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+        @prefix skos: <http://www.w3.org/2004/02/skos/core#> .
+
+        <https://testdirektoratet.no/model/dataset/0>
+                a                         dcat:Dataset ;
+                dct:accrualPeriodicity
+                    <http://publications.europa.eu/resource/authority/frequency/ANNUAL> ;
+                dct:identifier
+                    "adb4cf00-31c8-460c-9563-55f204cf8221" ;
+                dct:publisher
+                    <http://data.brreg.no/enhetsregisteret/enhet/987654321> ;
+                dct:provenance
+                    <http://data.brreg.no/datakatalog/provinens/tredjepart> ;
+                dcat:endpointDescription
+                    <https://testdirektoratet.no/openapi/dataset/0.yaml> ;
+                dct:spatial
+                    <https://data.geonorge.no/administrativeEnheter/fylke/id/34> ;
+                dct:subject
+                    <https://testdirektoratet.no/model/concept/0> ,
+                    <https://testdirektoratet.no/model/concept/1> ;
+                foaf:page
+                    <https://testdirektoratet.no> .
+
+        <http://publications.europa.eu/resource/authority/frequency/ANNUAL>
+            dc:identifier  "ANNUAL";
+            skos:prefLabel  "annual"@en , "årleg"@nn , "årlig"@nb , "årlig"@no .
+
+        <https://data.geonorge.no/administrativeEnheter/fylke/id/34>
+                a               dct:Location;
+                dct:identifier  "34";
+                dct:title       "Innlandet" .
+
+        <https://datasets.fellesdatakatalog.digdir.no/datasets/a1c680ca>
+                a                  dcat:CatalogRecord ;
+                dct:identifier     "a1c680ca" ;
+                dct:issued         "2020-03-12T11:52:16.122Z"^^xsd:dateTime ;
+                dct:modified       "2020-03-12T11:52:16.122Z"^^xsd:dateTime ;
+                dct:modified       "2020-03-13"^^xsd:date ;
+                foaf:primaryTopic  <https://testdirektoratet.no/model/dataset/0> ."""
+
+    expected = Dataset(
+        id="a1c680ca",
+        harvest=HarvestMetaData(
+            firstHarvested="2020-03-12T11:52:16Z",
+            changed=["2020-03-12T11:52:16Z", "2020-03-13"],
+        ),
+        identifier={"adb4cf00-31c8-460c-9563-55f204cf8221"},
+        uri="https://testdirektoratet.no/model/dataset/0",
+        publisher=Publisher(
+            uri="http://data.brreg.no/enhetsregisteret/enhet/987654321"
+        ),
+        page={"https://testdirektoratet.no"},
+        subject=[
+            Subject(uri="https://testdirektoratet.no/model/concept/0"),
+            Subject(uri="https://testdirektoratet.no/model/concept/1"),
+        ],
+        accrualPeriodicity=ReferenceDataCode(
+            uri="http://publications.europa.eu/resource/authority/frequency/ANNUAL",
+            code="ANNUAL",
+            prefLabel={"en": "annual", "nn": "årleg", "nb": "årlig", "no": "årlig"},
+        ),
+        provenance=ReferenceDataCode(
+            uri="http://data.brreg.no/datakatalog/provinens/tredjepart"
+        ),
+        spatial=[
+            ReferenceDataCode(
+                uri="https://data.geonorge.no/administrativeEnheter/fylke/id/34",
+                code="34",
+                prefLabel={"nb": "Innlandet"},
+            )
+        ],
+    )
+
+    assert parse_dataset(src) == expected
+    assert parse_dataset_as_dict(src) == asdict(expected)
diff --git a/tests/test_dataset_series.py b/tests/test_dataset_series.py
index ca131a6..59eebb7 100644
--- a/tests/test_dataset_series.py
+++ b/tests/test_dataset_series.py
@@ -1,3 +1,4 @@
+from dataclasses import asdict
 from unittest.mock import Mock
 
 from rdflib import (
@@ -15,7 +16,11 @@ from fdk_rdf_parser.classes import (
     Publisher,
     ReferenceDataCode,
 )
-from fdk_rdf_parser.parse_functions.dataset import parse_dataset_series
+from fdk_rdf_parser.fdk_rdf_parser import (
+    parse_dataset,
+    parse_dataset_as_dict,
+)
+from fdk_rdf_parser.parse_functions.dataset import _parse_dataset_series
 
 
 def test_parse_dataset_series(mock_reference_data_client: Mock) -> None:
@@ -237,7 +242,7 @@ def test_parse_dataset_series_abort_on_circular_graph(
     )
 
     graph = Graph().parse(data=src, format="turtle")
-    assert parse_dataset_series(graph, URIRef("http://example.org/budget")) == expected
+    assert _parse_dataset_series(graph, URIRef("http://example.org/budget")) == expected
 
 
 def test_parse_dataset_series_broken_linked_list(
@@ -286,4 +291,55 @@ def test_parse_dataset_series_broken_linked_list(
     )
 
     graph = Graph().parse(data=src, format="turtle")
-    assert parse_dataset_series(graph, URIRef("http://example.org/budget")) == expected
+    assert _parse_dataset_series(graph, URIRef("http://example.org/budget")) == expected
+
+
+def test_parse_single_dataset_series(
+    mock_reference_data_client: Mock,
+) -> None:
+    graph = """
+        @prefix ex: <http://example.org/> .
+        @prefix dcat: <http://www.w3.org/ns/dcat#> .
+        @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+        @prefix dct: <http://purl.org/dc/terms/> .
+        @prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+
+
+        <http://localhost:5000/datasets/f1e8443d-910f-3838-87e3-2b5e7ee307a6>
+            a                  dcat:CatalogRecord ;
+            dct:identifier     "f1e8443d-910f-3838-87e3-2b5e7ee307a6" ;
+            dct:issued         "2020-03-12T11:52:16.122Z"^^xsd:dateTime ;
+            dct:modified       "2020-03-12T11:52:16.122Z"^^xsd:dateTime ;
+            foaf:primaryTopic  ex:budget .
+
+
+        ex:budget a dcat:Dataset , dcat:DatasetSeries ;
+            dcat:last ex:budget-2020 ;
+            .
+
+        ex:budget-2019 a dcat:Dataset ;
+            dcat:inSeries ex:budget ;
+            .
+
+        ex:budget-2020 a dcat:Dataset ;
+            dcat:inSeries ex:budget ;
+            dcat:prev ex:budget-2019 ;
+            .
+
+    """
+    expected = DatasetSeries(
+        specializedType="datasetSeries",
+        uri="http://example.org/budget",
+        id="f1e8443d-910f-3838-87e3-2b5e7ee307a6",
+        harvest=HarvestMetaData(
+            firstHarvested="2020-03-12T11:52:16Z", changed=["2020-03-12T11:52:16Z"]
+        ),
+        datasetsInSeries=[
+            "http://example.org/budget-2020",
+            "http://example.org/budget-2019",
+        ],
+        last="http://example.org/budget-2020",
+    )
+
+    assert parse_dataset(graph) == expected
+    assert parse_dataset_as_dict(graph) == asdict(expected)
diff --git a/tests/test_event.py b/tests/test_event.py
index 56ef662..11ac988 100644
--- a/tests/test_event.py
+++ b/tests/test_event.py
@@ -1,6 +1,13 @@
+from dataclasses import asdict
 from unittest.mock import Mock
 
-from fdk_rdf_parser import parse_events
+import pytest
+
+from fdk_rdf_parser import (
+    parse_event,
+    parse_event_as_dict,
+    parse_events,
+)
 from fdk_rdf_parser.classes import (
     BusinessEvent,
     Catalog,
@@ -10,6 +17,7 @@ from fdk_rdf_parser.classes import (
     Publisher,
     SkosConcept,
 )
+from fdk_rdf_parser.classes.exceptions import MissingResourceError
 
 
 def test_parse_events(
@@ -277,3 +285,86 @@ def test_parse_cv_event(
     }
 
     assert parse_events(src) == expected
+
+
+def test_parse_single_cv_event(
+    mock_reference_data_client: Mock,
+) -> None:
+    src = """
+        @prefix cpsvno:    <https://data.norge.no/vocabulary/cpsvno#> .
+        @prefix dct: <http://purl.org/dc/terms/> .
+        @prefix cv: <http://data.europa.eu/m8g/> .
+        @prefix dcat:  <http://www.w3.org/ns/dcat#> .
+        @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+        @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+        <https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exHendelse.ttl>
+            a                   cv:Event ;
+            dct:description     "Teksten blir vist på nynorsk."@nn , "The text is displayed in English."@en , "Det er fattet vedtak om å gi skjenkebevilling til «Den beste restauranten AS», for servering av alkoholholdig drikk i gruppe 1, jf. Alkoholloven § 4-2"@nb ;
+            dct:identifier      "https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exHendelse.ttl"^^xsd:anyURI ;
+            dct:subject         <https://data.norge.no/concepts/ad2ab3f9-17a1-4494-b15e-4ba3967a6424> ;
+            dct:title           "Vedtak om skjenkebevilling"@nb ;
+            dct:type            <https://data.norge.no/vocabulary/event-type#administrative-decision-made> ;
+            dcat:distribution   <https://example.org/example-distribusjon> ;
+            cpsvno:mayInitiate  <https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exTjenesteDummy.ttl> .
+
+        <https://www.staging.fellesdatakatalog.digdir.no/events/4b75e194-4be7-337c-9950-699d2862c490>
+            a                  dcat:CatalogRecord ;
+            dct:identifier     "4b75e194-4be7-337c-9950-699d2862c490" ;
+            dct:issued         "2022-05-13T13:04:04.942Z"^^xsd:dateTime ;
+            dct:modified       "2022-05-13T13:04:04.942Z"^^xsd:dateTime ;
+            foaf:primaryTopic  <https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exHendelse.ttl> ."""
+
+    expected = Event(
+        id="4b75e194-4be7-337c-9950-699d2862c490",
+        uri="https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exHendelse.ttl",
+        identifier="https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exHendelse.ttl",
+        harvest=HarvestMetaData(
+            firstHarvested="2022-05-13T13:04:04Z", changed=["2022-05-13T13:04:04Z"]
+        ),
+        title={"nb": "Vedtak om skjenkebevilling"},
+        description={
+            "nn": "Teksten blir vist på nynorsk.",
+            "en": "The text is displayed in English.",
+            "nb": "Det er fattet vedtak om å gi skjenkebevilling til «Den beste restauranten AS», for servering av alkoholholdig drikk i gruppe 1, jf. Alkoholloven § 4-2",
+        },
+        dctType=[
+            SkosConcept(
+                uri="https://data.norge.no/vocabulary/event-type#administrative-decision-made",
+            )
+        ],
+        associatedBroaderTypes=[
+            "https://data.norge.no/vocabulary/event-type#administrative-decision-made"
+        ],
+        mayInitiate=[
+            "https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exTjenesteDummy.ttl"
+        ],
+        subject=["https://data.norge.no/concepts/ad2ab3f9-17a1-4494-b15e-4ba3967a6424"],
+        distribution=["https://example.org/example-distribusjon"],
+    )
+
+    assert parse_event(src) == expected
+    assert parse_event_as_dict(src) == asdict(expected)
+
+
+def test_parse_missing_event_raises_exception(
+    mock_reference_data_client: Mock,
+) -> None:
+    src = """
+        @prefix dct: <http://purl.org/dc/terms/> .
+        @prefix dcat:  <http://www.w3.org/ns/dcat#> .
+        @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+        @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+        <https://www.staging.fellesdatakatalog.digdir.no/events/4b75e194-4be7-337c-9950-699d2862c490>
+            a                  dcat:CatalogRecord ;
+            dct:identifier     "4b75e194-4be7-337c-9950-699d2862c490" ;
+            dct:issued         "2022-05-13T13:04:04.942Z"^^xsd:dateTime ;
+            dct:modified       "2022-05-13T13:04:04.942Z"^^xsd:dateTime ;
+            foaf:primaryTopic  <https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exHendelse.ttl> ."""
+
+    with pytest.raises(MissingResourceError):
+        parse_event(src)
+
+    with pytest.raises(MissingResourceError):
+        parse_event_as_dict(src)
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
new file mode 100644
index 0000000..544fec9
--- /dev/null
+++ b/tests/test_exceptions.py
@@ -0,0 +1,126 @@
+from unittest.mock import Mock
+
+import pytest
+
+from fdk_rdf_parser.classes.exceptions import (
+    MissingResourceError,
+    MultipleResourcesError,
+    ParserError,
+)
+from fdk_rdf_parser.fdk_rdf_parser import (
+    parse_concept,
+    parse_concept_as_dict,
+)
+
+
+def test_parse_concept_missing_resource_raises_exception(
+    mock_reference_data_client: Mock,
+) -> None:
+    src = """
+    @prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+    @prefix skos:  <http://www.w3.org/2004/02/skos/core#> .
+    @prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
+    @prefix dct:   <http://purl.org/dc/terms/> .
+    @prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+    @prefix dcat:  <http://www.w3.org/ns/dcat#> .
+    @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+
+    <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028>
+            a               skos:Collection ;
+            rdfs:label      "Concept collection belonging to 910258028" ;
+            dct:identifier  "https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028" ;
+            dct:publisher   <https://data.brreg.no/enhetsregisteret/api/enheter/910258028> ;
+            skos:member     <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0> .
+
+    <https://concepts.staging.fellesdatakatalog.digdir.no/concepts/55a38009-e114-301f-aa7c-8b5f09529f0f>
+            a                  dcat:CatalogRecord ;
+            dct:identifier     "55a38009-e114-301f-aa7c-8b5f09529f0f" ;
+            dct:isPartOf       <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662> ;
+            dct:issued         "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            dct:modified       "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            foaf:primaryTopic  <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0> .
+
+    <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662>
+            a                  dcat:CatalogRecord ;
+            dct:identifier     "5e08611a-4e94-3d8f-9d9f-d3a292ec1662" ;
+            dct:issued         "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            dct:modified       "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+            foaf:primaryTopic  <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028> ."""
+
+    with pytest.raises(MissingResourceError):
+        parse_concept(src)
+    with pytest.raises(MissingResourceError):
+        parse_concept_as_dict(src)
+
+
+def test_parse_concept_multiple_resources_raises_exception(
+    mock_reference_data_client: Mock,
+) -> None:
+    src = """
+        @prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+        @prefix skos:  <http://www.w3.org/2004/02/skos/core#> .
+        @prefix dct:   <http://purl.org/dc/terms/> .
+        @prefix dcat:  <http://www.w3.org/ns/dcat#> .
+        @prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+        @prefix uneskos: <http://purl.org/umu/uneskos#> .
+
+        <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028>
+                a               skos:Collection ;
+                dct:identifier  "https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028" ;
+                dct:publisher   <https://data.brreg.no/enhetsregisteret/api/enheter/910258028> ;
+                skos:member     <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/3609b02d-72c5-47e0-a6b8-df0a503cf190> ,
+                                <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0> ;
+                .
+
+        <https://concepts.staging.fellesdatakatalog.digdir.no/concepts/55a38009-e114-301f-aa7c-8b5f09529f0f>
+                a                  dcat:CatalogRecord ;
+                dct:identifier     "55a38009-e114-301f-aa7c-8b5f09529f0f" ;
+                dct:isPartOf       <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662> ;
+                foaf:primaryTopic  <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0> ;
+                .
+
+        <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/1843b048-f9af-4665-8e53-3c001d0166c0>
+                a                   skos:Concept ;
+                dct:identifier      "1843b048-f9af-4665-8e53-3c001d0166c0" ;
+                uneskos:memberOf    <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028> ;
+                .
+
+        <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/3609b02d-72c5-47e0-a6b8-df0a503cf190>
+                a                  skos:Concept ;
+                dct:identifier     "3609b02d-72c5-47e0-a6b8-df0a503cf190" ;
+                .
+
+        <https://concepts.staging.fellesdatakatalog.digdir.no/concepts/fc8baf8d-6146-3b69-93c5-52bd41592c4e>
+                a                  dcat:CatalogRecord ;
+                dct:identifier     "fc8baf8d-6146-3b69-93c5-52bd41592c4e" ;
+                dct:isPartOf       <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662> ;
+                foaf:primaryTopic  <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028/3609b02d-72c5-47e0-a6b8-df0a503cf190> ;
+                .
+
+        <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662>
+                a                  dcat:CatalogRecord ;
+                dct:identifier     "5e08611a-4e94-3d8f-9d9f-d3a292ec1662" ;
+                dct:issued         "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+                dct:modified       "2021-02-17T09:39:13.293Z"^^xsd:dateTime ;
+                foaf:primaryTopic  <https://registrering-begrep-api.staging.fellesdatakatalog.digdir.no/910258028> ;
+                .
+    """
+
+    with pytest.raises(MultipleResourcesError):
+        parse_concept(src)
+    with pytest.raises(MultipleResourcesError):
+        parse_concept_as_dict(src)
+
+
+def test_parse_invalid_rdf_raises_parse_error(
+    mock_reference_data_client: Mock,
+) -> None:
+    bad_turtle = """
+        @prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+
+        <https://concepts.staging.fellesdatakatalog.digdir.no/collections/5e08611a-4e94-3d8f-9d9f-d3a292ec1662>
+                a                  dcat:CatalogRecord ;
+    """
+
+    with pytest.raises(ParserError):
+        parse_concept(bad_turtle)
diff --git a/tests/test_info_model.py b/tests/test_info_model.py
index b548bd0..242f584 100644
--- a/tests/test_info_model.py
+++ b/tests/test_info_model.py
@@ -1,3 +1,4 @@
+from dataclasses import asdict
 from unittest.mock import Mock
 
 from rdflib import (
@@ -5,7 +6,11 @@ from rdflib import (
     URIRef,
 )
 
-from fdk_rdf_parser import parse_information_models
+from fdk_rdf_parser import (
+    parse_information_model,
+    parse_information_model_as_dict,
+    parse_information_models,
+)
 from fdk_rdf_parser.classes import (
     Catalog,
     DCATContactPoint,
@@ -23,7 +28,7 @@ from fdk_rdf_parser.classes.model_element import (
     ModelElement,
 )
 from fdk_rdf_parser.classes.model_property import ModelProperty
-from fdk_rdf_parser.parse_functions import parse_information_model
+from fdk_rdf_parser.parse_functions import _parse_information_model
 from fdk_rdf_parser.parse_functions.info_model import (
     add_elements_to_model,
     add_properties_to_model,
@@ -554,7 +559,7 @@ def test_handles_missing_elements() -> None:
         "https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#Diversemodell"
     )
 
-    assert parse_information_model(graph, URIRef("record"), subject) == expected
+    assert _parse_information_model(graph, URIRef("record"), subject) == expected
 
 
 def test_elements_are_not_added_twice() -> None:
@@ -864,7 +869,7 @@ def test_subjects_from_elements_and_properties_added_to_contains_subjects() -> N
         "https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#Diversemodell"
     )
 
-    result = parse_information_model(graph, URIRef("record"), subject)
+    result = _parse_information_model(graph, URIRef("record"), subject)
 
     assert result == expected
 
@@ -918,5 +923,252 @@ def test_code_element_subjects_are_added_to_containssubjects() -> None:
     subject = URIRef(
         "https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#PersonOgEnhet"
     )
-    result = parse_information_model(graph, URIRef("record"), subject)
+    result = _parse_information_model(graph, URIRef("record"), subject)
     assert result.containsSubjects is not None and len(result.containsSubjects) == 2
+
+
+def test_parse_single_info_model(
+    mock_reference_data_client: Mock,
+) -> None:
+    src = """@prefix adms:  <http://www.w3.org/ns/adms#> .
+@prefix at:    <http://publications.europa.eu/ontology/authority/> .
+@prefix owl:   <http://www.w3.org/2002/07/owl#> .
+@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
+@prefix skos:  <http://www.w3.org/2004/02/skos/core#> .
+@prefix vcard: <http://www.w3.org/2006/vcard/ns#> .
+@prefix ex-abstrakt: <http://example.com/test_abstraksjon#> .
+@prefix xkos:  <https://rdf-vocabulary.ddialliance.org/xkos/> .
+@prefix dct:   <http://purl.org/dc/terms/> .
+@prefix dc:   <http://purl.org/dc/elements/1.1/> .
+@prefix modelldcatno: <https://data.norge.no/vocabulary/modelldcatno#> .
+@prefix digdir: <https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#> .
+@prefix dcat:  <http://www.w3.org/ns/dcat#> .
+@prefix foaf:  <http://xmlns.com/foaf/0.1/> .
+@prefix prof:  <https://www.w3.org/ns/dx/prof/> .
+@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
+
+digdir:Tidsintervall  a  dct:PeriodOfTime , owl:NamedIndividual ;
+        dcat:startDate  "2016-02-11T00:00:00+01:00"^^xsd:dateTime .
+
+digdir:KontaktOss  a        owl:NamedIndividual , vcard:Kind ;
+        vcard:fn            "Avdeling for digitalisering" ;
+        vcard:hasEmail      <mailto:informasjonsforvaltning@digdir.no> ;
+        vcard:hasTelephone  <tel:12345678> .
+
+digdir:Utgiver  a       foaf:Agent , owl:NamedIndividual ;
+        dct:identifier  "991825827" ;
+        dct:type        "Organisasjonsledd"@nb ;
+        foaf:name       "Digitaliseringsdirektoratet"@nb .
+
+digdir:Diversemodell  a    modelldcatno:InformationModel , owl:NamedIndividual ;
+        dct:conformsTo          <https://statswiki.unece.org/display/gsim/Generic+Statistical+Information+Model> ;
+        dct:description    "Modell med diverse i. Inneholder modellelementer som AltMuligModell skal peke til."@nb ;
+        dct:hasFormat      <https://github.com/statisticsnorway/gsim-raml-schema/blob/master/ssb_gsim_ldm.png> , <https://format.for/mat> ;
+        dct:identifier     "https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#Diversemodell" ;
+        dct:isPartOf       digdir:AltMuligModell ;
+        dct:isReplacedBy   digdir:AdresseModell ;
+        dct:issued         "2016-09-28T00:00:00+01:00"^^xsd:dateTime ;
+        dct:language       <http://publications.europa.eu/resource/authority/language/NOB> ;
+        dct:license        <http://publications.europa.eu/resource/authority/licence/CC_BY_4_0> , [ ] ;
+        dct:modified       "2017-09-28T00:00:00+01:00"^^xsd:dateTime ;
+        dct:publisher      digdir:Utgiver ;
+        dct:spatial        <https://data.geonorge.no/administrativeEnheter/nasjon/id/173163> ;
+        dct:temporal       digdir:Tidsintervall ;
+        dct:title          "Diversemodell"@nb ;
+        dct:type           "Fellesmodell"@nb ;
+        owl:versionInfo    "1.0" ;
+        adms:status        <http://purl.org/adms/status/Completed> ;
+        adms:versionNotes  "Lagt til objekttypen Timeline"@nb ;
+        dcat:contactPoint  digdir:KontaktOss ;
+        dcat:keyword       "Adresse"@nb ;
+        dcat:theme         <https://psi.norge.no/los/tema/skole-og-utdanning> ;
+        foaf:homepage      <https://www.difi.no/fagomrader-og-tjenester/digitalisering-og-samordning/nasjonal-arkitektur/informasjonsforvaltning/adresse-felles-informasjonsmodell> ;
+        prof:isProfileOf   <https://statswiki.unece.org/display/gsim/Generic+Statistical+Information+Model> ;
+        modelldcatno:informationModelIdentifier
+                "https://www.digdir.no/diversemodell" .
+
+<http://publications.europa.eu/resource/authority/language/NOB>
+        a           skos:Concept;
+        at:authority-code      "NOB";
+        skos:prefLabel     "Norsk Bokmål"@nb , "Norsk Bokmål"@nn , "Norsk Bokmål"@no , "Norwegian Bokmål"@en .
+
+<http://publications.europa.eu/resource/authority/licence/CC_BY_4_0>
+        a           skos:Concept;
+        dc:identifier      "CC BY 4.0";
+        skos:prefLabel     "Creative Commons Navngivelse 4.0 Internasjonal"@no , "Creative Commons Attribution 4.0 International"@en .
+
+<https://data.geonorge.no/administrativeEnheter/nasjon/id/173163>
+        a               dct:Location;
+        dct:identifier  "173163";
+        dct:title       "Norge" .
+
+<https://github.com/statisticsnorway/gsim-raml-schema/blob/master/ssb_gsim_ldm.png>
+        a           foaf:Document ;
+        dct:format  <http://publications.europa.eu/resource/authority/file-type/PNG> ;
+        dct:language <http://pubs.europa.eu/resource/authority/language/NOR> ;
+        rdfs:seeAlso <https://github.com/statisticsnorway/gsim-raml-schema/blob/master/ssb_gsim_ldm.png> ;
+        dct:title   "Image of the logical data model (LDM)"@en .
+
+<https://format.for/mat>
+        a           foaf:Document ;
+        dct:format  <http://publications.europa.eu/resource/authority/file-type/JPG> ;
+        dct:title   "Image of test"@en .
+
+<https://statswiki.unece.org/display/gsim/Generic+Statistical+Information+Model>
+        a                dct:Standard ;
+        rdfs:seeAlso     <https://statswiki.unece.org/display/gsim/GSIM+resources+repository> ;
+        dct:title        "Generic Statistical Information Model"@en ;
+        owl:versionInfo  "??" .
+
+<https://informationmodels.staging.fellesdatakatalog.digdir.no/informationmodels/77e07f69-5fb4-30c7-afca-bffe179dc3b3>
+        a                  dcat:CatalogRecord ;
+        dct:identifier     "77e07f69-5fb4-30c7-afca-bffe179dc3b3" ;
+        dct:isPartOf       <https://informationmodels.staging.fellesdatakatalog.digdir.no/catalogs/03953a9d-5b6b-34ec-b41c-dcdcb21874d9> ;
+        dct:issued         "2020-10-13T11:35:47.394Z"^^xsd:dateTime ;
+        dct:modified       "2020-10-13T11:35:47.394Z"^^xsd:dateTime ;
+        foaf:primaryTopic  digdir:Diversemodell .
+
+<https://informationmodels.staging.fellesdatakatalog.digdir.no/catalogs/03953a9d-5b6b-34ec-b41c-dcdcb21874d9>
+        a                  dcat:CatalogRecord ;
+        dct:identifier     "03953a9d-5b6b-34ec-b41c-dcdcb21874d9" ;
+        dct:issued         "2020-10-06T10:29:22.705Z"^^xsd:dateTime ;
+        dct:modified       "2020-10-13T11:35:47.394Z"^^xsd:dateTime ;
+        foaf:primaryTopic  digdir:Katalog .
+
+<https://psi.norge.no/los/tema/skole-og-utdanning>
+        a                  skos:Concept ;
+        skos:prefLabel     "Skule og utdanning"@nn , "Skole og utdanning"@nb , "Schools and education"@en ;
+        <https://fellesdatakatalog.digdir.no/ontology/internal/themePath>
+                "skole-og-utdanning" ."""
+
+    expected = InformationModel(
+        identifier={
+            "https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#Diversemodell"
+        },
+        publisher=Publisher(
+            uri="https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#Utgiver",
+            id="991825827",
+            name="Digitaliseringsdirektoratet",
+            prefLabel={"nb": "Digitaliseringsdirektoratet"},
+        ),
+        title={"nb": "Diversemodell"},
+        description={
+            "nb": "Modell med diverse i. Inneholder modellelementer som AltMuligModell skal peke til."
+        },
+        descriptionFormatted={
+            "nb": "Modell med diverse i. Inneholder modellelementer som AltMuligModell skal peke til."
+        },
+        uri="https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#Diversemodell",
+        keyword=[{"nb": "Adresse"}],
+        contactPoint=[
+            DCATContactPoint(
+                uri="https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#KontaktOss",
+                fullname="Avdeling for digitalisering",
+                email="informasjonsforvaltning@digdir.no",
+                hasTelephone="12345678",
+            )
+        ],
+        dctType="Fellesmodell",
+        issued="2016-09-28T00:00:00+01:00",
+        modified="2017-09-28T00:00:00+01:00",
+        language=[
+            ReferenceDataCode(
+                uri="http://publications.europa.eu/resource/authority/language/NOB",
+                code="NOB",
+                prefLabel={
+                    "en": "Norwegian Bokmål",
+                    "nb": "Norsk Bokmål",
+                    "nn": "Norsk Bokmål",
+                    "no": "Norsk Bokmål",
+                },
+            )
+        ],
+        id="77e07f69-5fb4-30c7-afca-bffe179dc3b3",
+        harvest=HarvestMetaData(
+            firstHarvested="2020-10-13T11:35:47Z", changed=["2020-10-13T11:35:47Z"]
+        ),
+        conformsTo=[
+            DctStandard(
+                uri="https://statswiki.unece.org/display/gsim/Generic+Statistical+Information+Model",
+                title={"en": "Generic Statistical Information Model"},
+                seeAlso=[
+                    "https://statswiki.unece.org/display/gsim/GSIM+resources+repository"
+                ],
+                versionInfo="??",
+            )
+        ],
+        license=[
+            ReferenceDataCode(
+                uri="http://publications.europa.eu/resource/authority/licence/CC_BY_4_0",
+                code="CC BY 4.0",
+                prefLabel={
+                    "no": "Creative Commons Navngivelse 4.0 Internasjonal",
+                    "en": "Creative Commons Attribution 4.0 International",
+                },
+            )
+        ],
+        informationModelIdentifier="https://www.digdir.no/diversemodell",
+        spatial=[
+            ReferenceDataCode(
+                uri="https://data.geonorge.no/administrativeEnheter/nasjon/id/173163",
+                code="173163",
+                prefLabel={"nb": "Norge"},
+            )
+        ],
+        isPartOf="https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#AltMuligModell",
+        isProfileOf=[
+            DctStandard(
+                uri="https://statswiki.unece.org/display/gsim/Generic+Statistical+Information+Model",
+                title={"en": "Generic Statistical Information Model"},
+                seeAlso=[
+                    "https://statswiki.unece.org/display/gsim/GSIM+resources+repository"
+                ],
+                versionInfo="??",
+            )
+        ],
+        isReplacedBy="https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#AdresseModell",
+        temporal=[
+            Temporal(
+                uri="https://raw.githubusercontent.com/Informasjonsforvaltning/model-publisher/master/src/model/model-catalog.ttl#Tidsintervall",
+                startDate="2016-02-11T00:00:00+01:00",
+            )
+        ],
+        hasFormat=[
+            Format(
+                uri="https://format.for/mat",
+                title={"en": "Image of test"},
+                format="http://publications.europa.eu/resource/authority/file-type/JPG",
+            ),
+            Format(
+                uri="https://github.com/statisticsnorway/gsim-raml-schema/blob/master/ssb_gsim_ldm.png",
+                title={"en": "Image of the logical data model (LDM)"},
+                format="http://publications.europa.eu/resource/authority/file-type/PNG",
+                seeAlso="https://github.com/statisticsnorway/gsim-raml-schema/blob/master/ssb_gsim_ldm.png",
+                language=ReferenceDataCode(
+                    uri="http://pubs.europa.eu/resource/authority/language/NOR",
+                ),
+            ),
+        ],
+        homepage="https://www.difi.no/fagomrader-og-tjenester/digitalisering-og-samordning/nasjonal-arkitektur/informasjonsforvaltning/adresse-felles-informasjonsmodell",
+        status="http://purl.org/adms/status/Completed",
+        versionInfo="1.0",
+        versionNotes={"nb": "Lagt til objekttypen Timeline"},
+        themeUris=["https://psi.norge.no/los/tema/skole-og-utdanning"],
+        losTheme=[
+            LosNode(
+                isTema=True,
+                losPaths=["skole-og-utdanning"],
+                code="skole-og-utdanning",
+                name={
+                    "nn": "Skule og utdanning",
+                    "nb": "Skole og utdanning",
+                    "en": "Schools and education",
+                },
+                uri="https://psi.norge.no/los/tema/skole-og-utdanning",
+            )
+        ],
+        type="informationmodels",
+    )
+
+    assert parse_information_model(src) == expected
+    assert parse_information_model_as_dict(src) == asdict(expected)
diff --git a/tests/test_public_service.py b/tests/test_public_service.py
index a3d0238..3d13930 100644
--- a/tests/test_public_service.py
+++ b/tests/test_public_service.py
@@ -1,6 +1,11 @@
+from dataclasses import asdict
 from unittest.mock import Mock
 
-from fdk_rdf_parser import parse_public_services
+from fdk_rdf_parser import (
+    parse_public_services,
+    parse_service,
+    parse_service_as_dict,
+)
 from fdk_rdf_parser.classes import (
     Address,
     Agent,
@@ -742,3 +747,186 @@ def test_service_evidence_collection_from_channel(
 
     with open("tests/test_data/service1.ttl", "r") as src:
         assert parse_public_services(src.read()) == expected
+
+
+def test_parse_single_service(
+    mock_reference_data_client: Mock,
+) -> None:
+    expected = Service(
+        id="1fc38c3c-1c86-3161-a9a7-e443fd94d413",
+        uri="https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exTjenesteDummy.ttl",
+        identifier="https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exTjenesteDummy.ttl",
+        title={"en": "Dummy service", "nn": "Dummytjeneste", "nb": "Dummytjeneste"},
+        description={
+            "en": "The text is displayed in English.",
+            "nn": "Teksten blir vist på nynorsk.",
+            "nb": "Dette er en dummytjeneste som kan brukes i forbindelse med testing av CPSV-AP-NO når det er behov for en relasjon til en tjeneste som det ikke finnes eksempel på ennå.",
+        },
+        harvest=HarvestMetaData(
+            firstHarvested="2022-05-18T11:26:51Z", changed=["2022-05-18T11:26:51Z"]
+        ),
+        ownedBy=[
+            Organization(
+                uri="https://www.staging.fellesdatakatalog.digdir.no/organizations/exOrganisasjonReduced",
+                identifier="https://www.staging.fellesdatakatalog.digdir.no/organizations/exOrganisasjonReduced",
+                name={"nb": "Organisasjon i Brønnøysund"},
+                orgType=ReferenceDataCode(
+                    uri="http://purl.org/adms/publishertype/NonGovernmentalOrganisation",
+                    prefLabel={
+                        "nn": "Ikkje-statleg organisasjon",
+                        "nb": "Ikke-statlig organisasjon",
+                        "en": "Non-Governmental Organisation",
+                    },
+                    code="NonGovernmentalOrganisation",
+                ),
+                spatial=[
+                    "http://publications.europa.eu/resource/authority/country/NOR",
+                    "https://data.geonorge.no/administrativeEnheter/kommune/id/172833",
+                ],
+                homepage=["https://www.bronnoy.organisasjon.no"],
+            ),
+        ],
+        contactPoint=[
+            CVContactPoint(
+                uri="https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exKontaktpunktDummy.ttl",
+                contactType={"nb": "Kontakt test"},
+                email=["mailto:postmottak@example.org"],
+                telephone=["tel:+4712345678"],
+                contactPage=["https://example.org/exKontaktside"],
+                language=[
+                    ReferenceDataCode(
+                        uri="http://publications.europa.eu/resource/authority/language/ENG",
+                        code="ENG",
+                        prefLabel={
+                            "en": "English",
+                            "nb": "Engelsk",
+                            "nn": "Engelsk",
+                            "no": "Engelsk",
+                        },
+                    ),
+                    ReferenceDataCode(
+                        uri="http://publications.europa.eu/resource/authority/language/NNO",
+                        code="NNO",
+                        prefLabel={
+                            "en": "Norwegian Nynorsk",
+                            "nb": "Norsk Nynorsk",
+                            "nn": "Norsk Nynorsk",
+                            "no": "Norsk Nynorsk",
+                        },
+                    ),
+                    ReferenceDataCode(
+                        uri="http://publications.europa.eu/resource/authority/language/NOB",
+                        code="NOB",
+                        prefLabel={
+                            "en": "Norwegian Bokmål",
+                            "nb": "Norsk Bokmål",
+                            "nn": "Norsk Bokmål",
+                            "no": "Norsk Bokmål",
+                        },
+                    ),
+                ],
+            )
+        ],
+        language=[
+            ReferenceDataCode(
+                uri="http://publications.europa.eu/resource/authority/language/ENG",
+                code="ENG",
+                prefLabel={
+                    "en": "English",
+                    "nb": "Engelsk",
+                    "nn": "Engelsk",
+                    "no": "Engelsk",
+                },
+            ),
+            ReferenceDataCode(
+                uri="http://publications.europa.eu/resource/authority/language/NOB",
+                code="NOB",
+                prefLabel={
+                    "en": "Norwegian Bokmål",
+                    "nb": "Norsk Bokmål",
+                    "nn": "Norsk Bokmål",
+                    "no": "Norsk Bokmål",
+                },
+            ),
+        ],
+        produces=[
+            Output(
+                uri="https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exTjenesteresultatDummy.ttl",
+                identifier="https://raw.githubusercontent.com/Informasjonsforvaltning/cpsv-ap-no/develop/examples/exTjenesteresultatDummy.ttl",
+                name={
+                    "nb": "Dummy tjenesteresultat",
+                    "nn": "Dummy tjenesteresultat",
+                    "en": "Dummy service result",
+                },
+                description={
+                    "en": "The text is displayed in English.",
+                    "nn": "Teksten blir vist på nynorsk.",
+                    "nb": "Dette er et dummy tjenesteresultat som kan brukes i forbindelse med testing av CPSV-AP-NO når det er behov for en relasjon til et tjenesteresultat.",
+                },
+                language=[
+                    ReferenceDataCode(
+                        uri="http://publications.europa.eu/resource/authority/language/ENG",
+                        code="ENG",
+                        prefLabel={
+                            "nn": "Engelsk",
+                            "no": "Engelsk",
+                            "nb": "Engelsk",
+                            "en": "English",
+                        },
+                    ),
+                    ReferenceDataCode(
+                        uri="http://publications.europa.eu/resource/authority/language/NNO",
+                        code="NNO",
+                        prefLabel={
+                            "nn": "Norsk Nynorsk",
+                            "no": "Norsk Nynorsk",
+                            "nb": "Norsk Nynorsk",
+                            "en": "Norwegian Nynorsk",
+                        },
+                    ),
+                    ReferenceDataCode(
+                        uri="http://publications.europa.eu/resource/authority/language/NOB",
+                        code="NOB",
+                        prefLabel={
+                            "nn": "Norsk Bokmål",
+                            "no": "Norsk Bokmål",
+                            "nb": "Norsk Bokmål",
+                            "en": "Norwegian Bokmål",
+                        },
+                    ),
+                ],
+            )
+        ],
+        type="publicservices",
+        thematicAreaUris=[
+            "http://publications.europa.eu/resource/authority/data-theme/GOVE",
+            "https://psi.norge.no/los/tema/naring",
+            "https://psi.norge.no/not/in/los/or/eu",
+        ],
+        losThemes=[
+            LosNode(
+                isTema=True,
+                losPaths=["naring"],
+                name={"nn": "Næring", "nb": "Næring", "en": "Business"},
+                uri="https://psi.norge.no/los/tema/naring",
+                code="naring",
+            ),
+        ],
+        euDataThemes=[
+            EuDataTheme(
+                uri="http://publications.europa.eu/resource/authority/data-theme/GOVE",
+                code="GOVE",
+                title={
+                    "nn": "Forvaltning og offentleg sektor",
+                    "no": "Forvaltning og offentlig sektor",
+                    "nb": "Forvaltning og offentlig sektor",
+                    "en": "Government and public sector",
+                },
+            ),
+        ],
+    )
+
+    with open("tests/test_data/service0.ttl", "r") as turtle_file:
+        src = turtle_file.read()
+        assert parse_service(src) == expected
+        assert parse_service_as_dict(src) == asdict(expected)