diff --git a/.gitignore b/.gitignore
index fc6f72e2355847d30f07b61dddbd8fcd335bc342..f87582a8dbebcfc9f9c5b2aaf646b5ca737d4d29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,14 @@
+__pycache__
+.devcontainer
 .dll
+.env
+.pytest_cache
 .so
+.venv
 .vscode
-.pytest_cache
-__pycache__
-generator.log
-*.orig
 *.bkp
-.devcontainer
\ No newline at end of file
+*.orig
+build
+deps
+generator.log
+MODULE.*
diff --git a/NOTICE.md b/NOTICE.md
index 812ea2b8495990a7992c6ab61414b6853340f264..c623d498457ee69dac7734cc1dc381ea634e7644 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -78,6 +78,12 @@ Yase
 units_nhh
  * License: MIT
 
+nlohmann (JSON for Modern C++)
+ * License: MIT License
+ * Homepage: https://nlohmann.github.io/json/
+ * Repository: https://github.com/nlohmann/json
+ * Version: 3.9.1
+
 MantleAPI
  * License: EPL 2.0
 
diff --git a/README.md b/README.md
index c4eebab56e737b841c2557daf6c18b8996a938a4..79ebdf6cbfd0adcef63c5b3359e04f9b4b2324d7 100644
--- a/README.md
+++ b/README.md
@@ -251,17 +251,29 @@ Converts `NET_ASAM_OPENSCENARIO::v1_3::ITrajectoryRef` object type to [mantle_ap
 Converts `NET_ASAM_OPENSCENARIO::v1_3::ITransitionDynamics` object type to [mantle_api::TransitionDynamics](https://gitlab.eclipse.org/eclipse/openpass/mantle-api/-/blob/master/include/MantleAPI/Traffic/control_strategy.h)
 **Note**: Right now the property `followingMode` is not considered.
 
+# Customizations w.r.t ASAM OpenSCENARIO Standard
+
+## Property Parsing
+
+The OpenSCENARIO standard does not impose strict requirements on the format of custom properties, allowing for flexibility in their definition.
+To facilitate this, the OpenScenarioEngine uses JSON to parse string-type properties into an internal object format.
+This approach enables the definition of complex properties, such as distributions, which can be used within actions.
+For example, a velocity distribution might be defined and used in a `TrafficAreaAction`.
+
+For more information on how to define these properties, refer to the documentation an provided schemas in [doc/json_properties](./doc/json_properties).
+
 # Dependencies
 
 | Dependency | Commit | Version | License |
 | ---------- | ------ | ------- | ------- |
+| [CPM](https://github.com/cpm-cmake/CPM.cmake) | 03705fc | 0.36.0 | MIT License |
+| [googletest](https://github.com/google/googletest) | f8d7d77 | 1.14.0 | BSD-3-Clause License |
+| [JSON for Modern C++](https://github.com/nlohmann/json) | db78ac1 | 3.9.1 | MIT License |
 | [MantleAPI](https://gitlab.eclipse.org/eclipse/openpass/mantle-api) | 5541bf1a | 14.0.0 | EPL 2.0 |
+| [openpass/stochastics-library](https://gitlab.eclipse.org/eclipse/openpass/stochastics-library) | 6c9dde71 | 0.11.0 | EPL 2.0 |
 | [OpenSCENARIO API](https://github.com/RA-Consulting-GmbH/openscenario.api.test/) | 5980e88 | 1.4.0 | Apache 2.0 |
-| [YASE](https://gitlab.eclipse.org/eclipse/openpass/yase) | d0c0e58d | | EPL 2.0 |
 | [Units](https://github.com/nholthaus/units) | e27eed9 | 2.3.4 | MIT License |
-| [googletest](https://github.com/google/googletest) | f8d7d77 | 1.14.0 | BSD-3-Clause License |
-| [CPM](https://github.com/cpm-cmake/CPM.cmake) | 03705fc | 0.36.0 | MIT License |
-| [openpass/stochastics-library](https://gitlab.eclipse.org/eclipse/openpass/stochastics-library) | 6c9dde71 | 0.11.0 | EPL 2.0 |
+| [YASE](https://gitlab.eclipse.org/eclipse/openpass/yase) | d0c0e58d | | EPL 2.0 |
 
 # Issues
 
diff --git a/doc/json_properties/exponential_distribution.json b/doc/json_properties/exponential_distribution.json
new file mode 100644
index 0000000000000000000000000000000000000000..1d294b3804a8a4dd95588b3308e67969c7936198
--- /dev/null
+++ b/doc/json_properties/exponential_distribution.json
@@ -0,0 +1,20 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "ExponentialDistribution",
+  "type": "object",
+  "properties": {
+      "lowerLimit": {
+          "type": "number",
+          "description": "Lower limit"
+      },
+      "upperLimit": {
+          "type": "number",
+          "description": "Upper limit"
+      },
+      "expectedValue": {
+          "type": "number",
+          "description": "Reciprocal of the rate parameter lambda"
+      }
+  },
+  "required": ["lowerLimit", "upperLimit", "expectedValue"]
+}
\ No newline at end of file
diff --git a/doc/json_properties/gamma_distribution.json b/doc/json_properties/gamma_distribution.json
new file mode 100644
index 0000000000000000000000000000000000000000..479cb799d5d330b5df429f41d009083c1eb25ed4
--- /dev/null
+++ b/doc/json_properties/gamma_distribution.json
@@ -0,0 +1,28 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "GammaDistribution",
+  "type": "object",
+  "properties": {
+      "lowerLimit": {
+          "type": "number",
+          "description": "Lower limit",
+          "minimum": 0.0
+      },
+      "upperLimit": {
+          "type": "number",
+          "description": "Upper limit",
+          "minimum": 0.0
+      },
+      "expectedValue": {
+          "type": "number",
+          "description": "Expected value (mean) of the distribution, calculated as alpha / beta",
+          "minimum": 0.0
+      },
+      "variance": {
+          "type": "number",
+          "description": "Variance of the distribution, calculated as alpha / beta^2",
+          "minimum": 0.0
+      }
+  },
+  "required": ["lowerLimit", "upperLimit", "expectedValue", "variance"]
+}
\ No newline at end of file
diff --git a/doc/json_properties/lognormal_distribution.json b/doc/json_properties/lognormal_distribution.json
new file mode 100644
index 0000000000000000000000000000000000000000..1d294b3804a8a4dd95588b3308e67969c7936198
--- /dev/null
+++ b/doc/json_properties/lognormal_distribution.json
@@ -0,0 +1,20 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "ExponentialDistribution",
+  "type": "object",
+  "properties": {
+      "lowerLimit": {
+          "type": "number",
+          "description": "Lower limit"
+      },
+      "upperLimit": {
+          "type": "number",
+          "description": "Upper limit"
+      },
+      "expectedValue": {
+          "type": "number",
+          "description": "Reciprocal of the rate parameter lambda"
+      }
+  },
+  "required": ["lowerLimit", "upperLimit", "expectedValue"]
+}
\ No newline at end of file
diff --git a/doc/json_properties/normal_distrubution.json b/doc/json_properties/normal_distrubution.json
new file mode 100644
index 0000000000000000000000000000000000000000..3021ae8e4df0ccd86bc1f7e027bd4fbd8f6aae36
--- /dev/null
+++ b/doc/json_properties/normal_distrubution.json
@@ -0,0 +1,28 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "NormalDistribution",
+  "type": "object",
+  "properties": {
+      "lowerLimit": {
+          "type": "number",
+          "description": "Lower limit",
+          "minimum": 0.0
+      },
+      "upperLimit": {
+          "type": "number",
+          "description": "Upper limit",
+          "minimum": 0.0
+      },
+      "expectedValue": {
+          "type": "number",
+          "description": "Expected value",
+          "minimum": 0.0
+      },
+      "variance": {
+          "type": "number",
+          "description": "Variance (square of standard deviation)",
+          "minimum": 0.0
+      }
+  },
+  "required": ["lowerLimit", "upperLimit", "expectedValue", "variance"]
+}
\ No newline at end of file
diff --git a/doc/json_properties/readme.md b/doc/json_properties/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..6e9be80b292b999c99babe3b973addd24e764024
--- /dev/null
+++ b/doc/json_properties/readme.md
@@ -0,0 +1,40 @@
+# JSON Property Strings
+
+The OpenSCENARIO standard does not impose strict requirements on the format of custom properties, allowing for flexibility in their definition.
+To facilitate this, the OpenScenarioEngine uses JSON to parse string-type properties into an internal object format.
+This approach enables the definition of complex properties, such as distributions, which can be used within actions.
+For example, a velocity distribution might be defined and used in a `TrafficAreaAction`.
+
+This folder contains JSON schemas for property strings used by the **OpenScenarioEngine**.
+While these schemas are not enforced, they serve as a reference for how to properly define properties, such as distributions, in a way that they are correctly parsed.
+
+## General Usage
+
+```xml
+ <Property name="SomeProp" value="{ 'type': 'InterpretableType', 'value': 'InterpretableString'" />
+```
+
+## Available Distribution Types
+
+- [ExponentialDistribution](./exponential_distribution.json)
+- [ǸormalDistribution](./normal_distribution.json)
+- [GammaDistribution](./gamma_distribution.json)
+- [LogNormalDistribution](./lognormal_distribution.json)
+- [UniformDistribution](./uniform_distribution.json)
+
+**Example**
+
+```xml
+ <Property
+   name="velocity"
+   value="{
+            'type': 'NormalDistribution',
+            'value':
+            {
+              'lowerLimit': 19.265,
+              'upperLimit': 43.685,
+              'expectedValue': 31.475,
+              'variance': 37.271025
+            }
+          }" />
+```
diff --git a/doc/json_properties/uniform_distribution.json b/doc/json_properties/uniform_distribution.json
new file mode 100644
index 0000000000000000000000000000000000000000..1532d5e29bd2d66f9669b75510632687cc2574f9
--- /dev/null
+++ b/doc/json_properties/uniform_distribution.json
@@ -0,0 +1,18 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "UniformDistribution",
+  "type": "object",
+  "properties": {
+      "lowerLimit": {
+          "type": "number",
+          "description": "Lower limit",
+          "minimum": 0.0
+      },
+      "upperLimit": {
+          "type": "number",
+          "description": "Upper limit",
+          "minimum": 0.0
+      }
+  },
+  "required": ["lowerLimit", "upperLimit"]
+}
\ No newline at end of file
diff --git a/engine/.gitignore b/engine/.gitignore
index 193cf2701b37526f5038785366ad7fa9ef81e512..f3febcbe90eae591aecd37cb396dffba711ee955 100644
--- a/engine/.gitignore
+++ b/engine/.gitignore
@@ -4,3 +4,4 @@
 build
 bin
 bazel-*
+external
diff --git a/engine/BUILD.bazel b/engine/BUILD.bazel
index cbc396a17cf882360e9f72672fcad7fd57c626e9..b3f3450b48b16ffebdf936210318161a8f712e6a 100644
--- a/engine/BUILD.bazel
+++ b/engine/BUILD.bazel
@@ -28,6 +28,7 @@ cc_library(
     ],
     visibility = ["//visibility:public"],
     deps = [
+        "@//third_party/nlohmann_json",
         "@mantle_api",
         "@open_scenario_parser",
         "@stochastics_library",
@@ -178,9 +179,10 @@ cc_test(
     data = [":open_scenario_engine_test_data"],
     tags = ["test"],
     deps = [
-        ":test_utils",
         ":open_scenario_engine",
         ":open_scenario_engine_test_utils",
+        ":test_utils",
+        "@//third_party/nlohmann_json",
         "@googletest//:gtest_main",
         "@mantle_api//:test_utils",
     ],
diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt
index 72cdb526066e9a2acdf6761fd0c7abbdf8132bf3..74ad6e9656f6f7b393b939cba894f7b0d566e291 100644
--- a/engine/CMakeLists.txt
+++ b/engine/CMakeLists.txt
@@ -23,11 +23,11 @@ if(NOT CMAKE_BUILD_TYPE)
     set(CMAKE_BUILD_TYPE Release)
 endif()
 
-# YASE
-find_package(Yase REQUIRED)
-
-# units https://github.com/nholthaus/units (dependency of MantleAPI)
+find_package(MantleAPI REQUIRED)
+find_package(nlohmann_json REQUIRED)
+find_package(Stochastics REQUIRED)
 find_package(units REQUIRED)
+find_package(Yase REQUIRED)
 
 # googlemock
 include(CPM)
@@ -39,12 +39,6 @@ CPMAddPackage(
   OPTIONS "INSTALL_GTEST OFF" "gtest_force_shared_crt ON"
 )
 
-# MantleAPI https://gitlab.eclipse.org/eclipse/simopenpass/scenario_api
-find_package(MantleAPI REQUIRED)
-
-
-find_package(Stochastics REQUIRED)
-
 # see https://stackoverflow.com/a/58495612
 set(CMAKE_INSTALL_RPATH $ORIGIN)
 
@@ -70,12 +64,17 @@ target_include_directories(
             $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
 )
 
-target_link_libraries(${PROJECT_NAME} PUBLIC openscenario_api::shared
-                                             antlr4_runtime::shared
-                                             Yase::agnostic_behavior_tree
-                                             units::units
-                                             MantleAPI::MantleAPI
-                                      PRIVATE Stochastics::Stochastics)
+target_link_libraries(${PROJECT_NAME}
+  PUBLIC
+    antlr4_runtime::shared
+    MantleAPI::MantleAPI
+    units::units
+    openscenario_api::shared
+  PRIVATE
+    nlohmann_json::nlohmann_json
+    Stochastics::Stochastics
+    Yase::agnostic_behavior_tree
+)
 
 if(USE_CCACHE)
   find_program(CCACHE_FOUND ccache)
@@ -224,9 +223,12 @@ target_sources(
           ${CMAKE_CURRENT_LIST_DIR}/tests/TrafficSignalParsing/TreeGenerationTest.cpp
           ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/ConstantsTest.cpp
           ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/ControllerCreatorTest.cpp
+          ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/DistributionTest.cpp
           ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/EllipseTest.cpp
           ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/EntityCreatorTest.cpp
           ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/EntityUtilsTest.cpp
+          ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/JsonParsingTest.cpp
+          ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/PropertyInterpreterTest.cpp
 )
 
 add_custom_command(TARGET ${PROJECT_NAME}Test
@@ -250,11 +252,14 @@ target_include_directories(
 
 target_link_libraries(
   ${PROJECT_NAME}Test
-  PRIVATE ${PROJECT_NAME}
-          Stochastics::Stochastics
-          antlr4_runtime::shared
-          GTest::gmock_main
-          pthread
+  PRIVATE
+    ${PROJECT_NAME}
+    antlr4_runtime::shared
+    GTest::gmock_main
+    nlohmann_json::nlohmann_json
+    pthread
+    Stochastics::Stochastics
+    Yase::agnostic_behavior_tree
 )
 
 add_test(NAME ${PROJECT_NAME}Test COMMAND ${PROJECT_NAME}Test)
diff --git a/engine/WORKSPACE b/engine/WORKSPACE
index 91062dc5d72b33746b7bffd6b379bac978ff168f..5622781fa20638e25e6480b7cc104a0ecc8967c4 100644
--- a/engine/WORKSPACE
+++ b/engine/WORKSPACE
@@ -1,13 +1,5 @@
 workspace(name = "open_scenario_engine")
 
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-load("//third_party:deps.bzl", "osc_engine_deps")
+load("//third_party:dependencies.bzl", "third_party_deps")
 
-http_archive(
-    name = "googletest",
-    sha256 = "81964fe578e9bd7c94dfdb09c8e4d6e6759e19967e397dbea48d1c10e45d0df2",
-    strip_prefix = "googletest-release-1.12.1",
-    url = "https://github.com/google/googletest/archive/refs/tags/release-1.12.1.tar.gz",
-)
-
-osc_engine_deps()
+third_party_deps()
diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake
index 6644cd17f76486878a73b74112c053f759d08b2b..be6db1011e9ef42c114525cbe3f664527f5be9cb 100644
--- a/engine/cmake/generated_files.cmake
+++ b/engine/cmake/generated_files.cmake
@@ -242,6 +242,9 @@ list(APPEND ${PROJECT_NAME}_SOURCES
     src/Utils/EntityUtils.cpp
     src/Utils/EventPrioritizer.cpp
     src/Utils/Logger.cpp
+    src/Utils/PropertyInterpretation/DistributionInterpreter.cpp
+    src/Utils/PropertyInterpreter.cpp
+    src/Utils/JsonParsing.cpp
     src/Utils/TrafficSignalBuilder.cpp
 )
 
@@ -610,5 +613,17 @@ list(APPEND ${PROJECT_NAME}_HEADERS
     src/Utils/IControllerService.h
     src/Utils/IEventPrioritizer.h
     src/Utils/Logger.h
+    src/Utils/PropertyInterpretation/DistributionInterpreter.h
+    src/Utils/PropertyInterpretation/Distributions.h
+    src/Utils/PropertyInterpretation/Distributions/ExponentialDistribution.h
+    src/Utils/PropertyInterpretation/Distributions/GammaDistribution.h
+    src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h
+    src/Utils/PropertyInterpretation/Distributions/NormalDistribution.h
+    src/Utils/PropertyInterpretation/Distributions/UniformDistribution.h
+    src/Utils/PropertyInterpretation/Interpreter.h
+    src/Utils/PropertyInterpretation/JsonInterpreter.h
+    src/Utils/PropertyInterpretation/RawValueRelay.h
+    src/Utils/PropertyInterpreter.h
+    src/Utils/JsonParsing.h
     src/Utils/TrafficSignalBuilder.h
 )
diff --git a/engine/src/Utils/JsonParsing.cpp b/engine/src/Utils/JsonParsing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc26d3b5dd6a9e7eb92429a6315ce0f21be7fd64
--- /dev/null
+++ b/engine/src/Utils/JsonParsing.cpp
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#include "JsonParsing.h"
+
+#include <regex>
+
+namespace OpenScenarioEngine::v1_3
+{
+
+namespace detail
+{
+
+/// Replace all occurrences of a string in a string
+/// @param input   String to search in
+/// @param search  String to search for
+/// @param replace String to replace with
+/// @return String with all occurrences of search replaced with replace
+std::string Replace(std::string_view input, const std::string& search, const std::string& replace)
+{
+  std::regex search_regex(search);
+  return std::regex_replace(std::string(input), search_regex, replace);
+}
+
+}  // namespace detail
+
+namespace json
+{
+
+std::optional<nlohmann::json> TryParse(std::string_view property)
+{
+  if (auto j = nlohmann::json::parse(std::string(property), nullptr, false);
+      !j.is_discarded())
+  {
+    return j;
+  }
+
+  using namespace std::string_literals;
+  for (const std::string& search : {"&quot;"s, "'"s})
+  {
+    const auto modified_property = detail::Replace(property, search, "\"");
+    if (auto j = nlohmann::json::parse(modified_property, nullptr, false);
+        !j.is_discarded())
+    {
+      return j;
+    }
+  }
+
+  return std::nullopt;
+}
+
+}  // namespace json
+}  // namespace OpenScenarioEngine::v1_3
diff --git a/engine/src/Utils/JsonParsing.h b/engine/src/Utils/JsonParsing.h
new file mode 100644
index 0000000000000000000000000000000000000000..99435d4e2e01455ccc7fd8de1cb8fd881fa56382
--- /dev/null
+++ b/engine/src/Utils/JsonParsing.h
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+#include <optional>
+#include <regex>
+#include <string>
+#include <string_view>
+
+#include "Utils/Logger.h"
+
+namespace OpenScenarioEngine::v1_3
+{
+
+namespace json
+{
+
+/// Try to parse a JSON object from a string
+///
+/// In a first attempt, this function tries to parse the string as it is.
+/// But as the main use case for the parser is interpreting XML properties
+/// as JSON objects and the JSON standard requires double quotes for strings,
+/// the XML parser will try to escape double quotes in the XML file using &quot;
+/// or try with single quotes (not JSON standard, but we support it).
+/// @param value Value to be parsed
+/// @return Parsed JSON object or nullopt if parsing failed
+std::optional<nlohmann::json> TryParse(std::string_view value);
+
+/// Deserialize a string into a object using nlohmann::json
+/// @tparam DeserializedType  Type of the object to deserialize (e.g. a distribution)
+/// @param value           String to deserialize
+/// @return Deserialized object of type T or empty optional if deserialization failed
+template <typename DeserializedType>
+std::optional<DeserializedType> Deserialize(std::string_view value)
+{
+  try
+  {
+    if (auto j = TryParse(value))
+    {
+      return j->get<DeserializedType>();
+    }
+    Logger::Error("JsonParser: \'" + std::string(value) + "' is not a valid JSON object");
+  }
+  catch (const nlohmann::json::out_of_range&)
+  {
+    Logger::Error("JsonParser: \'" + std::string(value) + "' JSON object does not match expected object");
+  }
+
+  return std::nullopt;
+}
+
+}  // namespace json
+
+}  // namespace OpenScenarioEngine::v1_3
diff --git a/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.cpp b/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..479a44cc6217ccfaee1128788eac7632774e397c
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.cpp
@@ -0,0 +1,65 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#include "DistributionInterpreter.h"
+
+#include <Stochastics/StochasticsInterface.h>
+
+#include <algorithm>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+double sample(StochasticsInterface &stochastics, const ExponentialDistribution &distribution)
+{
+  return std::clamp(
+      stochastics.GetExponentialDistributed(
+          distribution.expectedValue != 0.0 ? 1.0 / distribution.expectedValue : 0.0),
+      distribution.lowerLimit,
+      distribution.upperLimit);
+}
+
+double sample(StochasticsInterface &stochastics, const GammaDistribution &distribution)
+{
+  return std::clamp(
+      stochastics.GetGammaDistributed(
+          distribution.expectedValue, std::sqrt(std::abs(distribution.variance))),
+      distribution.lowerLimit,
+      distribution.upperLimit);
+}
+
+double sample(StochasticsInterface &stochastics, const LogNormalDistribution &distribution)
+{
+  return std::clamp(
+      stochastics.GetLogNormalDistributed(
+          distribution.expectedValue, std::sqrt(std::abs(distribution.variance))),
+      distribution.lowerLimit,
+      distribution.upperLimit);
+}
+
+double sample(StochasticsInterface &stochastics, const NormalDistribution &distribution)
+{
+  return std::clamp(
+      stochastics.GetNormalDistributed(
+          distribution.expectedValue, std::sqrt(std::abs(distribution.variance))),
+      distribution.lowerLimit,
+      distribution.upperLimit);
+}
+
+double sample(StochasticsInterface &stochastics, const UniformDistribution &distribution)
+{
+  return std::clamp(
+      stochastics.GetUniformDistributed(
+          distribution.lowerLimit, distribution.upperLimit),
+      distribution.lowerLimit,
+      distribution.upperLimit);
+}
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
\ No newline at end of file
diff --git a/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.h b/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.h
new file mode 100644
index 0000000000000000000000000000000000000000..7b16b8361a5e0bbe960c22c895e72718c0494ca3
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.h
@@ -0,0 +1,104 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#pragma once
+
+#include "Utils/Logger.h"
+#include "Utils/PropertyInterpretation/Distributions.h"
+#include "Utils/PropertyInterpretation/JsonInterpreter.h"
+
+class StochasticsInterface;
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Sample from an exponential distribution
+/// @param stochastics  Stochastics interface
+/// @param distribution Definition of the distribution
+/// @return sampled value
+double sample(StochasticsInterface &stochastics, const ExponentialDistribution &distribution);
+
+/// Sample from a gamma distribution
+/// @param stochastics  Stochastics interface
+/// @param distribution Definition of the distribution
+/// @return sampled value
+double sample(StochasticsInterface &stochastics, const GammaDistribution &distribution);
+
+/// Sample from a log normal distribution
+/// @param stochastics  Stochastics interface
+/// @param distribution Definition of the distribution
+/// @return sampled value
+double sample(StochasticsInterface &stochastics, const LogNormalDistribution &distribution);
+
+/// Sample from a normal distribution
+/// @param stochastics  Stochastics interface
+/// @param distribution Definition of the distribution
+/// @return sampled value
+double sample(StochasticsInterface &stochastics, const NormalDistribution &distribution);
+
+/// Sample from a uniform distribution
+/// @param stochastics  Stochastics interface
+/// @param distribution Definition of the distribution
+/// @return sampled value
+double sample(StochasticsInterface &stochastics, const UniformDistribution &distribution);
+
+/// Interpreter for distributions
+/// @tparam Distribution Wrapped distribution type
+template <typename Distribution>
+struct DistributionInterpreter : public JsonInterpreter<Distribution>
+{
+  using JsonInterpreter<Distribution>::value_;  ///!< Internal representation
+
+  /// Binds Interpreter to stochastics interface
+  /// @param stochastics Stochastics interface
+  constexpr DistributionInterpreter(StochasticsInterface &stochastics) noexcept
+      : stochastics_{stochastics}
+  {
+  }
+
+  /// Sample from the distribution
+  /// @return string interpretation of the internal representation, empty if json interpretation failed
+  std::string to_string()
+  {
+    if (value_)
+    {
+      return std::to_string(sample(stochastics_, *value_));
+    }
+    Logger::Error("DistributionInterpreter: No distribution value available.");
+    return "";
+  }
+
+  StochasticsInterface &stochastics_;
+};
+
+/// Interpret a json object using a custom interpreter
+/// @note Specialize for custom behavor
+/// @tparam T          Type of the interpreter
+/// @tparam Payload    Type of the payload
+/// @param interpreter Interpreter to interpret the payload
+/// @param payload     Payload to interpret
+template <typename T, typename Payload>
+void interpret(DistributionInterpreter<T> &interpreter, const Payload &payload)
+{
+  interpreter.interpret(payload);
+}
+
+/// Convert internal representation to string
+/// @note Specialize for custom behavor
+/// @tparam T          Type of the interpreter
+/// @param interpreter Interpreter to convert to string
+/// @return            string interpretation of the internal representation
+template <typename T>
+std::string to_string(DistributionInterpreter<T> &interpreter)
+{
+  return interpreter.to_string();
+}
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
\ No newline at end of file
diff --git a/engine/src/Utils/PropertyInterpretation/Distributions.h b/engine/src/Utils/PropertyInterpretation/Distributions.h
new file mode 100644
index 0000000000000000000000000000000000000000..c4919f77944624ec0e3a0d03830fb54a85e8e918
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/Distributions.h
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#pragma once
+
+#include "Distributions/ExponentialDistribution.h"
+#include "Distributions/GammaDistribution.h"
+#include "Distributions/LogNormalDistribution.h"
+#include "Distributions/NormalDistribution.h"
+#include "Distributions/UniformDistribution.h"
diff --git a/engine/src/Utils/PropertyInterpretation/Distributions/ExponentialDistribution.h b/engine/src/Utils/PropertyInterpretation/Distributions/ExponentialDistribution.h
new file mode 100644
index 0000000000000000000000000000000000000000..7605671fca9d935e08424c371f132ccdd08b6cb0
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/Distributions/ExponentialDistribution.h
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Exponential distribution
+struct ExponentialDistribution
+{
+  double lowerLimit;     ///!< Lower limit
+  double upperLimit;     ///!< Upper limit
+  double expectedValue;  ///!< Reciprocal of the rate parameter lambda
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ExponentialDistribution,
+                                   lowerLimit,
+                                   upperLimit,
+                                   expectedValue);
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
diff --git a/engine/src/Utils/PropertyInterpretation/Distributions/GammaDistribution.h b/engine/src/Utils/PropertyInterpretation/Distributions/GammaDistribution.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e382fe7fa405eeecc94cc325bb82677cad9ee3a
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/Distributions/GammaDistribution.h
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Gamma distribution
+///
+/// The gamma distribution is a two-parameter family of continuous probability distributions.
+/// It is characterized by a shape parameter (alpha) and a rate parameter (beta).
+/// The expected value and variance of the gamma distribution are related to these parameters as follows:
+/// - Expected Value = alpha / beta
+/// - Variance = alpha / beta^2
+struct GammaDistribution
+{
+  double lowerLimit;     ///!< Lower limit
+  double upperLimit;     ///!< Upper limit
+  double expectedValue;  ///!< Expected value (mean) of the distribution, calculated as alpha / beta
+  double variance;       ///!< Variance of the distribution, calculated as alpha / beta^2
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GammaDistribution,
+                                   lowerLimit,
+                                   upperLimit,
+                                   expectedValue,
+                                   variance);
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
diff --git a/engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h b/engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h
new file mode 100644
index 0000000000000000000000000000000000000000..ba2397ae5f5833ee116bcbc02b080f12f6e48655
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// LogNormalDistribution
+struct LogNormalDistribution
+{
+  double lowerLimit;     ///!< Lower limit
+  double upperLimit;     ///!< Upper limit
+  double expectedValue;  ///!< Expected value
+  double variance;       ///!< Variance (square of standard deviation)
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LogNormalDistribution,
+                                   lowerLimit,
+                                   upperLimit,
+                                   expectedValue,
+                                   variance);
+
+}  // namespace OpenScenarioEngine::v1_3
\ No newline at end of file
diff --git a/engine/src/Utils/PropertyInterpretation/Distributions/NormalDistribution.h b/engine/src/Utils/PropertyInterpretation/Distributions/NormalDistribution.h
new file mode 100644
index 0000000000000000000000000000000000000000..d2a572f42b729b2a262b7fc4e60a833292156aed
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/Distributions/NormalDistribution.h
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Normal distribution
+struct NormalDistribution
+{
+  double lowerLimit;     ///!< Lower limit
+  double upperLimit;     ///!< Upper limit
+  double expectedValue;  ///!< Expected value
+  double variance;       ///!< Variance (square of standard deviation)
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NormalDistribution,
+                                   lowerLimit,
+                                   upperLimit,
+                                   expectedValue,
+                                   variance);
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
diff --git a/engine/src/Utils/PropertyInterpretation/Distributions/UniformDistribution.h b/engine/src/Utils/PropertyInterpretation/Distributions/UniformDistribution.h
new file mode 100644
index 0000000000000000000000000000000000000000..05b4b14582ff2b1146a1156ffc638203d0423afe
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/Distributions/UniformDistribution.h
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Uniform distribution
+struct UniformDistribution
+{
+  double lowerLimit;  ///!< Lower limit
+  double upperLimit;  ///!< Upper limit
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(UniformDistribution,
+                                   lowerLimit,
+                                   upperLimit);
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
diff --git a/engine/src/Utils/PropertyInterpretation/Interpreter.h b/engine/src/Utils/PropertyInterpretation/Interpreter.h
new file mode 100644
index 0000000000000000000000000000000000000000..0df771c44ac3bfa069099df320a28e59cd029823
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/Interpreter.h
@@ -0,0 +1,123 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <nlohmann/json.hpp>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Type-erased base for custom interpreter
+class Interpreter
+{
+  /// Concept for custom interpreter
+  struct InterpreterConcept
+  {
+    /// Destructor
+    virtual ~InterpreterConcept() {}
+
+    /// Interpret json object and hide internal representation
+    /// @param j json object to interpret
+    virtual void do_interpret(const nlohmann::json &j) = 0;
+
+    /// Convert internal representation to string
+    virtual std::string do_to_string() = 0;
+
+    /// Clone the interpreter
+    virtual std::unique_ptr<InterpreterConcept> clone() const = 0;
+  };
+
+  /// Model for custom interpreter
+  /// @tparam InterpreterT Actual interpreter type which shall be type-erased
+  template <typename InterpreterT>
+  struct InterpreterModel : public InterpreterConcept
+  {
+    InterpreterModel(InterpreterT interpreter)
+        : interpreter_{std::move(interpreter)}
+    {
+    }
+
+    std::unique_ptr<InterpreterConcept> clone() const override
+    {
+      return std::make_unique<InterpreterModel>(*this);
+    }
+
+    /// Relay to free function parse, which must be implemented for each custom interpreter
+    /// @param j json object to interpret
+    void do_interpret(const nlohmann::json &j) override
+    {
+      interpret(interpreter_, j);
+    }
+
+    /// Relay to free function to_string, which must be implemented for each custom interpreter
+    /// @return string interpretation of the internal representation
+    std::string do_to_string() override
+    {
+      return to_string(interpreter_);
+    }
+
+    InterpreterT interpreter_;  ///!< Actual interpreter
+  };
+
+  /// Free function to interpret json object (injects interpret into the base class)
+  /// @param interpreter interpreter to interpret the json object
+  /// @param j json object to interpret
+  friend void interpret(Interpreter &interpreter, const nlohmann::json &j)
+  {
+    interpreter.pimpl_->do_interpret(j);
+  }
+
+  /// Free function to convert internal representation to string (injects to_string into the base class)
+  /// @param interpreter interpreter used to convert to string
+  /// @return string interpretation of the internal representation
+  friend std::string to_string(Interpreter &interpreter)
+  {
+    return interpreter.pimpl_->do_to_string();
+  }
+
+  std::unique_ptr<InterpreterConcept> pimpl_;  ///!< Type-erased interpreter
+
+public:
+  /// Constructor to create a type erased custom interpreter
+  /// @tparam InterpreterT Actual interpreter type
+  /// @param interpreter Actual interpreter
+  template <typename InterpreterT>
+  Interpreter(InterpreterT interpreter)
+      : pimpl_{std::make_unique<InterpreterModel<InterpreterT>>(std::move(interpreter))}
+  {
+  }
+
+  /// Copy constructor
+  /// @param other
+  Interpreter(const Interpreter &other)
+      : pimpl_{other.pimpl_->clone()}
+  {
+  }
+
+  /// Copy assignment operator
+  /// @param other
+  /// @return New interpreter
+  Interpreter &operator=(const Interpreter &other)
+  {
+    other.pimpl_->clone().swap(pimpl_);
+    return *this;
+  }
+
+  Interpreter(Interpreter &&) = default;             ///!< Move constructor
+  Interpreter &operator=(Interpreter &&) = default;  ///!< Move assignment operator
+};
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
diff --git a/engine/src/Utils/PropertyInterpretation/JsonInterpreter.h b/engine/src/Utils/PropertyInterpretation/JsonInterpreter.h
new file mode 100644
index 0000000000000000000000000000000000000000..be2baa763b3e3bb194dc2d09cb55851fcc74e6b6
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/JsonInterpreter.h
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+#include <optional>
+#include <string>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Interpreter for json objects
+/// @tparam T Type for deserialization
+template <typename T>
+struct JsonInterpreter
+{
+  /// Interpret and store the json object
+  /// @note If the json object cannot be interpreted, the internal value will be std::nullopt
+  /// @param j json object to interpret
+  void interpret(const nlohmann::json &j)
+  {
+    try
+    {
+      value_ = j.get<T>();
+    }
+    catch (const nlohmann::json::out_of_range &)
+    {
+      value_ = std::nullopt;
+    }
+  }
+
+  std::optional<T> value_;
+};
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
\ No newline at end of file
diff --git a/engine/src/Utils/PropertyInterpretation/RawValueRelay.h b/engine/src/Utils/PropertyInterpretation/RawValueRelay.h
new file mode 100644
index 0000000000000000000000000000000000000000..26246b483fe72ab89b91c69fd8d0b311c0018656
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpretation/RawValueRelay.h
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#pragma once
+
+#include <string>
+#include <utility>
+
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
+{
+
+/// Default interpreter if no custom interpreter is available or can be resolved
+struct RawValueRelay
+{
+  std::string value_;
+};
+
+/// Void operation needed for type-erased interpreter stage
+/// @tparam ...DontCare     Catches all parameters, if any
+/// @param raw_value_relay  Actual intepreter
+/// @param ...              ignored
+template <typename... DontCare>
+void interpret(const RawValueRelay &raw_value_relay, DontCare...)
+{
+  std::ignore = raw_value_relay;
+}
+
+/// Return the internal representation without doing anything
+/// @param raw_value_relay Actual interpreter
+/// @return stored string
+std::string to_string(const RawValueRelay &raw_value_relay)
+{
+  return raw_value_relay.value_;
+}
+
+}  // namespace OpenScenarioEngine::v1_3::PropertyInterpretation
\ No newline at end of file
diff --git a/engine/src/Utils/PropertyInterpreter.cpp b/engine/src/Utils/PropertyInterpreter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4071c9124a7bce4a8a76500e1072b6c542ab1258
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpreter.cpp
@@ -0,0 +1,71 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#include "PropertyInterpreter.h"
+
+#include <algorithm>
+
+#include "Utils/JsonParsing.h"
+#include "Utils/PropertyInterpretation/DistributionInterpreter.h"
+#include "Utils/PropertyInterpretation/RawValueRelay.h"
+
+namespace OpenScenarioEngine::v1_3
+{
+
+using namespace PropertyInterpretation;
+
+namespace detail
+{
+std::pair<std::string, nlohmann::json> GetTypeAndValue(const std::string &property_value)
+{
+  if (auto j = json::TryParse(property_value);
+      j.has_value() && j->contains("type") && j->contains("value"))
+  {
+    return {j->at("type").get<std::string>(), j->at("value")};
+  }
+  return {"", nlohmann::json{}};
+}
+
+}  // namespace detail
+
+void PropertyInterpreter::Init(
+    const std::map<std::string, std::string> &properties)
+{
+  interpreter_mapping_.clear();
+  for (const auto &[property_key, property_value] : properties)
+  {
+    const auto [json_type, json_value] = detail::GetTypeAndValue(property_value);
+
+    const auto interpreter_iter = interpreter_prototypes_.find(json_type);
+    auto [interpreter, _] = interpreter_mapping_.try_emplace(
+        property_key,
+        interpreter_iter != interpreter_prototypes_.end()
+            ? interpreter_iter->second
+            : RawValueRelay{property_value});
+    interpret(interpreter->second, json_value);
+  }
+}
+
+std::map<std::string, std::string> PropertyInterpreter::GenerateParameters(
+    const std::map<std::string, std::string> &key_replacements)
+{
+  std::map<std::string, std::string> parameters;
+  for (auto &[key, value] : interpreter_mapping_)
+  {
+    auto iter = key_replacements.find(key);
+    parameters.emplace(iter == std::end(key_replacements)
+                           ? key
+                           : iter->second,
+                       to_string(value));
+  }
+  return parameters;
+}
+
+}  // namespace OpenScenarioEngine::v1_3
diff --git a/engine/src/Utils/PropertyInterpreter.h b/engine/src/Utils/PropertyInterpreter.h
new file mode 100644
index 0000000000000000000000000000000000000000..e115d6300746d94f2ed51abee4e8b3cb86cc0adc
--- /dev/null
+++ b/engine/src/Utils/PropertyInterpreter.h
@@ -0,0 +1,55 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "Utils/PropertyInterpretation/Interpreter.h"
+
+namespace OpenScenarioEngine::v1_3
+{
+
+/// Utility class to interpret properties, e.g. custom json distribution definitions
+///
+/// This class can take a map of properties and interpret them using custom interpreters.
+/// Each property is interpreted by a custom interpreter based the value of the property.
+/// If a json object is found, the "type" field is used to determine the interpreter.
+/// In such a case, the "value" field is relayed to the interpreter for interpretation.
+/// If no interpreter is found or the json has no field "type" and "value", the property
+/// is treated as a raw value (= string).
+class PropertyInterpreter
+{
+public:
+  using Interpreters = std::map<std::string, PropertyInterpretation::Interpreter>;
+
+  /// Constructor to create a property interpreter
+  /// @param interpreter_prototypes Prototypes for custom interpreters
+  PropertyInterpreter(Interpreters interpreter_prototypes)
+      : interpreter_prototypes_{std::move(interpreter_prototypes)} {}
+
+  /// Initialize the property interpreter for a given set of properties
+  /// @param properties             Properties to interpret
+  void Init(const std::map<std::string, std::string> &properties);
+
+  /// Generate parameters from the interpreted properties, given at initialization
+  /// @note Might return different values at every call, e.g. for stochastic properties
+  /// @param key_replacements defines a mapping from original keys to new keys
+  /// @return interpreted parameter map
+  std::map<std::string, std::string> GenerateParameters(
+      const std::map<std::string, std::string> &key_replacements = {});
+
+private:
+  Interpreters interpreter_prototypes_{};  ///!< Available custom interpreters
+  Interpreters interpreter_mapping_{};     ///!< Property to interpreter relation
+};
+}  // namespace OpenScenarioEngine::v1_3
diff --git a/engine/tests/Utils/DistributionTest.cpp b/engine/tests/Utils/DistributionTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51c4ce2bc8535ac23ae0bd926883bc3f77b6bf9b
--- /dev/null
+++ b/engine/tests/Utils/DistributionTest.cpp
@@ -0,0 +1,160 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "TestUtils/MockStochastics.h"
+#include "Utils/PropertyInterpretation/DistributionInterpreter.h"
+#include "Utils/PropertyInterpretation/Distributions.h"
+
+using namespace OpenScenarioEngine::v1_3;
+using namespace OpenScenarioEngine::v1_3::PropertyInterpretation;
+using namespace testing;
+
+class DistributionTest : public Test
+{
+protected:
+  MockStochastics stochastics;
+};
+
+struct DistributionTestParams
+{
+  double lowerLimit;
+  double upperLimit;
+  double sampledValue;
+  double expectedValue;
+};
+
+class ExponentialDistributionTest : public DistributionTest, public WithParamInterface<DistributionTestParams>
+{
+};
+
+TEST_P(ExponentialDistributionTest, GivenExponentialDistribution_WhenSampleIsCalled_ThenReturnsClampedValue)
+{
+  auto params = GetParam();
+  ExponentialDistribution distribution{params.lowerLimit, params.upperLimit, 2.0};
+  ON_CALL(stochastics, GetExponentialDistributed(1.0 / 2.0))
+      .WillByDefault(Return(params.sampledValue));
+
+  double result = sample(stochastics, distribution);
+
+  EXPECT_THAT(result, DoubleEq(params.expectedValue));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ExponentialDistributionTests,
+    ExponentialDistributionTest,
+    ::testing::Values(
+        DistributionTestParams{0.0, 10.0, 5.0, 5.0},    // regular test
+        DistributionTestParams{0.0, 10.0, -1.0, 0.0},   // lowerLimit test
+        DistributionTestParams{0.0, 10.0, 15.0, 10.0},  // upperLimit test
+        DistributionTestParams{0.0, 10.0, 0.0, 0.0}     // division by zero test for expectedValue
+        ));
+
+class GammaDistributionTest : public DistributionTest, public WithParamInterface<DistributionTestParams>
+{
+};
+
+TEST_P(GammaDistributionTest, GivenGammaDistribution_WhenSampleIsCalled_ThenReturnsClampedValue)
+{
+  auto params = GetParam();
+  GammaDistribution distribution{params.lowerLimit, params.upperLimit, 2.0, 4.0};
+  ON_CALL(stochastics, GetGammaDistributed(2.0, std::sqrt(4.0)))
+      .WillByDefault(Return(params.sampledValue));
+
+  double result = sample(stochastics, distribution);
+
+  EXPECT_THAT(result, DoubleEq(params.expectedValue));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    GammaDistributionTests,
+    GammaDistributionTest,
+    ::testing::Values(
+        DistributionTestParams{0.0, 10.0, 5.0, 5.0},   // regular test
+        DistributionTestParams{0.0, 10.0, -1.0, 0.0},  // lowerLimit test
+        DistributionTestParams{0.0, 10.0, 15.0, 10.0}  // upperLimit test
+        ));
+
+class LogNormalDistributionTest : public DistributionTest, public WithParamInterface<DistributionTestParams>
+{
+};
+
+TEST_P(LogNormalDistributionTest, GivenLogNormalDistribution_WhenSampleIsCalled_ThenReturnsClampedValue)
+{
+  auto params = GetParam();
+  LogNormalDistribution distribution{params.lowerLimit, params.upperLimit, 2.0, 4.0};
+  ON_CALL(stochastics, GetLogNormalDistributed(2.0, std::sqrt(4.0)))
+      .WillByDefault(Return(params.sampledValue));
+
+  double result = sample(stochastics, distribution);
+
+  EXPECT_THAT(result, DoubleEq(params.expectedValue));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    LogNormalDistributionTests,
+    LogNormalDistributionTest,
+    ::testing::Values(
+        DistributionTestParams{0.0, 10.0, 5.0, 5.0},   // regular test
+        DistributionTestParams{0.0, 10.0, -1.0, 0.0},  // lowerLimit test
+        DistributionTestParams{0.0, 10.0, 15.0, 10.0}  // upperLimit test
+        ));
+
+class NormalDistributionTest : public DistributionTest, public WithParamInterface<DistributionTestParams>
+{
+};
+
+TEST_P(NormalDistributionTest, GivenNormalDistribution_WhenSampleIsCalled_ThenReturnsClampedValue)
+{
+  auto params = GetParam();
+  NormalDistribution distribution{params.lowerLimit, params.upperLimit, 2.0, 4.0};
+  ON_CALL(stochastics, GetNormalDistributed(2.0, std::sqrt(4.0)))
+      .WillByDefault(Return(params.sampledValue));
+
+  double result = sample(stochastics, distribution);
+
+  EXPECT_THAT(result, DoubleEq(params.expectedValue));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    NormalDistributionTests,
+    NormalDistributionTest,
+    ::testing::Values(
+        DistributionTestParams{0.0, 10.0, 5.0, 5.0},   // regular test
+        DistributionTestParams{0.0, 10.0, -1.0, 0.0},  // lowerLimit test
+        DistributionTestParams{0.0, 10.0, 15.0, 10.0}  // upperLimit test
+        ));
+
+class UniformDistributionTest : public DistributionTest, public WithParamInterface<DistributionTestParams>
+{
+};
+
+TEST_P(UniformDistributionTest, GivenUniformDistribution_WhenSampleIsCalled_ThenReturnsClampedValue)
+{
+  auto params = GetParam();
+  UniformDistribution distribution{params.lowerLimit, params.upperLimit};
+  ON_CALL(stochastics, GetUniformDistributed(params.lowerLimit, params.upperLimit))
+      .WillByDefault(Return(params.sampledValue));
+
+  double result = sample(stochastics, distribution);
+
+  EXPECT_THAT(result, DoubleEq(params.expectedValue));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    UniformDistributionTests,
+    UniformDistributionTest,
+    ::testing::Values(
+        DistributionTestParams{0.0, 10.0, 5.0, 5.0},   // regular test
+        DistributionTestParams{0.0, 10.0, -1.0, 0.0},  // lowerLimit test
+        DistributionTestParams{0.0, 10.0, 15.0, 10.0}  // upperLimit test
+        ));
diff --git a/engine/tests/Utils/JsonParsingTest.cpp b/engine/tests/Utils/JsonParsingTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6287007017aceed8edda587d693a42c3f167dbf
--- /dev/null
+++ b/engine/tests/Utils/JsonParsingTest.cpp
@@ -0,0 +1,80 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Utils/JsonParsing.h"
+
+using testing::DoubleEq;
+using testing::Eq;
+
+using namespace OpenScenarioEngine::v1_3;
+
+struct DeserializationTestType
+{
+  int a;
+  double b;
+  std::string c;
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DeserializationTestType, a, b, c);
+
+TEST(JsonParsingTest, GivenPropertyString_WhenStringUsesDoubleQuotes_ThenReturnsObject)
+{
+  auto deserializationTestType = json::Deserialize<DeserializationTestType>(
+      R"({"a": 1, "b": 2.34, "c": "test"})");
+
+  ASSERT_TRUE(deserializationTestType.has_value());
+  EXPECT_THAT(deserializationTestType->a, Eq(1));
+  EXPECT_THAT(deserializationTestType->b, DoubleEq(2.34));
+  EXPECT_THAT(deserializationTestType->c, Eq("test"));
+}
+
+TEST(JsonParsingTest, GivenPropertyString_WhenStringUsesSingleQuotes_ThenReturnsObject)
+{
+  auto deserializationTestType = json::Deserialize<DeserializationTestType>(
+      R"({'a': 1, 'b': 2.34, 'c': 'test'})");
+
+  ASSERT_TRUE(deserializationTestType.has_value());
+  EXPECT_THAT(deserializationTestType->a, Eq(1));
+  EXPECT_THAT(deserializationTestType->b, DoubleEq(2.34));
+  EXPECT_THAT(deserializationTestType->c, Eq("test"));
+}
+
+TEST(JsonParsingTest, GivenPropertyString_WhenStringUsesExcapedXmlQuotes_ThenReturnsObject)
+{
+  auto deserializationTestType = json::Deserialize<DeserializationTestType>(
+      R"({&quot;a&quot;: 1, &quot;b&quot;: 2.34, &quot;c&quot;: &quot;test&quot;})");
+
+  ASSERT_TRUE(deserializationTestType.has_value());
+  EXPECT_THAT(deserializationTestType->a, Eq(1));
+  EXPECT_THAT(deserializationTestType->b, DoubleEq(2.34));
+  EXPECT_THAT(deserializationTestType->c, Eq("test"));
+}
+
+TEST(JsonParsingTest, GivenPropertyStringWithMoreFieldsThanNeccessary_WhenParsed_ReturnsObject)
+{
+  auto deserializationTestType = json::Deserialize<DeserializationTestType>(
+      R"({"a": 1, "b": 2.34, "c": "test", "d": 4})");
+
+  ASSERT_TRUE(deserializationTestType.has_value());
+  EXPECT_THAT(deserializationTestType->a, Eq(1));
+  EXPECT_THAT(deserializationTestType->b, DoubleEq(2.34));
+  EXPECT_THAT(deserializationTestType->c, Eq("test"));
+}
+
+TEST(JsonParsingTest, GivenPropertyString_WhenStringIsInvalid_ThenReturnsEmpty)
+{
+  auto deserializationTestType = json::Deserialize<DeserializationTestType>(
+      R"({a: 1, b: 2.34, c: test})");
+
+  ASSERT_FALSE(deserializationTestType.has_value());
+}
\ No newline at end of file
diff --git a/engine/tests/Utils/PropertyInterpreterTest.cpp b/engine/tests/Utils/PropertyInterpreterTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f17a955f0a1bc697b9c2e1dd957cf6930645f9e2
--- /dev/null
+++ b/engine/tests/Utils/PropertyInterpreterTest.cpp
@@ -0,0 +1,165 @@
+/********************************************************************************
+ * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "TestUtils/MockStochastics.h"
+#include "Utils/PropertyInterpretation/DistributionInterpreter.h"
+#include "Utils/PropertyInterpretation/Distributions.h"
+#include "Utils/PropertyInterpreter.h"
+
+using namespace OpenScenarioEngine::v1_3;
+using namespace OpenScenarioEngine::v1_3::PropertyInterpretation;
+using namespace testing;
+
+struct PropertyInterpreterTest : public Test
+{
+  void SetUp() override
+  {
+    ON_CALL(stochastics_, GetUniformDistributed(0, 1)).WillByDefault(Return(0.123));
+    // mean = 2, stddev = 3 ==> sqrt(variance = 9)
+    ON_CALL(stochastics_, GetNormalDistributed(2, 3)).WillByDefault(Return(2.345));
+  }
+
+  PropertyInterpreter::Interpreters GetInterpreterPrototypes()
+  {
+    return {
+        {"UniformDistribution", DistributionInterpreter<UniformDistribution>{stochastics_}},
+        {"NormalDistribution", DistributionInterpreter<NormalDistribution>{stochastics_}}};
+  }
+
+  NiceMock<MockStochastics> stochastics_;
+};
+
+TEST_F(PropertyInterpreterTest, GivenProperties_WhenCallingPropertyInterpreter_ThenInterpretsResults)
+{
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(
+      {{"prop0", R"({"type": "UniformDistribution", "value": {"lowerLimit": 0, "upperLimit": 1} })"},
+       {"prop1", R"({"type": "NormalDistribution", "value": {"lowerLimit": 0, "upperLimit": 10, "expectedValue": 2, "variance": 9} })"},
+       {"prop2", "RAW_PROPERTY"}});
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::SizeIs(3));
+  EXPECT_EQ(result["prop0"], "0.123000");
+  EXPECT_EQ(result["prop1"], "2.345000");
+  EXPECT_EQ(result["prop2"], "RAW_PROPERTY");
+}
+
+TEST_F(PropertyInterpreterTest, GivenPropertiesWithExtendedFields_WhenCalled_ThenInterpretsValues)
+{
+  std::map<std::string, std::string> test_properties{
+      {"extended", R"(
+            {
+                "filter": ["extra"],
+                "mapping": "custom_property",
+                "type": "UniformDistribution",
+                "value": {"lowerLimit": 0, "upperLimit": 1}
+            })"}};
+
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(test_properties);
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::SizeIs(1));
+  EXPECT_EQ(result["extended"], "0.123000");
+}
+
+TEST_F(PropertyInterpreterTest, GivenPropertyWithoutType_WhenCallingPropertyInterpreter_ThenReturnsRawValue)
+{
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(
+      {{"prop0", R"({"value": {"lowerLimit": 0, "upperLimit": 1} })"}});
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::SizeIs(1));
+  EXPECT_EQ(result["prop0"], R"({"value": {"lowerLimit": 0, "upperLimit": 1} })");
+}
+
+TEST_F(PropertyInterpreterTest, GivenPropertyWithoutValue_WhenCallingPropertyInterpreter_ThenReturnsRawValue)
+{
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(
+      {{"prop0", R"({"type": "UniformDistribution"})"}});
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::SizeIs(1));
+  EXPECT_EQ(result["prop0"], R"({"type": "UniformDistribution"})");
+}
+
+TEST_F(PropertyInterpreterTest, GivenInvalidJson_WhenCallingPropertyInterpreter_ThenReturnsRawValue)
+{
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(
+      {{"prop0", "invalid_json"}});
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::SizeIs(1));
+  EXPECT_EQ(result["prop0"], "invalid_json");
+}
+
+TEST_F(PropertyInterpreterTest, GivenUnsupportedType_WhenCallingPropertyInterpreter_ThenReturnsRawValue)
+{
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(
+      {{"prop0", R"({"type": "UnsupportedType", "value": {"lowerLimit": 0, "upperLimit": 1} })"}});
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::SizeIs(1));
+  EXPECT_EQ(result["prop0"], R"({"type": "UnsupportedType", "value": {"lowerLimit": 0, "upperLimit": 1} })");
+}
+
+TEST_F(PropertyInterpreterTest, GivenEmptyProperties_WhenCallingPropertyInterpreter_ThenReturnsEmptyResult)
+{
+  std::map<std::string, std::string> properties_under_test{};
+
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(properties_under_test);
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::IsEmpty());
+}
+
+TEST_F(PropertyInterpreterTest, GivenKeyReplacements_WhenCallingPropertyInterpreter_ThenReturnsReplacedKeys)
+{
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  property_interpreter.Init(
+      {{"prop0", R"({"type": "UniformDistribution", "value": {"lowerLimit": 0, "upperLimit": 1} })"}});
+
+  std::map<std::string, std::string> key_replacements{{"prop0", "replaced_key"}};
+  auto result = property_interpreter.GenerateParameters(key_replacements);
+
+  ASSERT_THAT(result, testing::SizeIs(1));
+  EXPECT_EQ(result["replaced_key"], "0.123000");
+}
+
+TEST_F(PropertyInterpreterTest, GivenUninitializedInterpreter_WhenCallingGenerate_ThenDoesNothing)
+{
+  PropertyInterpreter property_interpreter(GetInterpreterPrototypes());
+  auto result = property_interpreter.GenerateParameters();
+  ASSERT_THAT(result, testing::IsEmpty());
+}
+
+TEST_F(PropertyInterpreterTest, GivenPropertyInterpreter_WhenCopied_ThenHoldsState)
+{
+  auto generateInterpreter = [](auto prototypes) -> PropertyInterpreter
+  {
+    PropertyInterpreter interpreter(prototypes);
+    interpreter.Init({{"prop0", "RAW_PROPERTY"}});
+    return interpreter;
+  };
+
+  auto property_interpreter = generateInterpreter(GetInterpreterPrototypes());
+  auto result = property_interpreter.GenerateParameters();
+
+  ASSERT_THAT(result, testing::SizeIs(1));
+  EXPECT_EQ(result["prop0"], "RAW_PROPERTY");
+}
\ No newline at end of file
diff --git a/engine/third_party/dependencies.bzl b/engine/third_party/dependencies.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..180f0fdbc5f18c69fb7b3b522a4826708c049bff
--- /dev/null
+++ b/engine/third_party/dependencies.bzl
@@ -0,0 +1,21 @@
+"""A module defining the third party dependencies for instantiating in WORKSPACE"""
+
+load("@open_scenario_engine//third_party/boost:boost.bzl", "boost_deps")
+load("@open_scenario_engine//third_party/googletest:googletest.bzl", "googletest")
+load("@open_scenario_engine//third_party/mantle_api:mantle_api.bzl", "mantle_api")
+load("@open_scenario_engine//third_party/nlohmann_json:nlohmann_json.bzl", "nlohmann_json")
+load("@open_scenario_engine//third_party/open_scenario_parser:open_scenario_parser.bzl", "open_scenario_parser")
+load("@open_scenario_engine//third_party/stochastics_library:stochastics_library.bzl", "stochastics_library")
+load("@open_scenario_engine//third_party/units:units.bzl", "units_nhh")
+load("@open_scenario_engine//third_party/yase:yase.bzl", "yase")
+
+def third_party_deps():
+    """Load dependencies"""
+    boost_deps()
+    googletest()
+    mantle_api()
+    nlohmann_json()
+    open_scenario_parser()
+    stochastics_library()
+    units_nhh()
+    yase()
diff --git a/engine/third_party/deps.bzl b/engine/third_party/deps.bzl
deleted file mode 100644
index 1f832f8a4118020d008720161d4fea8b2b7e8350..0000000000000000000000000000000000000000
--- a/engine/third_party/deps.bzl
+++ /dev/null
@@ -1,18 +0,0 @@
-load("@//third_party/boost:boost.bzl", "boost_deps")
-load("@//third_party/mantle_api:mantle_api.bzl", "mantle_api")
-load("@//third_party/open_scenario_parser:open_scenario_parser.bzl", "open_scenario_parser")
-load("@//third_party/stochastics_library:stochastics_library.bzl", "stochastics_library")
-load("@//third_party/units:units.bzl", "units_nhh")
-load("@//third_party/yase:yase.bzl", "yase")
-
-
-
-def osc_engine_deps():
-    """Load dependencies"""
-    boost_deps()
-    mantle_api()
-    open_scenario_parser()
-    stochastics_library()
-    units_nhh()
-    yase()
-
diff --git a/engine/third_party/googletest/BUILD.bazel b/engine/third_party/googletest/BUILD.bazel
new file mode 100644
index 0000000000000000000000000000000000000000..5219fee534cc6b3f5bcfaf335fabe5b75695f2e5
--- /dev/null
+++ b/engine/third_party/googletest/BUILD.bazel
@@ -0,0 +1,11 @@
+alias(
+    name = "googletest",
+    actual = "@googletest//:gtest",
+    visibility = ["//visibility:public"],
+)
+
+alias(
+    name = "main",
+    actual = "@googletest//:gtest_main",
+    visibility = ["//visibility:public"],
+)
diff --git a/engine/third_party/googletest/gcc_printer_patch.patch b/engine/third_party/googletest/gcc_printer_patch.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a84e6ef6379e4fc65e73f95c0fec0cc9c1edeaf4
--- /dev/null
+++ b/engine/third_party/googletest/gcc_printer_patch.patch
@@ -0,0 +1,22 @@
+--- a/googletest/include/gtest/gtest-printers.h
++++ b/googletest/include/gtest/gtest-printers.h
+@@ -205,12 +205,13 @@ struct StreamPrinter {
+             // Don't accept member pointers here. We'd print them via implicit
+             // conversion to bool, which isn't useful.
+             typename = typename std::enable_if<
+-                !std::is_member_pointer<T>::value>::type,
+-            // Only accept types for which we can find a streaming operator via
+-            // ADL (possibly involving implicit conversions).
+-            typename = decltype(std::declval<std::ostream&>()
+-                                << std::declval<const T&>())>
+-  static void PrintValue(const T& value, ::std::ostream* os) {
++                !std::is_member_pointer<T>::value>::type>
++  // Only accept types for which we can find a streaming operator via
++  // ADL (possibly involving implicit conversions).
++  // (Use SFINAE via return type, because it seems GCC < 12 doesn't handle name
++  // lookup properly when we do it in the template parameter list.)
++  static auto PrintValue(const T& value, ::std::ostream* os)
++  -> decltype((void)(*os << value)) {
+     // Call streaming operator found by ADL, possibly with implicit conversions
+     // of the arguments.
+     *os << value;
diff --git a/engine/third_party/googletest/googletest.bzl b/engine/third_party/googletest/googletest.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..96cf4768c0a1281d25441734db96c633ca97795d
--- /dev/null
+++ b/engine/third_party/googletest/googletest.bzl
@@ -0,0 +1,20 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+
+_VERSION = "1.12.1"
+
+def googletest():
+    maybe(
+        http_archive,
+        name = "googletest",
+        url = "https://github.com/google/googletest/archive/refs/tags/release-{}.tar.gz".format(_VERSION),
+        sha256 = "81964fe578e9bd7c94dfdb09c8e4d6e6759e19967e397dbea48d1c10e45d0df2",
+        strip_prefix = "googletest-release-{}".format(_VERSION),
+        patches = [
+            # Gtest >= 1.11.0 is impacted by a GCC compiler bug regarding template deduction.
+            # See: https://github.com/google/googletest/issues/3552
+            # We backport the upstream fix to this until we reach a gtest version incorporating it by itself.
+            Label("//:third_party/googletest/gcc_printer_patch.patch"),
+        ],
+        patch_args = ["-p1"],
+    )
diff --git a/engine/third_party/nlohmann_json/BUILD.bazel b/engine/third_party/nlohmann_json/BUILD.bazel
new file mode 100644
index 0000000000000000000000000000000000000000..73a292eeb8340b378e07c9aff861d586c8b230b0
--- /dev/null
+++ b/engine/third_party/nlohmann_json/BUILD.bazel
@@ -0,0 +1,5 @@
+alias(
+    name = "nlohmann_json",
+    actual = "@nlohmann_json",
+    visibility = ["//visibility:public"],
+)
diff --git a/engine/third_party/nlohmann_json/nlohmann_json.BUILD b/engine/third_party/nlohmann_json/nlohmann_json.BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..d615d420b39e1e47744a892682c7220b64583a65
--- /dev/null
+++ b/engine/third_party/nlohmann_json/nlohmann_json.BUILD
@@ -0,0 +1,7 @@
+
+cc_library(
+    name = "nlohmann_json",
+    hdrs = ["single_include/nlohmann/json.hpp"],
+    includes = ["single_include"],
+    visibility = ["//visibility:public"],
+)
diff --git a/engine/third_party/nlohmann_json/nlohmann_json.bzl b/engine/third_party/nlohmann_json/nlohmann_json.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..5d2c07b93920d2df4a8a43024006920f6ecaeed4
--- /dev/null
+++ b/engine/third_party/nlohmann_json/nlohmann_json.bzl
@@ -0,0 +1,13 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+
+_VERSION = "3.9.1"
+
+def nlohmann_json():
+    maybe(
+        http_archive,
+        name = "nlohmann_json",
+        build_file = Label("//:third_party/nlohmann_json/nlohmann_json.BUILD"),
+        url =  "https://github.com/nlohmann/json/releases/download/v{version}/include.zip".format(version = _VERSION),
+        sha256 = "6bea5877b1541d353bd77bdfbdb2696333ae5ed8f9e8cc22df657192218cad91",
+    )
diff --git a/utils/ci/conan/conanfile.txt b/utils/ci/conan/conanfile.txt
index 2cd59264a312b987d035dff89046a1b764f15989..059e3c1ae668001a89672522fab0323b2dfe7004 100644
--- a/utils/ci/conan/conanfile.txt
+++ b/utils/ci/conan/conanfile.txt
@@ -1,9 +1,10 @@
 [requires]
-units/2.3.4@openscenarioengine/testing
 mantleapi/v14.0.0@openscenarioengine/testing
-yase/d0c0e58d17358044cc9018c74308b45f6097ecfb@openscenarioengine/testing
+nlohmann_json/v3.9.1@openscenarioengine/testing
 openscenario_api/v1.4.0@openscenarioengine/testing
 stochastics/0.11.0@openscenarioengine/testing
+units/2.3.4@openscenarioengine/testing
+yase/d0c0e58d17358044cc9018c74308b45f6097ecfb@openscenarioengine/testing
 
 [options]
 
diff --git a/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml b/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml
new file mode 100644
index 0000000000000000000000000000000000000000..346f60a21df16777d69b8c6c743fabc7c6c37079
--- /dev/null
+++ b/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml
@@ -0,0 +1,18 @@
+################################################################################
+# Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# SPDX-License-Identifier: EPL-2.0
+################################################################################
+
+################################################################################
+# conan data file for building nlohmann_json with Conan
+################################################################################
+
+sources:
+  "v3.9.1":
+    url: https://github.com/nlohmann/json/archive/refs/tags/v3.9.1.tar.gz
+    sha256: "4cf0df69731494668bdd6460ed8cb269b68de9c19ad8c27abc24cd72605b2d5b"
diff --git a/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py b/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3da81e2e9fbdc7f396d5d2bbe168b5cd1c81dd8
--- /dev/null
+++ b/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py
@@ -0,0 +1,60 @@
+################################################################################
+# Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# SPDX-License-Identifier: EPL-2.0
+################################################################################
+
+################################################################################
+# Install file for building nlohmann_json with Conan
+################################################################################
+
+from conan import ConanFile
+from conan.tools.cmake import CMake, CMakeToolchain
+from conan.tools.files import rm, copy, rmdir
+from conan.tools.files import get, copy
+import os
+
+required_conan_version = ">=1.53"
+
+class NlohmannJSONConan(ConanFile):
+    name = "nlohmann_json"
+    settings = "os", "compiler", "build_type", "arch"
+    options = {"shared": [True, False],
+               "fPIC": [True, False]}
+
+    default_options = {"shared": False,
+                       "fPIC": True}
+
+    exports_sources = "*"
+    no_copy_source = False
+    short_paths = True
+
+    def config_options(self):
+        if self.settings.os == "Windows":
+            del self.options.fPIC
+
+    def configure(self):
+        if self.options.shared:
+            self.options.rm_safe("fPIC")
+
+    def generate(self):
+        tc = CMakeToolchain(self)
+        tc.generate()
+
+    def source(self):
+        get(self, **self.conan_data["sources"][self.version], strip_root=True)
+
+    def build(self):
+        cmake = CMake(self)
+        cmake.configure()
+
+    def package(self):
+        cmake = CMake(self)
+        cmake.install()
+
+    def package_info(self):
+        self.cpp_info.set_property("cmake_find_mode", "none")
diff --git a/utils/ci/conan/recipe/nlohmann_json/config.yml b/utils/ci/conan/recipe/nlohmann_json/config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..84d55e7384f5e0fdab103c8830e74000dcfd99b1
--- /dev/null
+++ b/utils/ci/conan/recipe/nlohmann_json/config.yml
@@ -0,0 +1,17 @@
+################################################################################
+# Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# SPDX-License-Identifier: EPL-2.0
+################################################################################
+
+################################################################################
+# config file for building nlohmann_json with Conan
+################################################################################
+
+versions:
+  "v3.9.1":
+    folder: "all"
diff --git a/utils/ci/scripts/20_configure.sh b/utils/ci/scripts/20_configure.sh
index 303502a8c14b9bcf1c4b3f4e5403d56df6472b51..673e138463374d3082697709af0c83beb3fbf2f4 100755
--- a/utils/ci/scripts/20_configure.sh
+++ b/utils/ci/scripts/20_configure.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 ################################################################################
-# Copyright (c) 2023-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+# Copyright (c) 2023-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License 2.0 which is available at
@@ -31,6 +31,7 @@ mkdir -p "$MYDIR/../../../../build"
 cd "$MYDIR/../../../../build" || exit 1
 
 DEPS=(
+  "$PWD/../deps/direct_deploy/nlohmann_json"
   "$PWD/../deps/direct_deploy/units"
   "$PWD/../deps/direct_deploy/mantleapi"
   "$PWD/../deps/direct_deploy/stochastics"