diff --git a/lib/src/definitions/additional_expected_response.dart b/lib/src/definitions/additional_expected_response.dart
index 77689312..3dbede58 100644
--- a/lib/src/definitions/additional_expected_response.dart
+++ b/lib/src/definitions/additional_expected_response.dart
@@ -7,6 +7,8 @@
 import 'package:collection/collection.dart';
 import 'package:meta/meta.dart';
 
+import 'extensions/json_parser.dart';
+
 /// Communication metadata describing the expected response message for the
 /// primary response.
 @immutable
@@ -14,59 +16,53 @@ class AdditionalExpectedResponse {
   /// Constructs a new [AdditionalExpectedResponse] object from a [contentType].
   AdditionalExpectedResponse(
     this.contentType, {
-    String? schema,
+    this.schema,
     bool? success,
-  })  : _success = success,
-        _schema = schema;
+    Map<String, dynamic>? additionalFields,
+  })  : additionalFields = additionalFields ?? {},
+        success = success ?? false;
 
   /// Creates an [AdditionalExpectedResponse] from a [json] object.
-  AdditionalExpectedResponse.fromJson(
+  factory AdditionalExpectedResponse.fromJson(
     Map<String, dynamic> json,
     String formContentType,
-  )   : contentType = _parseJson(json, 'contentType') ?? formContentType,
-        _success = _parseJson(json, 'success'),
-        _schema = _parseJson(json, 'schema') {
-    const parsedFields = ['contentType', 'schema', 'success'];
-
-    for (final entry in json.entries) {
-      final key = entry.key;
-      if (parsedFields.contains(key)) {
-        continue;
-      }
-
-      additionalFields[key] = entry.value;
-    }
-  }
-
-  static T? _parseJson<T>(Map<String, dynamic> json, String key) {
-    final dynamic value = json[key];
-
-    if (value is T) {
-      return value;
-    }
-
-    return null;
+  ) {
+    final Set<String> parsedFields = {};
+
+    final contentType =
+        json.parseField<String>('contentType', parsedFields) ?? formContentType;
+    final success = json.parseField<bool>('success', parsedFields);
+    final schema = json.parseField<String>('schema', parsedFields);
+
+    final additionalFields = Map.fromEntries(
+      json.entries.where((entry) => !parsedFields.contains(entry.key)),
+    );
+
+    return AdditionalExpectedResponse(
+      contentType,
+      schema: schema,
+      success: success,
+      additionalFields: additionalFields,
+    );
   }
 
   /// The [contentType] of this [AdditionalExpectedResponse] object.
   final String contentType;
 
-  final bool? _success;
-
   /// Signals if an additional response should not be considered an error.
-  bool get success => _success ?? false;
-
-  final String? _schema;
+  ///
+  /// Defaults to `false` if not explicitly set.
+  final bool success;
 
   /// Used to define the output data schema for an additional response if it
   /// differs from the default output data schema.
   ///
   /// Rather than a `DataSchema` object, the name of a previous definition given
   /// in a `schemaDefinitions` map must be used.
-  String? get schema => _schema;
+  final String? schema;
 
   /// Any other additional field will be included in this [Map].
-  final Map<String, dynamic> additionalFields = <String, dynamic>{};
+  final Map<String, dynamic> additionalFields;
 
   @override
   bool operator ==(Object other) {
diff --git a/lib/src/definitions/context_entry.dart b/lib/src/definitions/context_entry.dart
index 5d7028a8..24615e28 100644
--- a/lib/src/definitions/context_entry.dart
+++ b/lib/src/definitions/context_entry.dart
@@ -4,8 +4,17 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
+import 'package:curie/curie.dart';
 import 'package:meta/meta.dart';
 
+import 'validation/validation_exception.dart';
+
+const _validTdContextValues = [
+  'https://www.w3.org/2019/wot/td/v1',
+  'https://www.w3.org/2022/wot/td/v1.1',
+  'http://www.w3.org/ns/td'
+];
+
 /// Class holding a [value] and an optional [key] for representing different
 /// types of `@context` entries.
 @immutable
@@ -13,6 +22,78 @@ class ContextEntry {
   /// Creates a new [ContextEntry].
   const ContextEntry(this.value, this.key);
 
+  /// Parses a single `@context` entry from a given [json] value.
+  ///
+  /// @context extensions are added to the provided [prefixMapping].
+  /// If the given entry is the [firstEntry], it will be set in the
+  /// [prefixMapping] accordingly.
+  factory ContextEntry.fromJson(
+    dynamic json,
+    PrefixMapping prefixMapping, {
+    required bool firstEntry,
+  }) {
+    if (json is String) {
+      if (firstEntry && _validTdContextValues.contains(json)) {
+        prefixMapping.defaultPrefixValue = json;
+        return ContextEntry(json, null);
+      }
+    }
+
+    if (json is Map<String, dynamic>) {
+      for (final contextEntry in json.entries) {
+        final key = contextEntry.key;
+        final value = contextEntry.value;
+        if (value is String) {
+          if (!key.startsWith('@') && Uri.tryParse(value) != null) {
+            prefixMapping.addPrefix(key, value);
+          }
+          return ContextEntry(value, key);
+        }
+      }
+    }
+
+    throw ValidationException(
+      'Excepted either a String or a Map<String, String> '
+      'as @context entry, got ${json.runtimeType} instead.',
+    );
+  }
+
+  /// Parses a TD `@context` from a [json] value.
+  ///
+  /// @context extensions are added to the provided [prefixMapping].
+  static List<ContextEntry> parseContext(
+    dynamic json,
+    PrefixMapping prefixMapping,
+  ) {
+    var firstEntry = true;
+
+    if (json is String) {
+      return [
+        ContextEntry.fromJson(json, prefixMapping, firstEntry: firstEntry)
+      ];
+    }
+
+    if (json is List<dynamic>) {
+      final List<ContextEntry> result = [];
+      for (final contextEntry in json) {
+        result.add(
+          ContextEntry.fromJson(
+            contextEntry,
+            prefixMapping,
+            firstEntry: firstEntry,
+          ),
+        );
+        firstEntry = false;
+      }
+      return result;
+    }
+
+    throw ValidationException(
+      'Excepted either a single @context entry or a List of @context entries, '
+      'got ${json.runtimeType} instead.',
+    );
+  }
+
   /// The [value] of this [ContextEntry].
   final String value;
 
diff --git a/lib/src/definitions/data_schema.dart b/lib/src/definitions/data_schema.dart
index bfae10df..cdfcce8a 100644
--- a/lib/src/definitions/data_schema.dart
+++ b/lib/src/definitions/data_schema.dart
@@ -4,30 +4,21 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
+import 'extensions/json_parser.dart';
+
 /// Parses a [json] object and adds its contents to a [dataSchema].
 void parseDataSchemaJson(DataSchema dataSchema, Map<String, dynamic> json) {
-  // TODO(JKRhb): Parse more DataSchema values
-  final Object? atType = json['@type'];
-  if (atType is String) {
-    dataSchema.atType = [atType];
-  } else if (atType is List<String>) {
-    dataSchema.atType = atType;
-  }
-
-  final Object? type = json['type'];
-  if (type is String) {
-    dataSchema.type = type;
-  }
-
-  final Object? readOnly = json['readOnly'];
-  if (readOnly is bool) {
-    dataSchema.readOnly = readOnly;
-  }
-
-  final Object? writeOnly = json['writeOnly'];
-  if (writeOnly is bool) {
-    dataSchema.writeOnly = writeOnly;
-  }
+  dataSchema
+    ..atType = json.parseArrayField<String>('@type')
+    ..title = json.parseField<String>('title')
+    ..titles = json.parseMapField<String>('titles')
+    ..description = json.parseField<String>('description')
+    ..constant = json.parseField<Object>('constant')
+    ..enumeration = json.parseField<List<Object>>('enum')
+    ..readOnly = json.parseField<bool>('readOnly') ?? dataSchema.readOnly
+    ..writeOnly = json.parseField<bool>('writeOnly') ?? dataSchema.writeOnly
+    ..format = json.parseField<String>('format')
+    ..type = json.parseField<String>('type');
 }
 
 /// Metadata that describes the data format used. It can be used for validation.
diff --git a/lib/src/definitions/expected_response.dart b/lib/src/definitions/expected_response.dart
index cc725f05..6934506e 100644
--- a/lib/src/definitions/expected_response.dart
+++ b/lib/src/definitions/expected_response.dart
@@ -4,36 +4,42 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'validation/validation_exception.dart';
+import 'extensions/json_parser.dart';
 
 /// Communication metadata describing the expected response message for the
 /// primary response.
 class ExpectedResponse {
   /// Constructs a new [ExpectedResponse] object from a [contentType].
-  ExpectedResponse(this.contentType);
+  ExpectedResponse(this.contentType, {Map<String, dynamic>? additionalFields})
+      : additionalFields = Map.fromEntries(
+          additionalFields?.entries
+                  .where((element) => element.key != 'contentType') ??
+              [],
+        );
 
   /// Creates an [ExpectedResponse] from a [json] object.
-  ExpectedResponse.fromJson(Map<String, dynamic> json)
-      : contentType = _parseContentType(json['contentType']) {
-    for (final entry in json.entries) {
-      if (entry.key == 'response') {
-        continue;
-      }
+  static ExpectedResponse? fromJson(
+    Map<String, dynamic> json, [
+    Set<String>? parsedFields,
+  ]) {
+    final responseJson = json['response'];
+    parsedFields?.add('response');
 
-      additionalFields[entry.key] = entry.value;
+    if (responseJson is! Map<String, dynamic>) {
+      return null;
     }
+
+    return ExpectedResponse(
+      responseJson.parseRequiredField<String>('contentType'),
+      additionalFields: Map.fromEntries(
+        responseJson.entries.where((element) => element.key != 'contentType'),
+      ),
+    );
   }
 
   /// The [contentType] of this [ExpectedResponse] object.
   String contentType;
 
   /// Any other additional field will be included in this [Map].
-  final Map<String, dynamic> additionalFields = <String, dynamic>{};
-
-  static String _parseContentType(dynamic contentType) {
-    if (contentType is! String) {
-      throw ValidationException('contentType of response map is not a String!');
-    }
-    return contentType;
-  }
+  final Map<String, dynamic> additionalFields;
 }
diff --git a/lib/src/definitions/extensions/json_parser.dart b/lib/src/definitions/extensions/json_parser.dart
new file mode 100644
index 00000000..619603d9
--- /dev/null
+++ b/lib/src/definitions/extensions/json_parser.dart
@@ -0,0 +1,87 @@
+import '../validation/validation_exception.dart';
+
+/// Extension for parsing fields of JSON objects.
+extension ParseField on Map<String, dynamic> {
+  /// Parses a single field with a given [name].
+  ///
+  /// Ensures that the field value is of type [T] and returns `null` if the
+  /// value does not have this type or is not present.
+  ///
+  /// If a [Set] of [parsedFields] is passed to this function, the field [name]
+  /// will added. This can be used for filtering when parsing additional fields.
+  T? parseField<T>(String name, [Set<String>? parsedFields]) {
+    final value = this[name];
+    parsedFields?.add(name);
+    if (value is T) {
+      return value;
+    }
+
+    return null;
+  }
+
+  /// Parses a single field with a given [name] and throws a
+  /// [ValidationException] if the field is not present or does not have the
+  /// type [T].
+  ///
+  /// Like [parseField], it adds the field [name] to the set of [parsedFields],
+  /// if present.
+  T parseRequiredField<T>(String name, [Set<String>? parsedFields]) {
+    final value = parseField(name, parsedFields);
+
+    if (value is! T) {
+      throw ValidationException(
+        'Value for field $name has wrong data type or is missing. '
+        'Expected ${T.runtimeType}, got ${value.runtimeType}.',
+      );
+    }
+
+    return value;
+  }
+
+  /// Parses a map field with a given [name].
+  ///
+  /// Ensures that the field value is of type [T] and returns `null` if the
+  /// value does not have this type or is not present.
+  ///
+  /// If a [Set] of [parsedFields] is passed to this function, the field [name]
+  /// will added. This can be used for filtering when parsing additional fields.
+  Map<String, T>? parseMapField<T>(String name, [Set<String>? parsedFields]) {
+    final dynamic mapField = this[name];
+    parsedFields?.add(name);
+
+    if (mapField is Map<String, dynamic>) {
+      final Map<String, T> result = {};
+
+      for (final entry in mapField.entries) {
+        final value = entry.value;
+        if (value is T) {
+          result[entry.key] = value;
+        }
+      }
+
+      return result;
+    }
+    return null;
+  }
+
+  /// Parses a field with a given [name] that can contain either a single value
+  /// or a list of values of type [T].
+  ///
+  /// Ensures that the field value is of type [T] or `List<T>` and returns
+  /// `null` if the value does not have one of these types or is not present.
+  ///
+  /// If a [Set] of [parsedFields] is passed to this function, the field [name]
+  /// will added. This can be used for filtering when parsing additional fields.
+  List<T>? parseArrayField<T>(String name, [Set<String>? parsedFields]) {
+    final value = this[name];
+    parsedFields?.add(name);
+
+    if (value is T) {
+      return [value];
+    } else if (value is List<dynamic>) {
+      return value.whereType<T>().toList(growable: false);
+    }
+
+    return null;
+  }
+}
diff --git a/lib/src/definitions/form.dart b/lib/src/definitions/form.dart
index b946f80e..6a019651 100644
--- a/lib/src/definitions/form.dart
+++ b/lib/src/definitions/form.dart
@@ -10,6 +10,7 @@ import 'package:uri/uri.dart';
 
 import 'additional_expected_response.dart';
 import 'expected_response.dart';
+import 'extensions/json_parser.dart';
 import 'interaction_affordances/action.dart';
 import 'interaction_affordances/event.dart';
 import 'interaction_affordances/interaction_affordance.dart';
@@ -49,78 +50,28 @@ class Form {
     Map<String, dynamic> json,
     InteractionAffordance interactionAffordance,
   ) {
-    final List<String> parsedJsonFields = [];
-    final href = _parseHref(json, parsedJsonFields);
+    final Set<String> parsedFields = {};
+    final href =
+        Uri.parse(json.parseRequiredField<String>('href', parsedFields));
 
-    String? subprotocol;
-    if (json['subprotocol'] is String) {
-      parsedJsonFields.add('subprotocol');
-      subprotocol = json['subprotocol'] as String;
-    }
-
-    List<String>? op;
-    if (json['op'] != null) {
-      final dynamic jsonOp = _getJsonValue(json, 'op', parsedJsonFields);
-      if (jsonOp is String) {
-        op = [jsonOp];
-      } else if (jsonOp is List<dynamic>) {
-        op = jsonOp.whereType<String>().toList(growable: false);
-      }
-    }
+    final subprotocol = json.parseField<String>('subprotocol', parsedFields);
 
-    String contentType = 'application/json';
-    if (json['contentType'] != null) {
-      final dynamic jsonContentType =
-          _getJsonValue(json, 'contentType', parsedJsonFields);
-      if (jsonContentType is String) {
-        contentType = jsonContentType;
-      }
-    }
+    final List<String>? op = json.parseArrayField<String>('op', parsedFields);
 
-    String? contentCoding;
-    if (json['contentCoding'] != null) {
-      final dynamic jsonContentCoding =
-          _getJsonValue(json, 'contentCoding', parsedJsonFields);
-      if (jsonContentCoding is String) {
-        contentCoding = jsonContentCoding;
-      }
-    }
+    final contentType = json.parseField<String>('contentType', parsedFields) ??
+        'application/json';
 
-    List<String>? security;
-    if (json['security'] != null) {
-      final dynamic jsonSecurity =
-          _getJsonValue(json, 'security', parsedJsonFields);
-      if (jsonSecurity is String) {
-        security = [jsonSecurity];
-      } else if (jsonSecurity is List<dynamic>) {
-        security = jsonSecurity.whereType<String>().toList(growable: false);
-      }
-    }
+    final contentCoding =
+        json.parseField<String>('contentCoding', parsedFields);
 
-    List<String>? scopes;
-    if (json['scopes'] != null) {
-      final dynamic jsonScopes =
-          _getJsonValue(json, 'scopes', parsedJsonFields);
-      if (jsonScopes is String) {
-        scopes = [jsonScopes];
-      } else if (jsonScopes is List<dynamic>) {
-        scopes = jsonScopes.whereType<String>().toList(growable: false);
-      }
-    }
-
-    ExpectedResponse? response;
-    if (json['response'] != null) {
-      final dynamic jsonResponse =
-          _getJsonValue(json, 'response', parsedJsonFields);
-      if (jsonResponse is Map<String, dynamic>) {
-        response = ExpectedResponse.fromJson(jsonResponse);
-      }
-    }
+    final security = json.parseArrayField<String>('security', parsedFields);
+    final scopes = json.parseArrayField<String>('scopes', parsedFields);
+    final response = ExpectedResponse.fromJson(json, parsedFields);
 
     List<AdditionalExpectedResponse>? additionalResponses;
     if (json['additionalResponses'] != null) {
       final dynamic jsonResponse =
-          _getJsonValue(json, 'additionalResponses', parsedJsonFields);
+          _getJsonValue(json, 'additionalResponses', parsedFields);
       if (jsonResponse is Map<String, dynamic>) {
         additionalResponses = [
           AdditionalExpectedResponse.fromJson(jsonResponse, contentType)
@@ -138,7 +89,7 @@ class Form {
 
     final additionalFields = _parseAdditionalFields(
       json,
-      parsedJsonFields,
+      parsedFields,
       interactionAffordance.thingDescription.prefixMapping,
     );
 
@@ -252,19 +203,6 @@ class Form {
     }
   }
 
-  static Uri _parseHref(
-    Map<String, dynamic> json,
-    List<String> parsedJsonFields,
-  ) {
-    final dynamic href = json['href'];
-    parsedJsonFields.add('href');
-    if (href is String) {
-      return Uri.parse(href);
-    } else {
-      throw ValidationException("'href' field must be a string.");
-    }
-  }
-
   static List<OperationType> _setOpValue(
     InteractionAffordance interactionAffordance,
     List<String>? opStrings,
@@ -290,14 +228,14 @@ class Form {
 
     throw StateError(
       'Encountered unknown InteractionAffordance '
-      '${interactionAffordance.runtimeType} encountered',
+      '${interactionAffordance.runtimeType}.',
     );
   }
 
   static dynamic _getJsonValue(
     Map<String, dynamic> formJson,
     String key,
-    List<String> parsedJsonFields,
+    Set<String> parsedJsonFields,
   ) {
     parsedJsonFields.add(key);
     return formJson[key];
@@ -332,12 +270,12 @@ class Form {
 
   static Map<String, dynamic> _parseAdditionalFields(
     Map<String, dynamic> formJson,
-    List<String> parsedJsonFields,
+    Set<String> parsedFields,
     PrefixMapping prefixMapping,
   ) {
     final additionalFields = <String, dynamic>{};
     for (final entry in formJson.entries) {
-      if (!parsedJsonFields.contains(entry.key)) {
+      if (!parsedFields.contains(entry.key)) {
         final String key = _expandCurieKey(entry.key, prefixMapping);
         final dynamic value = _expandCurieValue(entry.value, prefixMapping);
 
diff --git a/lib/src/definitions/interaction_affordances/interaction_affordance.dart b/lib/src/definitions/interaction_affordances/interaction_affordance.dart
index 1d243293..abf26fc5 100644
--- a/lib/src/definitions/interaction_affordances/interaction_affordance.dart
+++ b/lib/src/definitions/interaction_affordances/interaction_affordance.dart
@@ -6,6 +6,7 @@
 
 import 'package:curie/curie.dart';
 
+import '../extensions/json_parser.dart';
 import '../form.dart';
 import '../thing_description.dart';
 
@@ -49,24 +50,6 @@ abstract class InteractionAffordance {
     }
   }
 
-  Map<String, String>? _parseMultilangString(
-    Map<String, dynamic> json,
-    String jsonKey,
-  ) {
-    Map<String, String>? field;
-    final dynamic jsonEntries = json[jsonKey];
-    if (jsonEntries is Map<String, dynamic>) {
-      field = {};
-      for (final entry in jsonEntries.entries) {
-        final dynamic value = entry.value;
-        if (value is String) {
-          field[entry.key] = value;
-        }
-      }
-    }
-    return field;
-  }
-
   /// Parses the [InteractionAffordance] contained in a [json] object.
   void parseAffordanceFields(
     Map<String, dynamic> json,
@@ -74,25 +57,10 @@ abstract class InteractionAffordance {
   ) {
     _parseForms(json, prefixMapping);
 
-    final dynamic title = json['title'];
-    if (title is String) {
-      this.title = title;
-    }
-
-    titles = _parseMultilangString(json, 'titles');
-
-    final dynamic description = json['description'];
-    if (description is String) {
-      this.description = description;
-    }
-
-    descriptions = _parseMultilangString(json, 'descriptions');
-
-    if (json['uriVariables'] != null) {
-      final dynamic jsonUriVariables = json['uriVariables'];
-      if (jsonUriVariables is Map<String, dynamic>) {
-        uriVariables = jsonUriVariables;
-      }
-    }
+    title = json.parseField('title');
+    titles = json.parseMapField<String>('titles');
+    description = json.parseField('description');
+    descriptions = json.parseMapField<String>('descriptions');
+    uriVariables = json.parseMapField<dynamic>('uriVariables');
   }
 }
diff --git a/lib/src/definitions/interaction_affordances/property.dart b/lib/src/definitions/interaction_affordances/property.dart
index 089930ed..6a8a8149 100644
--- a/lib/src/definitions/interaction_affordances/property.dart
+++ b/lib/src/definitions/interaction_affordances/property.dart
@@ -7,25 +7,22 @@
 import 'package:curie/curie.dart';
 
 import '../data_schema.dart';
+import '../extensions/json_parser.dart';
 import '../thing_description.dart';
 import 'interaction_affordance.dart';
 
 /// Class representing a [Property] Affordance in a Thing Description.
 class Property extends InteractionAffordance implements DataSchema {
   /// Default constructor that creates a [Property] from a [List] of [forms].
-  Property(super.forms, super.thingDescription);
+  Property(super.forms, super.thingDescription, {this.observable = false});
 
   /// Creates a new [Property] from a [json] object.
   Property.fromJson(
     Map<String, dynamic> json,
     ThingDescription thingDescription,
     PrefixMapping prefixMapping,
-  ) : super([], thingDescription) {
-    final dynamic observable = json['observable'];
-    if (observable is bool) {
-      _observable = observable;
-    }
-
+  )   : observable = json.parseField<bool>('observable') ?? false,
+        super([], thingDescription) {
     parseAffordanceFields(json, prefixMapping);
     parseDataSchemaJson(this, json);
     rawJson = json;
@@ -61,12 +58,10 @@ class Property extends InteractionAffordance implements DataSchema {
   @override
   bool? writeOnly = false;
 
-  bool _observable = false;
-
   /// A hint that indicates whether Servients hosting the Thing and
   /// Intermediaries should provide a Protocol Binding that supports the
   /// `observeproperty` and `unobserveproperty` operations for this Property.
-  bool get observable => _observable;
+  final bool observable;
 
   @override
   Map<String, dynamic>? rawJson;
diff --git a/lib/src/definitions/link.dart b/lib/src/definitions/link.dart
index 6ba21231..0ddcc4a0 100644
--- a/lib/src/definitions/link.dart
+++ b/lib/src/definitions/link.dart
@@ -4,7 +4,7 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'validation/validation_exception.dart';
+import 'extensions/json_parser.dart';
 
 /// Represents an element of the `links` array in a Thing Description.
 ///
@@ -30,45 +30,21 @@ class Link {
 
   /// Creates a new [Link] from a [json] object.
   Link.fromJson(Map<String, dynamic> json) {
-    // TODO(JKRhb): Check if this can be refactored
-    if (json['href'] is String) {
-      _parsedJsonFields.add('href');
-      final hrefString = json['href'] as String;
-      href = Uri.parse(hrefString);
-    } else {
-      // [href] *must* be initialized.
-      throw ValidationException("'href' field must exist as a string.");
-    }
-
-    if (json['type'] is String) {
-      _parsedJsonFields.add('type');
-      type = json['type'] as String;
-    }
-
-    if (json['rel'] is String) {
-      _parsedJsonFields.add('rel');
-      rel = json['rel'] as String;
-    }
-
-    if (json['anchor'] is String) {
-      _parsedJsonFields.add('anchor');
-      anchor = Uri.parse(json['anchor'] as String);
-    }
-
-    if (json['sizes'] is String) {
-      _parsedJsonFields.add('sizes');
-      sizes = json['sizes'] as String;
-    }
-
-    final dynamic hreflang = json['hreflang'];
-    _parsedJsonFields.add('hreflang');
-    if (hreflang is String) {
-      this.hreflang = [hreflang];
-    } else if (hreflang is List<dynamic>) {
-      this.hreflang = hreflang.whereType<String>().toList();
-    }
-
-    _addAdditionalFields(json);
+    final Set<String> parsedFields = {};
+
+    href = Uri.parse(json.parseRequiredField<String>('href', parsedFields));
+    type = json.parseField<String>('@type', parsedFields);
+    rel = json.parseField<String>('rel', parsedFields);
+    anchor =
+        Uri.tryParse(json.parseField<String>('anchor', parsedFields) ?? '');
+    sizes = json.parseField<String>('sizes', parsedFields);
+    hreflang = json.parseArrayField<String>('hreflang', parsedFields);
+
+    additionalFields.addAll(
+      Map.fromEntries(
+        json.entries.where((element) => !parsedFields.contains(element.key)),
+      ),
+    );
   }
 
   /// Target IRI of a link or submission target of a form.
@@ -98,16 +74,6 @@ class Link {
   /// [BCP47 link]: https://tools.ietf.org/search/bcp47
   List<String>? hreflang;
 
-  final List<String> _parsedJsonFields = [];
-
   /// Additional fields collected during the parsing of a JSON object.
   final Map<String, dynamic> additionalFields = <String, dynamic>{};
-
-  void _addAdditionalFields(Map<String, dynamic> formJson) {
-    for (final entry in formJson.entries) {
-      if (!_parsedJsonFields.contains(entry.key)) {
-        additionalFields[entry.key] = entry.value;
-      }
-    }
-  }
 }
diff --git a/lib/src/definitions/operation_type.dart b/lib/src/definitions/operation_type.dart
index 2c92f6af..3475ca3b 100644
--- a/lib/src/definitions/operation_type.dart
+++ b/lib/src/definitions/operation_type.dart
@@ -43,19 +43,19 @@ enum OperationType {
   /// Corresponds with the `unsubscribeevent` operation type.
   unsubscribeevent;
 
-  /// Constructor
-  const OperationType();
+  static final Map<String, OperationType> _registry =
+      Map.fromEntries(OperationType.values.map((e) => MapEntry(e.name, e)));
 
   /// Creates an [OperationType] from a [stringValue].
   static OperationType fromString(String stringValue) {
-    for (final value in OperationType.values) {
-      if (stringValue == value.name) {
-        return value;
-      }
+    final operationType = OperationType._registry[stringValue];
+
+    if (operationType == null) {
+      throw ValidationException(
+        'Encountered unknown OperationType $stringValue.',
+      );
     }
 
-    throw ValidationException(
-      'Encountered unknown OperationType $stringValue.',
-    );
+    return operationType;
   }
 }
diff --git a/lib/src/definitions/security/ace_security_scheme.dart b/lib/src/definitions/security/ace_security_scheme.dart
index f63dce22..ad92e81e 100644
--- a/lib/src/definitions/security/ace_security_scheme.dart
+++ b/lib/src/definitions/security/ace_security_scheme.dart
@@ -4,12 +4,11 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
+import '../extensions/json_parser.dart';
+
 import 'security_scheme.dart';
 
 /// Experimental ACE Security Scheme.
-// TODO(JKRhb): Check whether an audience field is needed or if this implied by
-// the base field/form href.
 class AceSecurityScheme extends SecurityScheme {
   /// Constructor.
   AceSecurityScheme({
@@ -26,36 +25,14 @@ class AceSecurityScheme extends SecurityScheme {
 
   /// Creates an [AceSecurityScheme] from a [json] object.
   AceSecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
-
-    final dynamic jsonAs = _getJsonValue(json, 'ace:as');
-    if (jsonAs is String) {
-      as = jsonAs;
-      _parsedJsonFields.add('ace:as');
-    }
-
-    final dynamic jsonCnonce = _getJsonValue(json, 'ace:cnonce');
-    if (jsonCnonce is bool) {
-      cnonce = jsonCnonce;
-      _parsedJsonFields.add('ace:cnonce');
-    }
-
-    final dynamic jsonAudience = _getJsonValue(json, 'ace:audience');
-    if (jsonAudience is String) {
-      audience = jsonAudience;
-      _parsedJsonFields.add('ace:audience');
-    }
+    final Set<String> parsedFields = {};
 
-    final dynamic jsonScopes = _getJsonValue(json, 'ace:scopes');
-    if (jsonScopes is String) {
-      scopes = [jsonScopes];
-      _parsedJsonFields.add('ace:scopes');
-    } else if (jsonScopes is List<dynamic>) {
-      scopes = jsonScopes.whereType<String>().toList();
-      _parsedJsonFields.add('ace:scopes');
-    }
+    as = json.parseField<String>('ace:as', parsedFields);
+    cnonce = json.parseField<bool>('ace:cnonce', parsedFields);
+    audience = json.parseField<String>('ace:audience', parsedFields);
+    scopes = json.parseArrayField<String>('ace:scopes', parsedFields);
 
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, parsedFields);
   }
 
   @override
@@ -77,11 +54,4 @@ class AceSecurityScheme extends SecurityScheme {
 
   /// Indicates whether a [cnonce] is required by the Resource Server.
   bool? cnonce;
-
-  final List<String> _parsedJsonFields = [];
-
-  dynamic _getJsonValue(Map<String, dynamic> json, String key) {
-    _parsedJsonFields.add(key);
-    return json[key];
-  }
 }
diff --git a/lib/src/definitions/security/apikey_security_scheme.dart b/lib/src/definitions/security/apikey_security_scheme.dart
index 6726bbc6..1667104a 100644
--- a/lib/src/definitions/security/apikey_security_scheme.dart
+++ b/lib/src/definitions/security/apikey_security_scheme.dart
@@ -4,42 +4,31 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
+import '../extensions/json_parser.dart';
 import 'security_scheme.dart';
 
+const _defaultInValue = 'query';
+
 /// API key authentication security configuration identified by the Vocabulary
 /// Term `apikey`.
 class ApiKeySecurityScheme extends SecurityScheme {
   /// Constructor.
   ApiKeySecurityScheme({
-    String? description,
-    String? proxy,
+    super.description,
+    super.proxy,
     this.name,
     String? in_,
-    Map<String, String>? descriptions,
-  }) : in_ = in_ ?? 'query' {
-    this.description = description;
-    this.proxy = proxy;
-    this.descriptions.addAll(descriptions ?? {});
-  }
+    super.descriptions,
+  }) : in_ = in_ ?? _defaultInValue;
 
   /// Creates a [ApiKeySecurityScheme] from a [json] object.
   ApiKeySecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
-
-    final dynamic jsonIn = _getJsonValue(json, 'in');
-    if (jsonIn is String) {
-      in_ = jsonIn;
-      _parsedJsonFields.add('in');
-    }
+    final Set<String> parsedFields = {};
 
-    final dynamic jsonName = _getJsonValue(json, 'name');
-    if (jsonName is String) {
-      name = jsonName;
-      _parsedJsonFields.add('name');
-    }
+    name = json.parseField<String>('name', parsedFields);
+    in_ = json.parseField<String>('in', parsedFields) ?? _defaultInValue;
 
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, parsedFields);
   }
 
   @override
@@ -49,12 +38,5 @@ class ApiKeySecurityScheme extends SecurityScheme {
   String? name;
 
   /// Specifies the location of security authentication information.
-  late String in_ = 'query';
-
-  final List<String> _parsedJsonFields = [];
-
-  dynamic _getJsonValue(Map<String, dynamic> json, String key) {
-    _parsedJsonFields.add(key);
-    return json[key];
-  }
+  late String in_;
 }
diff --git a/lib/src/definitions/security/auto_security_scheme.dart b/lib/src/definitions/security/auto_security_scheme.dart
index b8987de3..0e273789 100644
--- a/lib/src/definitions/security/auto_security_scheme.dart
+++ b/lib/src/definitions/security/auto_security_scheme.dart
@@ -4,7 +4,6 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
 import 'security_scheme.dart';
 
 /// An automatic security configuration identified by the
@@ -12,12 +11,9 @@ import 'security_scheme.dart';
 class AutoSecurityScheme extends SecurityScheme {
   /// Creates an [AutoSecurityScheme] from a [json] object.
   AutoSecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, {});
   }
 
   @override
   String get scheme => 'auto';
-
-  final List<String> _parsedJsonFields = [];
 }
diff --git a/lib/src/definitions/security/basic_security_scheme.dart b/lib/src/definitions/security/basic_security_scheme.dart
index f27879db..b8dd3497 100644
--- a/lib/src/definitions/security/basic_security_scheme.dart
+++ b/lib/src/definitions/security/basic_security_scheme.dart
@@ -4,57 +4,39 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
+import '../extensions/json_parser.dart';
 import 'security_scheme.dart';
 
+const _defaultInValue = 'header';
+
 /// Basic Authentication security configuration identified by the Vocabulary
 /// Term `basic`.
 class BasicSecurityScheme extends SecurityScheme {
   /// Constructor.
   BasicSecurityScheme({
-    String? description,
-    String? proxy,
+    super.description,
+    super.proxy,
     this.name,
     String? in_,
-    Map<String, String>? descriptions,
-  }) : in_ = in_ ?? 'header' {
-    this.description = description;
-    this.proxy = proxy;
-    this.descriptions.addAll(descriptions ?? {});
-  }
+    super.descriptions,
+  }) : in_ = in_ ?? _defaultInValue;
 
   /// Creates a [BasicSecurityScheme] from a [json] object.
   BasicSecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
-
-    final dynamic jsonIn = _getJsonValue(json, 'in');
-    if (jsonIn is String) {
-      in_ = jsonIn;
-      _parsedJsonFields.add('in');
-    }
+    final Set<String> parsedFields = {};
 
-    final dynamic jsonName = _getJsonValue(json, 'name');
-    if (jsonName is String) {
-      name = jsonName;
-      _parsedJsonFields.add('name');
-    }
+    name = json.parseField<String>('name', parsedFields);
+    in_ = json.parseField<String>('in', parsedFields) ?? _defaultInValue;
 
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, parsedFields);
   }
 
   @override
   String get scheme => 'basic';
 
   /// Name for query, header, cookie, or uri parameters.
-  String? name;
+  late final String? name;
 
   /// Specifies the location of security authentication information.
   late String in_ = 'header';
-
-  final List<String> _parsedJsonFields = [];
-
-  dynamic _getJsonValue(Map<String, dynamic> json, String key) {
-    _parsedJsonFields.add(key);
-    return json[key];
-  }
 }
diff --git a/lib/src/definitions/security/bearer_security_scheme.dart b/lib/src/definitions/security/bearer_security_scheme.dart
index d8a7e7e2..1928b485 100644
--- a/lib/src/definitions/security/bearer_security_scheme.dart
+++ b/lib/src/definitions/security/bearer_security_scheme.dart
@@ -4,89 +4,59 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
+import '../extensions/json_parser.dart';
 import 'security_scheme.dart';
 
+const _defaultInValue = 'header';
+const _defaultAlgValue = 'ES256';
+const _defaultFormatValue = 'jwt';
+
 /// Bearer Token security configuration identified by the Vocabulary Term
 /// `bearer`.
 class BearerSecurityScheme extends SecurityScheme {
   /// Constructor.
   BearerSecurityScheme({
-    String? description,
-    String? proxy,
     this.name,
     String? alg,
     String? format,
     this.authorization,
     String? in_,
-    Map<String, String>? descriptions,
-  })  : in_ = in_ ?? 'header',
-        alg = alg ?? 'ES256',
-        format = format ?? 'jwt' {
-    this.description = description;
-    this.proxy = proxy;
-    this.descriptions.addAll(descriptions ?? {});
-  }
+    super.proxy,
+    super.description,
+    super.descriptions,
+  })  : in_ = in_ ?? _defaultInValue,
+        alg = alg ?? _defaultAlgValue,
+        format = format ?? _defaultFormatValue;
 
   /// Creates a [BearerSecurityScheme] from a [json] object.
   BearerSecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
-
-    final dynamic jsonIn = _getJsonValue(json, 'in');
-    if (jsonIn is String) {
-      in_ = jsonIn;
-      _parsedJsonFields.add('in');
-    }
-
-    final dynamic jsonName = _getJsonValue(json, 'name');
-    if (jsonName is String) {
-      name = jsonName;
-      _parsedJsonFields.add('name');
-    }
-
-    final dynamic jsonFormat = _getJsonValue(json, 'format');
-    if (jsonFormat is String) {
-      format = jsonFormat;
-      _parsedJsonFields.add('format');
-    }
+    final Set<String> parsedFields = {};
 
-    final dynamic jsonAlg = _getJsonValue(json, 'alg');
-    if (jsonAlg is String) {
-      alg = jsonAlg;
-      _parsedJsonFields.add('alg');
-    }
+    name = json.parseField<String>('name', parsedFields);
+    in_ = json.parseField<String>('in', parsedFields) ?? _defaultInValue;
+    format =
+        json.parseField<String>('format', parsedFields) ?? _defaultFormatValue;
+    alg = json.parseField<String>('alg', parsedFields) ?? _defaultAlgValue;
+    authorization = json.parseField<String>('authorization', parsedFields);
 
-    final dynamic jsonAuthorization = _getJsonValue(json, 'authorization');
-    if (jsonAuthorization is String) {
-      authorization = jsonAuthorization;
-      _parsedJsonFields.add('authorization');
-    }
-
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, parsedFields);
   }
 
   @override
   String get scheme => 'bearer';
 
   /// URI of the authorization server.
-  String? authorization;
+  late final String? authorization;
 
   /// Name for query, header, cookie, or uri parameters.
-  String? name;
+  late final String? name;
 
   /// Encoding, encryption, or digest algorithm.
-  String alg = 'ES256';
+  late final String alg;
 
   /// Specifies format of security authentication information.
-  String? format = 'jwt';
+  late final String format;
 
   /// Specifies the location of security authentication information.
-  String in_ = 'header';
-
-  final List<String> _parsedJsonFields = [];
-
-  dynamic _getJsonValue(Map<String, dynamic> json, String key) {
-    _parsedJsonFields.add(key);
-    return json[key];
-  }
+  late final String in_;
 }
diff --git a/lib/src/definitions/security/digest_security_scheme.dart b/lib/src/definitions/security/digest_security_scheme.dart
index 6ee1c59f..edbcb2ba 100644
--- a/lib/src/definitions/security/digest_security_scheme.dart
+++ b/lib/src/definitions/security/digest_security_scheme.dart
@@ -4,7 +4,7 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
+import '../extensions/json_parser.dart';
 import 'security_scheme.dart';
 
 const _defaultInValue = 'header';
@@ -16,72 +16,35 @@ const _defaultQoPValue = 'auth';
 class DigestSecurityScheme extends SecurityScheme {
   /// Constructor.
   DigestSecurityScheme({
-    String? description,
-    String? proxy,
-    this.name,
     String? in_,
     String? qop,
-    Map<String, String>? descriptions,
-  })  : _in = in_,
-        _qop = qop {
-    this.description = description;
-    this.proxy = proxy;
-    this.descriptions.addAll(descriptions ?? {});
-  }
+    super.description,
+    super.proxy,
+    this.name,
+    super.descriptions,
+  })  : in_ = in_ ?? _defaultInValue,
+        qop = qop ?? _defaultQoPValue;
 
   /// Creates a [DigestSecurityScheme] from a [json] object.
-  DigestSecurityScheme.fromJson(Map<String, dynamic> json)
-      : name = _parseNameJson(json) {
-    _parsedJsonFields
-      ..addAll(parseSecurityJson(this, json))
-      ..add('name');
+  DigestSecurityScheme.fromJson(Map<String, dynamic> json) {
+    final Set<String> parsedFields = {};
 
-    final dynamic jsonIn = _getJsonValue(json, 'in', _parsedJsonFields);
-    if (jsonIn is String) {
-      _in = jsonIn;
-    }
+    name = json.parseField<String>('name', parsedFields);
+    in_ = json.parseField<String>('in', parsedFields) ?? _defaultInValue;
+    qop = json.parseField<String>('qop', parsedFields) ?? _defaultInValue;
 
-    final dynamic jsonQop = _getJsonValue(json, 'qop', _parsedJsonFields);
-    if (jsonQop is String) {
-      _qop = jsonQop;
-    }
-
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, parsedFields);
   }
 
   @override
   String get scheme => 'digest';
 
   /// Name for query, header, cookie, or uri parameters.
-  final String? name;
-
-  String? _in;
+  late final String? name;
 
   /// Specifies the location of security authentication information.
-  String get in_ => _in ?? _defaultInValue;
-
-  String? _qop;
+  late final String in_;
 
   /// Quality of protection.
-  String get qop => _qop ?? _defaultQoPValue;
-
-  final List<String> _parsedJsonFields = [];
-
-  static dynamic _getJsonValue(
-    Map<String, dynamic> json,
-    String key, [
-    List<String>? parsedJsonFields,
-  ]) {
-    parsedJsonFields?.add(key);
-    return json[key];
-  }
-
-  static String? _parseNameJson(Map<String, dynamic> json) {
-    final dynamic jsonName = _getJsonValue(json, 'name');
-    if (jsonName is String) {
-      return jsonName;
-    }
-
-    return null;
-  }
+  late final String? qop;
 }
diff --git a/lib/src/definitions/security/helper_functions.dart b/lib/src/definitions/security/helper_functions.dart
deleted file mode 100644
index 7b0a339e..00000000
--- a/lib/src/definitions/security/helper_functions.dart
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2022 The NAMIB Project Developers. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-//
-// SPDX-License-Identifier: BSD-3-Clause
-
-import 'security_scheme.dart';
-
-/// Parses the fields shared by all [SecurityScheme]s.
-List<String> parseSecurityJson(
-  SecurityScheme securityScheme,
-  Map<String, dynamic> json,
-) {
-  final List<String> parsedJsonFields = ['scheme'];
-
-  final dynamic proxy = json['proxy'];
-  if (proxy is String) {
-    securityScheme.proxy = proxy;
-  }
-
-  final dynamic description = json['description'];
-  if (description is String) {
-    securityScheme.description = description;
-  }
-
-  final dynamic descriptions = json['descriptions'];
-  if (descriptions is Map<String, dynamic>) {
-    for (final entry in descriptions.entries) {
-      final dynamic value = entry.value;
-      if (value is String) {
-        securityScheme.descriptions[entry.key] = value;
-      }
-    }
-  }
-
-  final dynamic jsonLdType = json['@type'];
-  if (jsonLdType is String) {
-    securityScheme.jsonLdType = [jsonLdType];
-  } else if (jsonLdType is List<dynamic>) {
-    securityScheme.jsonLdType =
-        jsonLdType.whereType<String>().toList(growable: false);
-  }
-
-  return parsedJsonFields;
-}
-
-/// Parses additional fields which are not part of the WoT specification.
-void parseAdditionalFields(
-  Map<String, dynamic> additionalFields,
-  Map<String, dynamic> json,
-  List<String> parsedJsonFields,
-) {
-  final additionEntries = json.entries
-      .where((jsonEntry) => !parsedJsonFields.contains(jsonEntry.key));
-  additionalFields.addEntries(additionEntries);
-}
diff --git a/lib/src/definitions/security/no_security_scheme.dart b/lib/src/definitions/security/no_security_scheme.dart
index 9285c10a..0eaff994 100644
--- a/lib/src/definitions/security/no_security_scheme.dart
+++ b/lib/src/definitions/security/no_security_scheme.dart
@@ -4,7 +4,6 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
 import 'security_scheme.dart';
 
 /// A security configuration corresponding to identified by the Vocabulary Term
@@ -12,12 +11,9 @@ import 'security_scheme.dart';
 class NoSecurityScheme extends SecurityScheme {
   /// Creates a [NoSecurityScheme] from a [json] object.
   NoSecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, {});
   }
 
   @override
   String get scheme => 'nosec';
-
-  final List<String> _parsedJsonFields = [];
 }
diff --git a/lib/src/definitions/security/oauth2_security_scheme.dart b/lib/src/definitions/security/oauth2_security_scheme.dart
index 18030cf5..9f713e09 100644
--- a/lib/src/definitions/security/oauth2_security_scheme.dart
+++ b/lib/src/definitions/security/oauth2_security_scheme.dart
@@ -4,8 +4,7 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import '../validation/validation_exception.dart';
-import 'helper_functions.dart';
+import '../extensions/json_parser.dart';
 import 'security_scheme.dart';
 
 /// OAuth 2.0 authentication security configuration for systems conformant with
@@ -28,44 +27,15 @@ class OAuth2SecurityScheme extends SecurityScheme {
 
   /// Creates a [OAuth2SecurityScheme] from a [json] object.
   OAuth2SecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
+    final Set<String> parsedFields = {};
 
-    final dynamic jsonAuthorization = _getJsonValue(json, 'authorization');
-    if (jsonAuthorization is String) {
-      authorization = jsonAuthorization;
-      _parsedJsonFields.add('authorization');
-    }
+    authorization = json.parseField<String>('authorization', parsedFields);
+    token = json.parseField<String>('token', parsedFields);
+    refresh = json.parseField<String>('refresh', parsedFields);
+    scopes = json.parseArrayField<String>('scopes', parsedFields);
+    flow = json.parseRequiredField<String>('flow', parsedFields);
 
-    final dynamic jsonToken = _getJsonValue(json, 'token');
-    if (jsonToken is String) {
-      token = jsonToken;
-      _parsedJsonFields.add('token');
-    }
-
-    final dynamic jsonRefresh = _getJsonValue(json, 'refresh');
-    if (jsonRefresh is String) {
-      refresh = jsonRefresh;
-      _parsedJsonFields.add('refresh');
-    }
-
-    final dynamic jsonScopes = _getJsonValue(json, 'scopes');
-    if (jsonScopes is String) {
-      scopes = [jsonScopes];
-      _parsedJsonFields.add('scopes');
-    } else if (jsonScopes is List<dynamic>) {
-      scopes = jsonScopes.whereType<String>().toList(growable: false);
-      _parsedJsonFields.add('scopes');
-    }
-
-    final dynamic jsonFlow = _getJsonValue(json, 'flow');
-    if (jsonFlow is String) {
-      flow = jsonFlow;
-      _parsedJsonFields.add('flow');
-    } else {
-      throw ValidationException("flow must be of type 'string'!");
-    }
-
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, parsedFields);
   }
   @override
   String get scheme => 'oauth2';
@@ -74,13 +44,13 @@ class OAuth2SecurityScheme extends SecurityScheme {
   ///
   /// In the case of the `device` flow, the URI provided for the [authorization]
   /// value refers to the device authorization endpoint.
-  String? authorization;
+  late String? authorization;
 
   /// URI of the token server.
-  String? token;
+  late String? token;
 
   /// URI of the authorization server.
-  String? refresh;
+  late String? refresh;
 
   /// Set of authorization scope identifiers provided as an array.
   ///
@@ -88,15 +58,8 @@ class OAuth2SecurityScheme extends SecurityScheme {
   /// associated with forms in order to identify what resources a client may
   /// access and how. The values associated with a form should be chosen from
   /// those defined in an [OAuth2SecurityScheme] active on that form.
-  List<String>? scopes;
+  late List<String>? scopes;
 
   /// Authorization flow.
   late String flow;
-
-  final List<String> _parsedJsonFields = [];
-
-  dynamic _getJsonValue(Map<String, dynamic> json, String key) {
-    _parsedJsonFields.add(key);
-    return json[key];
-  }
 }
diff --git a/lib/src/definitions/security/psk_security_scheme.dart b/lib/src/definitions/security/psk_security_scheme.dart
index f298a038..1e715b3a 100644
--- a/lib/src/definitions/security/psk_security_scheme.dart
+++ b/lib/src/definitions/security/psk_security_scheme.dart
@@ -4,7 +4,7 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
-import 'helper_functions.dart';
+import '../extensions/json_parser.dart';
 import 'security_scheme.dart';
 
 /// Pre-shared key authentication security configuration identified by the
@@ -24,15 +24,11 @@ class PskSecurityScheme extends SecurityScheme {
 
   /// Creates a [PskSecurityScheme] from a [json] object.
   PskSecurityScheme.fromJson(Map<String, dynamic> json) {
-    _parsedJsonFields.addAll(parseSecurityJson(this, json));
+    final Set<String> parsedFields = {};
 
-    final dynamic jsonIdentity = _getJsonValue(json, 'identity');
-    if (jsonIdentity is String) {
-      identity = jsonIdentity;
-      _parsedJsonFields.add('identity');
-    }
+    identity = json.parseField<String>('identity');
 
-    parseAdditionalFields(additionalFields, json, _parsedJsonFields);
+    parseSecurityJson(json, parsedFields);
   }
 
   @override
@@ -40,11 +36,4 @@ class PskSecurityScheme extends SecurityScheme {
 
   /// Name for query, header, cookie, or uri parameters.
   String? identity;
-
-  final List<String> _parsedJsonFields = [];
-
-  dynamic _getJsonValue(Map<String, dynamic> json, String key) {
-    _parsedJsonFields.add(key);
-    return json[key];
-  }
 }
diff --git a/lib/src/definitions/security/security_scheme.dart b/lib/src/definitions/security/security_scheme.dart
index f6983365..d01774ee 100644
--- a/lib/src/definitions/security/security_scheme.dart
+++ b/lib/src/definitions/security/security_scheme.dart
@@ -4,9 +4,29 @@
 //
 // SPDX-License-Identifier: BSD-3-Clause
 
+import '../extensions/json_parser.dart';
+import 'ace_security_scheme.dart';
+import 'apikey_security_scheme.dart';
+import 'auto_security_scheme.dart';
+import 'basic_security_scheme.dart';
+import 'bearer_security_scheme.dart';
+import 'digest_security_scheme.dart';
+import 'no_security_scheme.dart';
+import 'oauth2_security_scheme.dart';
+import 'psk_security_scheme.dart';
+
 /// Class that contains metadata describing the configuration of a security
 /// mechanism.
 abstract class SecurityScheme {
+  /// Constructor.
+  SecurityScheme({
+    this.description,
+    this.proxy,
+    Map<String, String>? descriptions,
+  }) {
+    this.descriptions.addAll(descriptions ?? {});
+  }
+
   /// The actual security [scheme] identifier.
   ///
   /// Can be one of `nosec`, `combo`, `basic`, `digest`, `bearer`, `psk`,
@@ -17,7 +37,7 @@ abstract class SecurityScheme {
   String? description;
 
   /// A [Map] of multi-language [descriptions].
-  final Map<String, String> descriptions = {};
+  Map<String, String> descriptions = {};
 
   ///
   String? proxy;
@@ -27,4 +47,48 @@ abstract class SecurityScheme {
 
   /// Additional fields collected during the parsing of a JSON object.
   final Map<String, dynamic> additionalFields = <String, dynamic>{};
+
+  /// Parses the fields shared by all [SecurityScheme]s.
+  void parseSecurityJson(
+    Map<String, dynamic> json,
+    Set<String> parsedFields,
+  ) {
+    parsedFields.add('scheme');
+
+    proxy = json.parseField<String>('proxy', parsedFields);
+    description = json.parseField<String>('description', parsedFields);
+    descriptions
+        .addAll(json.parseMapField<String>('descriptions', parsedFields) ?? {});
+    jsonLdType = json.parseArrayField<String>('@type');
+
+    additionalFields.addEntries(
+      json.entries.where((jsonEntry) => !parsedFields.contains(jsonEntry.key)),
+    );
+  }
+
+  /// Creates a [SecurityScheme] from a [json] object.
+  static SecurityScheme? fromJson(Map<String, dynamic> json) {
+    switch (json['scheme']) {
+      case 'auto':
+        return AutoSecurityScheme.fromJson(json);
+      case 'basic':
+        return BasicSecurityScheme.fromJson(json);
+      case 'bearer':
+        return BearerSecurityScheme.fromJson(json);
+      case 'nosec':
+        return NoSecurityScheme.fromJson(json);
+      case 'psk':
+        return PskSecurityScheme.fromJson(json);
+      case 'digest':
+        return DigestSecurityScheme.fromJson(json);
+      case 'apikey':
+        return ApiKeySecurityScheme.fromJson(json);
+      case 'oauth2':
+        return OAuth2SecurityScheme.fromJson(json);
+      case 'ace:ACESecurityScheme':
+        return AceSecurityScheme.fromJson(json);
+    }
+
+    return null;
+  }
 }
diff --git a/lib/src/definitions/thing_description.dart b/lib/src/definitions/thing_description.dart
index 1301db33..aeed2825 100644
--- a/lib/src/definitions/thing_description.dart
+++ b/lib/src/definitions/thing_description.dart
@@ -9,29 +9,14 @@ import 'dart:convert';
 import 'package:curie/curie.dart';
 
 import 'context_entry.dart';
+import 'extensions/json_parser.dart';
 import 'interaction_affordances/action.dart';
 import 'interaction_affordances/event.dart';
 import 'interaction_affordances/property.dart';
 import 'link.dart';
-import 'security/ace_security_scheme.dart';
-import 'security/apikey_security_scheme.dart';
-import 'security/auto_security_scheme.dart';
-import 'security/basic_security_scheme.dart';
-import 'security/bearer_security_scheme.dart';
-import 'security/digest_security_scheme.dart';
-import 'security/no_security_scheme.dart';
-import 'security/oauth2_security_scheme.dart';
-import 'security/psk_security_scheme.dart';
 import 'security/security_scheme.dart';
 import 'thing_model.dart';
 import 'validation/thing_description_schema.dart';
-import 'validation/validation_exception.dart';
-
-const _validContextValues = [
-  'https://www.w3.org/2019/wot/td/v1',
-  'https://www.w3.org/2022/wot/td/v1.1',
-  'http://www.w3.org/ns/td'
-];
 
 /// Represents a WoT Thing Description
 class ThingDescription {
@@ -140,36 +125,27 @@ class ThingDescription {
   }
 
   void _parseJson(Map<String, dynamic> json) {
-    _parseTitle(json['title']);
-    _parseContext(json['@context']);
-    final dynamic id = json['id'];
-    if (id is String) {
-      this.id = id;
-    }
-    final dynamic base = json['base'];
-    if (base is String) {
-      this.base = Uri.parse(base);
-    }
-    final dynamic description = json['description'];
-    if (description is String) {
-      this.description = description;
-    }
-    _parseMultilangString(titles, json, 'titles');
-    _parseMultilangString(descriptions, json, 'descriptions');
-    final dynamic security = json['security'];
-    if (security is List<dynamic>) {
-      this.security.addAll(security.whereType<String>());
-    } else if (security is String) {
-      this.security.add(security);
-    }
+    // TODO: Move to constructor?
+    final Set<String> parsedFields = {};
+
+    context.addAll(ContextEntry.parseContext(json['@context'], prefixMapping));
+    title = json.parseRequiredField<String>('title', parsedFields);
+    titles.addAll(json.parseMapField<String>('titles', parsedFields) ?? {});
+    description = json.parseField<String>('description', parsedFields);
+    descriptions
+        .addAll(json.parseMapField<String>('descriptions', parsedFields) ?? {});
+    id = json.parseField<String>('id', parsedFields);
+    base = Uri.tryParse(json.parseField<String>('base', parsedFields) ?? '');
+    security
+        .addAll(json.parseArrayField<String>('security', parsedFields) ?? []);
+
     final dynamic securityDefinitions = json['securityDefinitions'];
     if (securityDefinitions is Map<String, dynamic>) {
       _parseSecurityDefinitions(securityDefinitions);
     }
-    final dynamic jsonUriVariables = json['uriVariables'];
-    if (jsonUriVariables is Map<String, dynamic>) {
-      uriVariables = jsonUriVariables;
-    }
+
+    uriVariables = json.parseMapField<dynamic>('uriVariables');
+
     final dynamic properties = json['properties'];
     if (properties is Map<String, dynamic>) {
       _parseProperties(properties);
@@ -188,72 +164,6 @@ class ThingDescription {
     }
   }
 
-  // TODO(JKRhb): Refactor
-  void _parseMultilangString(
-    Map<String, String> field,
-    Map<String, dynamic> json,
-    String jsonKey,
-  ) {
-    final dynamic jsonEntries = json[jsonKey];
-    if (jsonEntries is Map<String, dynamic>) {
-      for (final entry in jsonEntries.entries) {
-        final dynamic value = entry.value;
-        if (value is String) {
-          field[entry.key] = value;
-        }
-      }
-    }
-  }
-
-  void _parseTitle(dynamic titleJson) {
-    if (titleJson is String) {
-      title = titleJson;
-    } else {
-      throw ValidationException(
-        'Thing Description type is not a '
-        'String but ${title.runtimeType}',
-      );
-    }
-  }
-
-  void _parseContext(dynamic contextJson) {
-    if (contextJson is String || contextJson is Map<String, dynamic>) {
-      _parseContextListEntry(contextJson);
-    } else if (contextJson is List<dynamic>) {
-      var firstEntry = true;
-      for (final contextEntry in contextJson) {
-        _parseContextListEntry(contextEntry, firstEntry: firstEntry);
-        if (contextEntry is String &&
-            _validContextValues.contains(contextEntry)) {
-          firstEntry = false;
-        }
-      }
-    }
-  }
-
-  void _parseContextListEntry(
-    dynamic contextJsonListEntry, {
-    bool firstEntry = false,
-  }) {
-    if (contextJsonListEntry is String) {
-      context.add(ContextEntry(contextJsonListEntry, null));
-      if (firstEntry && _validContextValues.contains(contextJsonListEntry)) {
-        prefixMapping.defaultPrefixValue = contextJsonListEntry;
-      }
-    } else if (contextJsonListEntry is Map<String, dynamic>) {
-      for (final mapEntry in contextJsonListEntry.entries) {
-        final dynamic value = mapEntry.value;
-        final key = mapEntry.key;
-        if (value is String) {
-          context.add(ContextEntry(value, key));
-          if (!key.startsWith('@') && Uri.tryParse(value) != null) {
-            prefixMapping.addPrefix(key, value);
-          }
-        }
-      }
-    }
-  }
-
   void _parseLinks(List<dynamic> json) {
     for (final link in json) {
       if (link is Map<String, dynamic>) {
@@ -294,57 +204,10 @@ class ThingDescription {
     for (final securityDefinition in json.entries) {
       final dynamic value = securityDefinition.value;
       if (value is Map<String, dynamic>) {
-        SecurityScheme securityScheme;
-        switch (value['scheme']) {
-          case 'auto':
-            {
-              securityScheme = AutoSecurityScheme.fromJson(value);
-              break;
-            }
-          case 'basic':
-            {
-              securityScheme = BasicSecurityScheme.fromJson(value);
-              break;
-            }
-          case 'bearer':
-            {
-              securityScheme = BearerSecurityScheme.fromJson(value);
-              break;
-            }
-          case 'nosec':
-            {
-              securityScheme = NoSecurityScheme.fromJson(value);
-              break;
-            }
-          case 'psk':
-            {
-              securityScheme = PskSecurityScheme.fromJson(value);
-              break;
-            }
-          case 'digest':
-            {
-              securityScheme = DigestSecurityScheme.fromJson(value);
-              break;
-            }
-          case 'apikey':
-            {
-              securityScheme = ApiKeySecurityScheme.fromJson(value);
-              break;
-            }
-          case 'oauth2':
-            {
-              securityScheme = OAuth2SecurityScheme.fromJson(value);
-              break;
-            }
-          case 'ace:ACESecurityScheme':
-            {
-              securityScheme = AceSecurityScheme.fromJson(value);
-              break;
-            }
-          default:
-            continue;
+        final securityScheme = SecurityScheme.fromJson(value);
+        if (securityScheme != null) {
+          securityDefinitions[securityDefinition.key] = securityScheme;
         }
-        securityDefinitions[securityDefinition.key] = securityScheme;
       }
     }
   }
diff --git a/test/core/dart_wot_test.dart b/test/core/dart_wot_test.dart
index f08f57fa..9e4fcf99 100644
--- a/test/core/dart_wot_test.dart
+++ b/test/core/dart_wot_test.dart
@@ -20,6 +20,7 @@ void main() {
       final servient = Servient();
       final wot = await servient.start();
       final Map<String, dynamic> exposedThingInit = <String, dynamic>{
+        '@context': 'https://www.w3.org/2022/wot/td/v1.1',
         'title': 'Test Thing'
       };
       final dynamic exposedThing = await wot.produce(exposedThingInit);
@@ -46,7 +47,7 @@ void main() {
             "href": "https://example.org",
             "rel": "test",
             "anchor": "https://example.org",
-            "type": "test",
+            "@type": "test",
             "sizes": "42",
             "test": "test",
             "hreflang": "de"
diff --git a/test/core/definitions_test.dart b/test/core/definitions_test.dart
index 96517a80..7a706bd4 100644
--- a/test/core/definitions_test.dart
+++ b/test/core/definitions_test.dart
@@ -10,11 +10,19 @@ import 'package:dart_wot/dart_wot.dart';
 import 'package:dart_wot/src/definitions/additional_expected_response.dart';
 import 'package:dart_wot/src/definitions/context_entry.dart';
 import 'package:dart_wot/src/definitions/expected_response.dart';
+import 'package:dart_wot/src/definitions/interaction_affordances/interaction_affordance.dart';
 import 'package:dart_wot/src/definitions/interaction_affordances/property.dart';
 import 'package:dart_wot/src/definitions/operation_type.dart';
+import 'package:dart_wot/src/definitions/security/auto_security_scheme.dart';
+import 'package:dart_wot/src/definitions/security/no_security_scheme.dart';
 import 'package:dart_wot/src/definitions/validation/thing_description_schema.dart';
+import 'package:dart_wot/src/definitions/validation/validation_exception.dart';
 import 'package:test/test.dart';
 
+class _InvalidInteractionAffordance extends InteractionAffordance {
+  _InvalidInteractionAffordance(super.forms, super.thingDescription);
+}
+
 void main() {
   group('Definitions', () {
     setUp(() {
@@ -182,6 +190,14 @@ void main() {
 
       expect(additionalResponse2.contentType, 'text/plain');
       expect(additionalResponse2.schema, null);
+
+      expect(
+        () => Form(
+          Uri.parse('http://example.org'),
+          _InvalidInteractionAffordance([], thingDescription),
+        ),
+        throwsStateError,
+      );
     });
 
     test('should correctly parse actions', () {
@@ -228,13 +244,24 @@ void main() {
         'title': 'MyLampThing',
         'security': 'nosec_sc',
         'securityDefinitions': {
-          'nosec_sc': {'scheme': 'nosec'}
+          'nosec_sc': {'scheme': 'nosec'},
+          'auto_sc': {'scheme': 'auto'},
         },
         'properties': {
           'property': {
+            'title': 'Test',
+            'titles': {'de': 'German Test', 'en': 'English Test'},
+            'description': 'This is a Test',
+            'descriptions': {
+              'es': 'Esto es una prueba',
+              'en': 'This is a Test'
+            },
             'writeOnly': true,
             'readOnly': true,
             'observable': true,
+            'enum': ['On', 'Off', 3],
+            'constant': 'On',
+            'type': 'string',
             'forms': [
               {'href': 'https://example.org'}
             ]
@@ -243,22 +270,57 @@ void main() {
             'forms': [
               {'href': 'https://example.org'}
             ]
+          },
+          'objectSchemeProperty': {
+            'properties': {
+              'test': {'type': 'string'}
+            },
+            'forms': [
+              {
+                'href': 'https://example.org',
+                'security': 'auto_sc',
+              }
+            ],
           }
         }
       };
 
       final thingDescription = ThingDescription.fromJson(validThingDescription);
 
+      expect(thingDescription.security[0], 'nosec_sc');
+      final noSecurityScheme = thingDescription.securityDefinitions['nosec_sc'];
+      expect(noSecurityScheme, isA<NoSecurityScheme>());
+      expect(noSecurityScheme?.scheme, 'nosec');
+
       final property = thingDescription.properties['property'];
+      expect(property?.title, 'Test');
+      expect(property?.description, 'This is a Test');
+      expect(property?.descriptions?['es'], 'Esto es una prueba');
+      expect(property?.descriptions?['en'], 'This is a Test');
       expect(property?.writeOnly, true);
       expect(property?.readOnly, true);
       expect(property?.observable, true);
+      expect(property?.enumeration, ['On', 'Off', 3]);
+      expect(property?.constant, 'On');
 
       final propertyWithDefaults =
           thingDescription.properties['propertyWithDefaults'];
       expect(propertyWithDefaults?.writeOnly, false);
       expect(propertyWithDefaults?.readOnly, false);
       expect(propertyWithDefaults?.observable, false);
+
+      final objectSchemeProperty =
+          thingDescription.properties['objectSchemeProperty'];
+
+      expect(objectSchemeProperty?.forms[0].security, ['auto_sc']);
+      final autoSecurityScheme =
+          objectSchemeProperty?.forms[0].securityDefinitions[0];
+      expect(autoSecurityScheme, isA<AutoSecurityScheme>());
+      expect(autoSecurityScheme?.scheme, 'auto');
+
+      final testSchema = objectSchemeProperty?.properties?['test'];
+      expect(testSchema, isA<DataSchema>());
+      expect(testSchema?.type, 'string');
     });
   });
 
@@ -327,4 +389,70 @@ void main() {
       'ace:ACESecurityScheme',
     );
   });
+
+  test('Should only parse allowed Operation Types', () {
+    expect(
+      OperationType.fromString('invokeaction'),
+      OperationType.invokeaction,
+    );
+
+    expect(
+      () => OperationType.fromString('test'),
+      throwsA(isA<ValidationException>()),
+    );
+  });
+
+  test('Should correctly parse ExpectedResponse', () {
+    final firstResponse = ExpectedResponse(
+      'application/json',
+      additionalFields: {'test': 'test'},
+    );
+
+    expect(firstResponse.additionalFields['test'], 'test');
+
+    final expectedResponseJson = {
+      'response': {
+        'contentType': 'application/json',
+        'test': 'test',
+      },
+    };
+
+    final secondResponse = ExpectedResponse.fromJson(expectedResponseJson);
+
+    expect(secondResponse, isA<ExpectedResponse>());
+    expect(secondResponse?.additionalFields['test'], 'test');
+  });
+
+  test('Should reject invalid @context entries', () {
+    final invalidThingDescription1 = {
+      '@context': 5,
+      'title': 'Test',
+      'security': 'nosec_sc',
+      'securityDefinitions': {
+        'nosec_sc': {'scheme': 'nosec'}
+      },
+    };
+
+    expect(
+      () => ThingDescription.fromJson(
+        invalidThingDescription1,
+        validate: false,
+      ),
+      throwsA(isA<ValidationException>()),
+    );
+
+    final invalidThingDescription2 = {
+      '@context': ['https://www.w3.org/2022/wot/td/v1.1', 5],
+      'title': 'Test',
+      'security': 'nosec_sc',
+      'securityDefinitions': {
+        'nosec_sc': {'scheme': 'nosec'}
+      },
+    };
+
+    expect(
+      () => ThingDescription.fromJson(invalidThingDescription2),
+      throwsA(isA<ValidationException>()),
+    );
+  });
 }