From f248cabd9206b9526d56b74b5661297a0a3f376d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Paris?= <rene.paris@in-tech.com>
Date: Wed, 12 Feb 2025 08:08:50 +0100
Subject: [PATCH 1/6] feat(JSON for modern C++): Integrate nlohmann_json
 library

Integrates the commonly used JSON library for convenient parsing
of custom properites, carring object data encoded as string.
---
 NOTICE.md                                     |  6 ++
 README.md                                     |  9 +--
 engine/BUILD.bazel                            |  4 +-
 engine/CMakeLists.txt                         | 44 +++++++-------
 engine/WORKSPACE                              | 12 +---
 engine/third_party/dependencies.bzl           | 21 +++++++
 engine/third_party/deps.bzl                   | 18 ------
 engine/third_party/googletest/BUILD.bazel     | 11 ++++
 .../googletest/gcc_printer_patch.patch        | 22 +++++++
 engine/third_party/googletest/googletest.bzl  | 20 +++++++
 engine/third_party/nlohmann_json/BUILD.bazel  |  5 ++
 .../nlohmann_json/nlohmann_json.BUILD         |  7 +++
 .../nlohmann_json/nlohmann_json.bzl           | 13 ++++
 utils/ci/conan/conanfile.txt                  |  5 +-
 .../recipe/nlohmann_json/all/conandata.yml    | 21 +++++++
 .../recipe/nlohmann_json/all/conanfile.py     | 60 +++++++++++++++++++
 .../ci/conan/recipe/nlohmann_json/config.yml  | 20 +++++++
 utils/ci/scripts/20_configure.sh              |  1 +
 18 files changed, 243 insertions(+), 56 deletions(-)
 create mode 100644 engine/third_party/dependencies.bzl
 delete mode 100644 engine/third_party/deps.bzl
 create mode 100644 engine/third_party/googletest/BUILD.bazel
 create mode 100644 engine/third_party/googletest/gcc_printer_patch.patch
 create mode 100644 engine/third_party/googletest/googletest.bzl
 create mode 100644 engine/third_party/nlohmann_json/BUILD.bazel
 create mode 100644 engine/third_party/nlohmann_json/nlohmann_json.BUILD
 create mode 100644 engine/third_party/nlohmann_json/nlohmann_json.bzl
 create mode 100644 utils/ci/conan/recipe/nlohmann_json/all/conandata.yml
 create mode 100644 utils/ci/conan/recipe/nlohmann_json/all/conanfile.py
 create mode 100644 utils/ci/conan/recipe/nlohmann_json/config.yml

diff --git a/NOTICE.md b/NOTICE.md
index 812ea2b8..c623d498 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 c4eebab5..7e6662c2 100644
--- a/README.md
+++ b/README.md
@@ -255,13 +255,14 @@ Converts `NET_ASAM_OPENSCENARIO::v1_3::ITransitionDynamics` object type to [mant
 
 | 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/engine/BUILD.bazel b/engine/BUILD.bazel
index cbc396a1..c53cf6dd 100644
--- a/engine/BUILD.bazel
+++ b/engine/BUILD.bazel
@@ -29,6 +29,7 @@ cc_library(
     visibility = ["//visibility:public"],
     deps = [
         "@mantle_api",
+        "@nlohmann_json",
         "@open_scenario_parser",
         "@stochastics_library",
         "@units_nhh",
@@ -178,11 +179,12 @@ cc_test(
     data = [":open_scenario_engine_test_data"],
     tags = ["test"],
     deps = [
-        ":test_utils",
         ":open_scenario_engine",
         ":open_scenario_engine_test_utils",
+        ":test_utils",
         "@googletest//:gtest_main",
         "@mantle_api//:test_utils",
+        "@nlohmann_json",
     ],
 )
 
diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt
index 72cdb526..d469f08f 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)
@@ -250,11 +249,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 91062dc5..5622781f 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/third_party/dependencies.bzl b/engine/third_party/dependencies.bzl
new file mode 100644
index 00000000..180f0fdb
--- /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 1f832f8a..00000000
--- 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 00000000..5219fee5
--- /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 00000000..a84e6ef6
--- /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 00000000..96cf4768
--- /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 00000000..73a292ee
--- /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 00000000..d615d420
--- /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 00000000..5d2c07b9
--- /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 2cd59264..059e3c1a 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 00000000..3a9de20a
--- /dev/null
+++ b/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml
@@ -0,0 +1,21 @@
+################################################################################
+# Copyright (c) 2024 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"
+  "v3.11.3":
+    url: https://github.com/nlohmann/json/archive/refs/tags/v3.11.3.tar.gz
+    sha256: "0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406"
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 00000000..2a50d134
--- /dev/null
+++ b/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py
@@ -0,0 +1,60 @@
+################################################################################
+# Copyright (c) 2024 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 00000000..c2ea0f20
--- /dev/null
+++ b/utils/ci/conan/recipe/nlohmann_json/config.yml
@@ -0,0 +1,20 @@
+################################################################################
+# Copyright (c) 2024 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"
+
+  "v3.11.3":
+    folder: "all"
diff --git a/utils/ci/scripts/20_configure.sh b/utils/ci/scripts/20_configure.sh
index 303502a8..8dc0b896 100755
--- a/utils/ci/scripts/20_configure.sh
+++ b/utils/ci/scripts/20_configure.sh
@@ -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"
-- 
GitLab


From 6bc0c97f444320d3618249c8796f86be8bdc63d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Paris?= <rene.paris@in-tech.com>
Date: Wed, 12 Feb 2025 15:09:31 +0000
Subject: [PATCH 2/6] feat(JsonParser): Add parser for JSON property strings
 and distribution definitions

This commit adds a generic JSON property parser to parse arbitrary strings
to a concrete object. For reference, several distributions have been defined,
both as json schema and C++ structs.

See readme and corresponding test for more information.
---
 README.md                                     |  11 ++
 engine/CMakeLists.txt                         |   1 +
 .../Property/Distribution/BaseDistribution.h  |  32 ++++++
 .../Distribution/ExponentialDistribution.h    |  28 +++++
 .../Property/Distribution/GammaDistribution.h |  29 +++++
 .../Distribution/LogNormalDistribution.h      |  29 +++++
 .../Distribution/NormalDistribution.h         |  29 +++++
 .../Distribution/UniformDistribution.h        |  28 +++++
 engine/src/Utils/Property/Distributions.h     |  18 +++
 engine/src/Utils/Property/JsonParser.h        |  89 +++++++++++++++
 .../distributions/base_distribution.json      |  20 ++++
 .../exponential_distribution.json             |  30 +++++
 .../distributions/gamma_distribution.json     |  34 ++++++
 .../distributions/lognormal_distribution.json |  35 ++++++
 .../distributions/normal_distrubution.json    |  35 ++++++
 .../distributions/uniform_distribution.json   |  24 ++++
 engine/src/Utils/Property/schemas/readme.md   |  21 ++++
 engine/tests/Utils/JsonParserTest.cpp         | 106 ++++++++++++++++++
 18 files changed, 599 insertions(+)
 create mode 100644 engine/src/Utils/Property/Distribution/BaseDistribution.h
 create mode 100644 engine/src/Utils/Property/Distribution/ExponentialDistribution.h
 create mode 100644 engine/src/Utils/Property/Distribution/GammaDistribution.h
 create mode 100644 engine/src/Utils/Property/Distribution/LogNormalDistribution.h
 create mode 100644 engine/src/Utils/Property/Distribution/NormalDistribution.h
 create mode 100644 engine/src/Utils/Property/Distribution/UniformDistribution.h
 create mode 100644 engine/src/Utils/Property/Distributions.h
 create mode 100644 engine/src/Utils/Property/JsonParser.h
 create mode 100644 engine/src/Utils/Property/schemas/distributions/base_distribution.json
 create mode 100644 engine/src/Utils/Property/schemas/distributions/exponential_distribution.json
 create mode 100644 engine/src/Utils/Property/schemas/distributions/gamma_distribution.json
 create mode 100644 engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json
 create mode 100644 engine/src/Utils/Property/schemas/distributions/normal_distrubution.json
 create mode 100644 engine/src/Utils/Property/schemas/distributions/uniform_distribution.json
 create mode 100644 engine/src/Utils/Property/schemas/readme.md
 create mode 100644 engine/tests/Utils/JsonParserTest.cpp

diff --git a/README.md b/README.md
index 7e6662c2..8bc1f748 100644
--- a/README.md
+++ b/README.md
@@ -251,6 +251,17 @@ 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 provided schemas in [engine/src/Utils/Property/schemas](engine/src/Utils/Property/schemas).
+
 # Dependencies
 
 | Dependency | Commit | Version | License |
diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt
index d469f08f..b490fff9 100644
--- a/engine/CMakeLists.txt
+++ b/engine/CMakeLists.txt
@@ -226,6 +226,7 @@ target_sources(
           ${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/JsonParserTest.cpp
 )
 
 add_custom_command(TARGET ${PROJECT_NAME}Test
diff --git a/engine/src/Utils/Property/Distribution/BaseDistribution.h b/engine/src/Utils/Property/Distribution/BaseDistribution.h
new file mode 100644
index 00000000..30c7e68d
--- /dev/null
+++ b/engine/src/Utils/Property/Distribution/BaseDistribution.h
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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 <string>
+
+namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+{
+
+/// @brief Base class for all distribution types
+///
+/// Distributions and their values need to be defined by
+/// means of string properties. For convenience, they can
+/// be deserialized from a JSON into individual distributions.
+/// See schemas/distributions/*.json for examples.
+struct BaseDistribution
+{
+  std::string type;  ///!< Type of the distribution
+  double min;        ///!< Minimum value
+  double max;        ///!< Maximum value
+};
+
+}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/ExponentialDistribution.h b/engine/src/Utils/Property/Distribution/ExponentialDistribution.h
new file mode 100644
index 00000000..9fc2b083
--- /dev/null
+++ b/engine/src/Utils/Property/Distribution/ExponentialDistribution.h
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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 "BaseDistribution.h"
+
+namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+{
+
+/// @brief Normal distribution
+struct ExponentialDistribution : public BaseDistribution
+{
+  double lamda;  ///!< Rate parameter
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ExponentialDistribution, type, min, max, lamda);
+
+}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/GammaDistribution.h b/engine/src/Utils/Property/Distribution/GammaDistribution.h
new file mode 100644
index 00000000..703c3d02
--- /dev/null
+++ b/engine/src/Utils/Property/Distribution/GammaDistribution.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>
+
+#include "BaseDistribution.h"
+
+namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+{
+
+/// @brief Normal distribution
+struct GammaDistribution : public BaseDistribution
+{
+  double shape;  ///!< Shape parameter alpha
+  double scale;  ///!< Scale parameter theta
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GammaDistribution, type, min, max, shape, scale);
+
+}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/LogNormalDistribution.h b/engine/src/Utils/Property/Distribution/LogNormalDistribution.h
new file mode 100644
index 00000000..a78cb842
--- /dev/null
+++ b/engine/src/Utils/Property/Distribution/LogNormalDistribution.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>
+
+#include "BaseDistribution.h"
+
+namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+{
+
+/// @brief Log-normal distribution
+struct LogNormalDistribution : public BaseDistribution
+{
+  double mu;     ///!< Mean value
+  double sigma;  ///!< Standard deviation
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LogNormalDistribution, type, min, max, mu, sigma);
+
+}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/NormalDistribution.h b/engine/src/Utils/Property/Distribution/NormalDistribution.h
new file mode 100644
index 00000000..aa25d000
--- /dev/null
+++ b/engine/src/Utils/Property/Distribution/NormalDistribution.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>
+
+#include "BaseDistribution.h"
+
+namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+{
+
+/// @brief Normal distribution
+struct NormalDistribution : public BaseDistribution
+{
+  double mean;    ///!< Mean value
+  double stddev;  ///!< Standard deviation
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NormalDistribution, type, min, max, mean, stddev);
+
+}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/UniformDistribution.h b/engine/src/Utils/Property/Distribution/UniformDistribution.h
new file mode 100644
index 00000000..f0c465fd
--- /dev/null
+++ b/engine/src/Utils/Property/Distribution/UniformDistribution.h
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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 "BaseDistribution.h"
+
+namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+{
+
+/// @brief Uniform distribution
+struct UniformDistribution : public BaseDistribution
+{
+  // No additional fields needed
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(UniformDistribution, type, min, max);
+
+}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distributions.h b/engine/src/Utils/Property/Distributions.h
new file mode 100644
index 00000000..75b675a3
--- /dev/null
+++ b/engine/src/Utils/Property/Distributions.h
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * 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 "Distribution/BaseDistribution.h"
+#include "Distribution/ExponentialDistribution.h"
+#include "Distribution/GammaDistribution.h"
+#include "Distribution/LogNormalDistribution.h"
+#include "Distribution/NormalDistribution.h"
+#include "Distribution/UniformDistribution.h"
diff --git a/engine/src/Utils/Property/JsonParser.h b/engine/src/Utils/Property/JsonParser.h
new file mode 100644
index 00000000..706368ee
--- /dev/null
+++ b/engine/src/Utils/Property/JsonParser.h
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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_view>
+
+#include "Utils/Logger.h"
+
+namespace OpenScenarioEngine::v1_3::Utils::Property
+{
+
+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);
+}
+
+/// Try to parse a JSON object from a property 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).
+/// @tparam DeserializedType Type of the object to deserialize (e.g. a distribution)
+/// @param property Property to be parsed
+/// @return Parsed JSON object or throw if parsing failed
+template <typename DeserializedType>
+std::optional<DeserializedType> ParseJson(std::string_view property)
+{
+  if (auto json = nlohmann::json::parse(std::string(property), nullptr, false);
+      !json.is_discarded())
+  {
+    return json.get<DeserializedType>();
+  }
+
+  constexpr const char* DOUBLE_QUOTE{"\""};
+  for (const std::string& search : {"&quot;", "'"})
+  {
+    if (auto json = nlohmann::json::parse(Replace(property, search, DOUBLE_QUOTE), nullptr, false);
+        !json.is_discarded())
+    {
+      return json.get<DeserializedType>();
+    }
+  }
+
+  return std::nullopt;
+}
+
+}  // namespace detail
+
+/// Deserialize a property string into a object using nlohmann::json
+/// @tparam DeserializedType  Type of the object to deserialize (e.g. a distribution)
+/// @param property           Property string to deserialize
+/// @return Deserialized object of type T or empty optional if deserialization failed
+template <typename DeserializedType>
+std::optional<DeserializedType> ReadFromJson(std::string_view property)
+{
+  try
+  {
+    return detail::ParseJson<DeserializedType>(property);
+  }
+  catch (const nlohmann::json::out_of_range&)
+  {
+    Logger::Error("JsonParser: Property '" + std::string(property) + "' does not match expected object");
+  }
+  return std::nullopt;
+}
+
+}  // namespace OpenScenarioEngine::v1_3::Utils::Property
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/base_distribution.json b/engine/src/Utils/Property/schemas/distributions/base_distribution.json
new file mode 100644
index 00000000..cc55de3e
--- /dev/null
+++ b/engine/src/Utils/Property/schemas/distributions/base_distribution.json
@@ -0,0 +1,20 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "BaseDistribution",
+  "type": "object",
+  "properties": {
+    "type": {
+      "type": "string",
+      "description": "The type of the distribution, e.g. 'uniform', 'normal', etc."
+    },
+    "min": {
+      "type": "number",
+      "description": "The minimum value of the distribution"
+    },
+    "max": {
+      "type": "number",
+      "description": "The maximum value of the distribution"
+    }
+  },
+  "required": ["min", "max"]
+}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/exponential_distribution.json b/engine/src/Utils/Property/schemas/distributions/exponential_distribution.json
new file mode 100644
index 00000000..60eae64c
--- /dev/null
+++ b/engine/src/Utils/Property/schemas/distributions/exponential_distribution.json
@@ -0,0 +1,30 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "ExponentialDistribution",
+  "allOf": [
+    {
+      "$ref": "base_distribution.json"
+    },
+    {
+      "if": {
+        "properties": {
+          "type": {
+            "enum": [
+              "exponential", "Exponential", "ExponentialDistribution", "exponential_distribution",
+              "Exponential_Distribution", "exponential-distribution", "Exponential-Distribution",
+              "exponentialDistribution", "exponentialdistribution"]
+          }
+        }
+      },
+      "then": {
+         "properties": {
+           "lambda": {
+             "type": "number",
+             "description": "Rate parameter of the distribution"
+           }
+        },
+        "required": ["type", "min", "max", "lambda"]
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/gamma_distribution.json b/engine/src/Utils/Property/schemas/distributions/gamma_distribution.json
new file mode 100644
index 00000000..c07cb9cf
--- /dev/null
+++ b/engine/src/Utils/Property/schemas/distributions/gamma_distribution.json
@@ -0,0 +1,34 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "GammaDistribution",
+  "allOf": [
+    {
+      "$ref": "base_distribution.json"
+    },
+    {
+      "if": {
+        "properties": {
+          "type": {
+            "enum": [
+              "gamma", "Gamma", "GammaDistribution", "gamma_distribution",
+              "Gamma_Distribution", "gamma-distribution", "Gamma-Distribution",
+              "gammaDistribution", "gammadistribution"]
+          }
+        }
+      },
+      "then": {
+         "properties": {
+           "shape": {
+             "type": "number",
+             "description": "shape parameter alpha"
+           },
+           "scale": {
+             "type": "number",
+             "description": "scale parameter theta"
+           }
+        },
+        "required": ["type", "min", "max", "shape", "scale"]
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json b/engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json
new file mode 100644
index 00000000..90ea615d
--- /dev/null
+++ b/engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json
@@ -0,0 +1,35 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "LogNormalDistribution",
+  "allOf": [
+    {
+      "$ref": "base_distribution.json"
+    },
+    {
+      "if": {
+        "properties": {
+          "type": {
+            "enum": [
+              "lognormal", "LogNormal", "LogNormalDistribution", "lognormal_distribution",
+              "LogNormal_Distribution", "lognormal-distribution", "LogNormal-Distribution",
+              "logNormalDistribution", "lognormaldistribution"
+            ]
+          }
+        }
+      },
+      "then": {
+        "properties": {
+          "mu": {
+            "type": "number",
+            "description": "The mean of the natural logarithm of the distribution"
+          },
+          "sigma": {
+            "type": "number",
+            "description": "The standard deviation of the natural logarithm of the distribution"
+          }
+        },
+        "required": ["type", "min", "max", "mu", "sigma"]
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/normal_distrubution.json b/engine/src/Utils/Property/schemas/distributions/normal_distrubution.json
new file mode 100644
index 00000000..e4b3be83
--- /dev/null
+++ b/engine/src/Utils/Property/schemas/distributions/normal_distrubution.json
@@ -0,0 +1,35 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "NormalDistribution",
+  "allOf": [
+    {
+      "$ref": "base_distribution.json"
+    },
+    {
+      "if": {
+        "properties": {
+          "type": {
+            "enum": [
+              "normal", "Normal", "NormalDistribution", "normal_distribution",
+              "Normal_Distribution", "normal-distribution", "Normal-Distribution",
+              "normalDistribution", "normaldistribution"
+            ]
+          }
+        }
+      },
+      "then": {
+        "properties": {
+          "mean": {
+            "type": "number",
+            "description": "The mean value of the distribution"
+          },
+          "stddev": {
+            "type": "number",
+            "description": "The standard deviation of the distribution"
+          }
+        },
+        "required": ["type", "min", "max", "mean", "stddev"]
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/uniform_distribution.json b/engine/src/Utils/Property/schemas/distributions/uniform_distribution.json
new file mode 100644
index 00000000..c7d19757
--- /dev/null
+++ b/engine/src/Utils/Property/schemas/distributions/uniform_distribution.json
@@ -0,0 +1,24 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "UniformDistribution",
+  "allOf": [
+    {
+      "$ref": "base_distribution.json"
+    },
+    {
+      "if": {
+        "properties": {
+          "type": {
+            "enum": [
+              "uniform", "Uniform", "UniformDistribution", "uniform_distribution",
+              "Uniform_Distribution", "uniform-distribution", "Uniform-Distribution",
+              "uniformDistribution", "uniformdistribution"]
+          }
+        }
+      },
+      "then": {
+        "required": ["type", "min", "max"]
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/readme.md b/engine/src/Utils/Property/schemas/readme.md
new file mode 100644
index 00000000..9e2391a1
--- /dev/null
+++ b/engine/src/Utils/Property/schemas/readme.md
@@ -0,0 +1,21 @@
+# JSON Schemas for Property Strings
+
+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.
+
+## Usage Example
+
+Here is an example of how to use these schemas within an OpenSCENARIO description:
+
+```xml
+<Property key="MyCustomProperty" value="{'type': 'NormalDistribution', 'min': 0.0, 'max': 10.0, 'mean': 7.5, 'stddev': 4.0 }">
+```
+
+In this example, a property with the key `MyCustomProperty` is defined using a JSON string that specifies a `NormalDistribution` with the following parameters:
+- `min`: 0.0
+- `max`: 10.0
+- `mean`: 7.5
+- `stddev`: 4.0
+
+Refer to the JSON schemas in this folder for more details on how to define other types of distributions and properties.
+```
\ No newline at end of file
diff --git a/engine/tests/Utils/JsonParserTest.cpp b/engine/tests/Utils/JsonParserTest.cpp
new file mode 100644
index 00000000..58ef5dff
--- /dev/null
+++ b/engine/tests/Utils/JsonParserTest.cpp
@@ -0,0 +1,106 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Utils/Property/Distributions.h"
+#include "Utils/Property/JsonParser.h"
+
+using testing::DoubleEq;
+using testing::Eq;
+
+using namespace OpenScenarioEngine::v1_3::Utils::Property;
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidUniformDistribition_DeserializesToObject)
+{
+  auto uniformDistribution = ReadFromJson<Distribution::UniformDistribution>(
+      R"({"type": "uniform", "min": 1.2, "max": 3.4 })");
+
+  ASSERT_TRUE(uniformDistribution.has_value());
+  EXPECT_THAT(uniformDistribution->type, "uniform");
+  EXPECT_THAT(uniformDistribution->min, DoubleEq(1.2));
+  EXPECT_THAT(uniformDistribution->max, DoubleEq(3.4));
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringsAreEscaped_DeserializesToObject)
+{
+  auto uniformDistribution = ReadFromJson<Distribution::UniformDistribution>(
+      R"({&quot;type&quot;: &quot;uniform&quot;, &quot;min&quot;: 1.2, &quot;max&quot;: 3.4 })");
+
+  ASSERT_TRUE(uniformDistribution.has_value());
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringsAreInSingleQuotes_DeserializesToObject)
+{
+  auto uniformDistribution = ReadFromJson<Distribution::UniformDistribution>(
+      R"({'type': 'uniform', 'min': 1.2, 'max': 3.4 })");
+
+  ASSERT_TRUE(uniformDistribution.has_value());
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidNormalDistribition_DeserializesToObject)
+{
+  auto normalDistribution = ReadFromJson<Distribution::NormalDistribution>(
+      R"({"type": "normal", "min": 1.2, "max": 3.4 , "mean": 5.6, "stddev": 7.8 })");
+
+  ASSERT_TRUE(normalDistribution.has_value());
+  EXPECT_THAT(normalDistribution->type, "normal");
+  EXPECT_THAT(normalDistribution->min, DoubleEq(1.2));
+  EXPECT_THAT(normalDistribution->max, DoubleEq(3.4));
+  EXPECT_THAT(normalDistribution->mean, DoubleEq(5.6));
+  EXPECT_THAT(normalDistribution->stddev, DoubleEq(7.8));
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidLogNormalDistribition_DeserializesToObject)
+{
+  auto logNormalDistribution = ReadFromJson<Distribution::LogNormalDistribution>(
+      R"({"type": "lognormal", "min": 1.2, "max": 3.4, "mu": 5.6, "sigma": 7.8 })");
+
+  ASSERT_TRUE(logNormalDistribution.has_value());
+  EXPECT_THAT(logNormalDistribution->type, "lognormal");
+  EXPECT_THAT(logNormalDistribution->min, DoubleEq(1.2));
+  EXPECT_THAT(logNormalDistribution->max, DoubleEq(3.4));
+  EXPECT_THAT(logNormalDistribution->mu, DoubleEq(5.6));
+  EXPECT_THAT(logNormalDistribution->sigma, DoubleEq(7.8));
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidGammaDistribition_DeserializesToObject)
+{
+  auto gammaDistribution = ReadFromJson<Distribution::GammaDistribution>(
+      R"({"type": "gamma", "min": 1.2, "max": 3.4, "shape": 5.6, "scale": 7.8 })");
+
+  ASSERT_TRUE(gammaDistribution.has_value());
+  EXPECT_THAT(gammaDistribution->type, "gamma");
+  EXPECT_THAT(gammaDistribution->min, DoubleEq(1.2));
+  EXPECT_THAT(gammaDistribution->max, DoubleEq(3.4));
+  EXPECT_THAT(gammaDistribution->shape, DoubleEq(5.6));
+  EXPECT_THAT(gammaDistribution->scale, DoubleEq(7.8));
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidExponentialDistribition_DeserializesToObject)
+{
+  auto exponentialDistribution = ReadFromJson<Distribution::ExponentialDistribution>(
+      R"({"type": "exponential", "min": 1.2, "max": 3.4, "lamda": 5.6 })");
+
+  ASSERT_TRUE(exponentialDistribution.has_value());
+  EXPECT_THAT(exponentialDistribution->type, "exponential");
+  EXPECT_THAT(exponentialDistribution->min, DoubleEq(1.2));
+  EXPECT_THAT(exponentialDistribution->max, DoubleEq(3.4));
+  EXPECT_THAT(exponentialDistribution->lamda, DoubleEq(5.6));
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenStringIsInvalidJson_ReturnsEmptyOptional)
+{
+  EXPECT_THAT(ReadFromJson<Distribution::UniformDistribution>("this is not a json"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::NormalDistribution>("this is not a json"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::LogNormalDistribution>("this is not a json"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::GammaDistribution>("this is not a json"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::ExponentialDistribution>("this is not a json"), Eq(std::nullopt));
+}
+
+TEST(JsonParserTest, GivenPropertyString_WhenJsonDoesNotMatch_ReturnsEmptyOptional)
+{
+  EXPECT_THAT(ReadFromJson<Distribution::UniformDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::NormalDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::LogNormalDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::GammaDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
+  EXPECT_THAT(ReadFromJson<Distribution::ExponentialDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
+}
-- 
GitLab


From bf23b2ddb81dcb3cf0f7c3d390fa456060fed7c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Paris?= <rene.paris@in-tech.com>
Date: Wed, 19 Mar 2025 14:52:11 +0000
Subject: [PATCH 3/6] feat(PropertyInterpreter): Generic structure for custom
 property interpretation

---
 .gitignore                                    |  15 +-
 .../exponential_distribution.json             |  20 +++
 doc/json_schemas/gamma_distribution.json      |  28 +++
 doc/json_schemas/lognormal_distribution.json  |  20 +++
 doc/json_schemas/normal_distrubution.json     |  28 +++
 .../schemas => doc/json_schemas}/readme.md    |   0
 doc/json_schemas/uniform_distribution.json    |  18 ++
 engine/CMakeLists.txt                         |   4 +-
 engine/cmake/generated_files.cmake            |  15 ++
 engine/src/Utils/JsonParsing.cpp              |  60 +++++++
 engine/src/Utils/JsonParsing.h                |  63 +++++++
 .../Property/Distribution/BaseDistribution.h  |  32 ----
 .../Distribution/ExponentialDistribution.h    |  28 ---
 .../Property/Distribution/GammaDistribution.h |  29 ---
 .../Distribution/LogNormalDistribution.h      |  29 ---
 .../Distribution/NormalDistribution.h         |  29 ---
 engine/src/Utils/Property/JsonParser.h        |  89 ----------
 .../distributions/base_distribution.json      |  20 ---
 .../exponential_distribution.json             |  30 ----
 .../distributions/gamma_distribution.json     |  34 ----
 .../distributions/lognormal_distribution.json |  35 ----
 .../distributions/normal_distrubution.json    |  35 ----
 .../distributions/uniform_distribution.json   |  24 ---
 .../DistributionInterpreter.cpp               |  65 +++++++
 .../DistributionInterpreter.h                 | 104 +++++++++++
 .../Distributions.h                           |  11 +-
 .../Distributions/ExponentialDistribution.h   |  31 ++++
 .../Distributions/GammaDistribution.h         |  39 +++++
 .../Distributions/LogNormalDistribution.h     |  33 ++++
 .../Distributions/NormalDistribution.h        |  33 ++++
 .../Distributions}/UniformDistribution.h      |  17 +-
 .../PropertyInterpretation/Interpreter.h      | 123 +++++++++++++
 .../PropertyInterpretation/JsonInterpreter.h  |  43 +++++
 .../PropertyInterpretation/RawValueRelay.h    |  43 +++++
 engine/src/Utils/PropertyInterpreter.cpp      |  71 ++++++++
 engine/src/Utils/PropertyInterpreter.h        |  55 ++++++
 engine/tests/Utils/DistributionTest.cpp       | 160 +++++++++++++++++
 engine/tests/Utils/JsonParserTest.cpp         | 106 -----------
 engine/tests/Utils/JsonParsingTest.cpp        |  80 +++++++++
 .../tests/Utils/PropertyInterpreterTest.cpp   | 165 ++++++++++++++++++
 40 files changed, 1324 insertions(+), 540 deletions(-)
 create mode 100644 doc/json_schemas/exponential_distribution.json
 create mode 100644 doc/json_schemas/gamma_distribution.json
 create mode 100644 doc/json_schemas/lognormal_distribution.json
 create mode 100644 doc/json_schemas/normal_distrubution.json
 rename {engine/src/Utils/Property/schemas => doc/json_schemas}/readme.md (100%)
 create mode 100644 doc/json_schemas/uniform_distribution.json
 create mode 100644 engine/src/Utils/JsonParsing.cpp
 create mode 100644 engine/src/Utils/JsonParsing.h
 delete mode 100644 engine/src/Utils/Property/Distribution/BaseDistribution.h
 delete mode 100644 engine/src/Utils/Property/Distribution/ExponentialDistribution.h
 delete mode 100644 engine/src/Utils/Property/Distribution/GammaDistribution.h
 delete mode 100644 engine/src/Utils/Property/Distribution/LogNormalDistribution.h
 delete mode 100644 engine/src/Utils/Property/Distribution/NormalDistribution.h
 delete mode 100644 engine/src/Utils/Property/JsonParser.h
 delete mode 100644 engine/src/Utils/Property/schemas/distributions/base_distribution.json
 delete mode 100644 engine/src/Utils/Property/schemas/distributions/exponential_distribution.json
 delete mode 100644 engine/src/Utils/Property/schemas/distributions/gamma_distribution.json
 delete mode 100644 engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json
 delete mode 100644 engine/src/Utils/Property/schemas/distributions/normal_distrubution.json
 delete mode 100644 engine/src/Utils/Property/schemas/distributions/uniform_distribution.json
 create mode 100644 engine/src/Utils/PropertyInterpretation/DistributionInterpreter.cpp
 create mode 100644 engine/src/Utils/PropertyInterpretation/DistributionInterpreter.h
 rename engine/src/Utils/{Property => PropertyInterpretation}/Distributions.h (63%)
 create mode 100644 engine/src/Utils/PropertyInterpretation/Distributions/ExponentialDistribution.h
 create mode 100644 engine/src/Utils/PropertyInterpretation/Distributions/GammaDistribution.h
 create mode 100644 engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h
 create mode 100644 engine/src/Utils/PropertyInterpretation/Distributions/NormalDistribution.h
 rename engine/src/Utils/{Property/Distribution => PropertyInterpretation/Distributions}/UniformDistribution.h (56%)
 create mode 100644 engine/src/Utils/PropertyInterpretation/Interpreter.h
 create mode 100644 engine/src/Utils/PropertyInterpretation/JsonInterpreter.h
 create mode 100644 engine/src/Utils/PropertyInterpretation/RawValueRelay.h
 create mode 100644 engine/src/Utils/PropertyInterpreter.cpp
 create mode 100644 engine/src/Utils/PropertyInterpreter.h
 create mode 100644 engine/tests/Utils/DistributionTest.cpp
 delete mode 100644 engine/tests/Utils/JsonParserTest.cpp
 create mode 100644 engine/tests/Utils/JsonParsingTest.cpp
 create mode 100644 engine/tests/Utils/PropertyInterpreterTest.cpp

diff --git a/.gitignore b/.gitignore
index fc6f72e2..f87582a8 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/doc/json_schemas/exponential_distribution.json b/doc/json_schemas/exponential_distribution.json
new file mode 100644
index 00000000..1d294b38
--- /dev/null
+++ b/doc/json_schemas/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_schemas/gamma_distribution.json b/doc/json_schemas/gamma_distribution.json
new file mode 100644
index 00000000..479cb799
--- /dev/null
+++ b/doc/json_schemas/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_schemas/lognormal_distribution.json b/doc/json_schemas/lognormal_distribution.json
new file mode 100644
index 00000000..1d294b38
--- /dev/null
+++ b/doc/json_schemas/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_schemas/normal_distrubution.json b/doc/json_schemas/normal_distrubution.json
new file mode 100644
index 00000000..3021ae8e
--- /dev/null
+++ b/doc/json_schemas/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/engine/src/Utils/Property/schemas/readme.md b/doc/json_schemas/readme.md
similarity index 100%
rename from engine/src/Utils/Property/schemas/readme.md
rename to doc/json_schemas/readme.md
diff --git a/doc/json_schemas/uniform_distribution.json b/doc/json_schemas/uniform_distribution.json
new file mode 100644
index 00000000..1532d5e2
--- /dev/null
+++ b/doc/json_schemas/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/CMakeLists.txt b/engine/CMakeLists.txt
index b490fff9..74ad6e96 100644
--- a/engine/CMakeLists.txt
+++ b/engine/CMakeLists.txt
@@ -223,10 +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/JsonParserTest.cpp
+          ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/JsonParsingTest.cpp
+          ${CMAKE_CURRENT_LIST_DIR}/tests/Utils/PropertyInterpreterTest.cpp
 )
 
 add_custom_command(TARGET ${PROJECT_NAME}Test
diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake
index 6644cd17..be6db101 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 00000000..dc26d3b5
--- /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 00000000..ba716982
--- /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
\ No newline at end of file
diff --git a/engine/src/Utils/Property/Distribution/BaseDistribution.h b/engine/src/Utils/Property/Distribution/BaseDistribution.h
deleted file mode 100644
index 30c7e68d..00000000
--- a/engine/src/Utils/Property/Distribution/BaseDistribution.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*******************************************************************************
- * 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 <string>
-
-namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
-{
-
-/// @brief Base class for all distribution types
-///
-/// Distributions and their values need to be defined by
-/// means of string properties. For convenience, they can
-/// be deserialized from a JSON into individual distributions.
-/// See schemas/distributions/*.json for examples.
-struct BaseDistribution
-{
-  std::string type;  ///!< Type of the distribution
-  double min;        ///!< Minimum value
-  double max;        ///!< Maximum value
-};
-
-}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/ExponentialDistribution.h b/engine/src/Utils/Property/Distribution/ExponentialDistribution.h
deleted file mode 100644
index 9fc2b083..00000000
--- a/engine/src/Utils/Property/Distribution/ExponentialDistribution.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*******************************************************************************
- * 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 "BaseDistribution.h"
-
-namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
-{
-
-/// @brief Normal distribution
-struct ExponentialDistribution : public BaseDistribution
-{
-  double lamda;  ///!< Rate parameter
-};
-
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ExponentialDistribution, type, min, max, lamda);
-
-}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/GammaDistribution.h b/engine/src/Utils/Property/Distribution/GammaDistribution.h
deleted file mode 100644
index 703c3d02..00000000
--- a/engine/src/Utils/Property/Distribution/GammaDistribution.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*******************************************************************************
- * 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 "BaseDistribution.h"
-
-namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
-{
-
-/// @brief Normal distribution
-struct GammaDistribution : public BaseDistribution
-{
-  double shape;  ///!< Shape parameter alpha
-  double scale;  ///!< Scale parameter theta
-};
-
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GammaDistribution, type, min, max, shape, scale);
-
-}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/LogNormalDistribution.h b/engine/src/Utils/Property/Distribution/LogNormalDistribution.h
deleted file mode 100644
index a78cb842..00000000
--- a/engine/src/Utils/Property/Distribution/LogNormalDistribution.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*******************************************************************************
- * 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 "BaseDistribution.h"
-
-namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
-{
-
-/// @brief Log-normal distribution
-struct LogNormalDistribution : public BaseDistribution
-{
-  double mu;     ///!< Mean value
-  double sigma;  ///!< Standard deviation
-};
-
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LogNormalDistribution, type, min, max, mu, sigma);
-
-}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/Distribution/NormalDistribution.h b/engine/src/Utils/Property/Distribution/NormalDistribution.h
deleted file mode 100644
index aa25d000..00000000
--- a/engine/src/Utils/Property/Distribution/NormalDistribution.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*******************************************************************************
- * 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 "BaseDistribution.h"
-
-namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
-{
-
-/// @brief Normal distribution
-struct NormalDistribution : public BaseDistribution
-{
-  double mean;    ///!< Mean value
-  double stddev;  ///!< Standard deviation
-};
-
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NormalDistribution, type, min, max, mean, stddev);
-
-}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
diff --git a/engine/src/Utils/Property/JsonParser.h b/engine/src/Utils/Property/JsonParser.h
deleted file mode 100644
index 706368ee..00000000
--- a/engine/src/Utils/Property/JsonParser.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*******************************************************************************
- * 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_view>
-
-#include "Utils/Logger.h"
-
-namespace OpenScenarioEngine::v1_3::Utils::Property
-{
-
-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);
-}
-
-/// Try to parse a JSON object from a property 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).
-/// @tparam DeserializedType Type of the object to deserialize (e.g. a distribution)
-/// @param property Property to be parsed
-/// @return Parsed JSON object or throw if parsing failed
-template <typename DeserializedType>
-std::optional<DeserializedType> ParseJson(std::string_view property)
-{
-  if (auto json = nlohmann::json::parse(std::string(property), nullptr, false);
-      !json.is_discarded())
-  {
-    return json.get<DeserializedType>();
-  }
-
-  constexpr const char* DOUBLE_QUOTE{"\""};
-  for (const std::string& search : {"&quot;", "'"})
-  {
-    if (auto json = nlohmann::json::parse(Replace(property, search, DOUBLE_QUOTE), nullptr, false);
-        !json.is_discarded())
-    {
-      return json.get<DeserializedType>();
-    }
-  }
-
-  return std::nullopt;
-}
-
-}  // namespace detail
-
-/// Deserialize a property string into a object using nlohmann::json
-/// @tparam DeserializedType  Type of the object to deserialize (e.g. a distribution)
-/// @param property           Property string to deserialize
-/// @return Deserialized object of type T or empty optional if deserialization failed
-template <typename DeserializedType>
-std::optional<DeserializedType> ReadFromJson(std::string_view property)
-{
-  try
-  {
-    return detail::ParseJson<DeserializedType>(property);
-  }
-  catch (const nlohmann::json::out_of_range&)
-  {
-    Logger::Error("JsonParser: Property '" + std::string(property) + "' does not match expected object");
-  }
-  return std::nullopt;
-}
-
-}  // namespace OpenScenarioEngine::v1_3::Utils::Property
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/base_distribution.json b/engine/src/Utils/Property/schemas/distributions/base_distribution.json
deleted file mode 100644
index cc55de3e..00000000
--- a/engine/src/Utils/Property/schemas/distributions/base_distribution.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "BaseDistribution",
-  "type": "object",
-  "properties": {
-    "type": {
-      "type": "string",
-      "description": "The type of the distribution, e.g. 'uniform', 'normal', etc."
-    },
-    "min": {
-      "type": "number",
-      "description": "The minimum value of the distribution"
-    },
-    "max": {
-      "type": "number",
-      "description": "The maximum value of the distribution"
-    }
-  },
-  "required": ["min", "max"]
-}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/exponential_distribution.json b/engine/src/Utils/Property/schemas/distributions/exponential_distribution.json
deleted file mode 100644
index 60eae64c..00000000
--- a/engine/src/Utils/Property/schemas/distributions/exponential_distribution.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "ExponentialDistribution",
-  "allOf": [
-    {
-      "$ref": "base_distribution.json"
-    },
-    {
-      "if": {
-        "properties": {
-          "type": {
-            "enum": [
-              "exponential", "Exponential", "ExponentialDistribution", "exponential_distribution",
-              "Exponential_Distribution", "exponential-distribution", "Exponential-Distribution",
-              "exponentialDistribution", "exponentialdistribution"]
-          }
-        }
-      },
-      "then": {
-         "properties": {
-           "lambda": {
-             "type": "number",
-             "description": "Rate parameter of the distribution"
-           }
-        },
-        "required": ["type", "min", "max", "lambda"]
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/gamma_distribution.json b/engine/src/Utils/Property/schemas/distributions/gamma_distribution.json
deleted file mode 100644
index c07cb9cf..00000000
--- a/engine/src/Utils/Property/schemas/distributions/gamma_distribution.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "GammaDistribution",
-  "allOf": [
-    {
-      "$ref": "base_distribution.json"
-    },
-    {
-      "if": {
-        "properties": {
-          "type": {
-            "enum": [
-              "gamma", "Gamma", "GammaDistribution", "gamma_distribution",
-              "Gamma_Distribution", "gamma-distribution", "Gamma-Distribution",
-              "gammaDistribution", "gammadistribution"]
-          }
-        }
-      },
-      "then": {
-         "properties": {
-           "shape": {
-             "type": "number",
-             "description": "shape parameter alpha"
-           },
-           "scale": {
-             "type": "number",
-             "description": "scale parameter theta"
-           }
-        },
-        "required": ["type", "min", "max", "shape", "scale"]
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json b/engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json
deleted file mode 100644
index 90ea615d..00000000
--- a/engine/src/Utils/Property/schemas/distributions/lognormal_distribution.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "LogNormalDistribution",
-  "allOf": [
-    {
-      "$ref": "base_distribution.json"
-    },
-    {
-      "if": {
-        "properties": {
-          "type": {
-            "enum": [
-              "lognormal", "LogNormal", "LogNormalDistribution", "lognormal_distribution",
-              "LogNormal_Distribution", "lognormal-distribution", "LogNormal-Distribution",
-              "logNormalDistribution", "lognormaldistribution"
-            ]
-          }
-        }
-      },
-      "then": {
-        "properties": {
-          "mu": {
-            "type": "number",
-            "description": "The mean of the natural logarithm of the distribution"
-          },
-          "sigma": {
-            "type": "number",
-            "description": "The standard deviation of the natural logarithm of the distribution"
-          }
-        },
-        "required": ["type", "min", "max", "mu", "sigma"]
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/normal_distrubution.json b/engine/src/Utils/Property/schemas/distributions/normal_distrubution.json
deleted file mode 100644
index e4b3be83..00000000
--- a/engine/src/Utils/Property/schemas/distributions/normal_distrubution.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "NormalDistribution",
-  "allOf": [
-    {
-      "$ref": "base_distribution.json"
-    },
-    {
-      "if": {
-        "properties": {
-          "type": {
-            "enum": [
-              "normal", "Normal", "NormalDistribution", "normal_distribution",
-              "Normal_Distribution", "normal-distribution", "Normal-Distribution",
-              "normalDistribution", "normaldistribution"
-            ]
-          }
-        }
-      },
-      "then": {
-        "properties": {
-          "mean": {
-            "type": "number",
-            "description": "The mean value of the distribution"
-          },
-          "stddev": {
-            "type": "number",
-            "description": "The standard deviation of the distribution"
-          }
-        },
-        "required": ["type", "min", "max", "mean", "stddev"]
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/engine/src/Utils/Property/schemas/distributions/uniform_distribution.json b/engine/src/Utils/Property/schemas/distributions/uniform_distribution.json
deleted file mode 100644
index c7d19757..00000000
--- a/engine/src/Utils/Property/schemas/distributions/uniform_distribution.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "UniformDistribution",
-  "allOf": [
-    {
-      "$ref": "base_distribution.json"
-    },
-    {
-      "if": {
-        "properties": {
-          "type": {
-            "enum": [
-              "uniform", "Uniform", "UniformDistribution", "uniform_distribution",
-              "Uniform_Distribution", "uniform-distribution", "Uniform-Distribution",
-              "uniformDistribution", "uniformdistribution"]
-          }
-        }
-      },
-      "then": {
-        "required": ["type", "min", "max"]
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.cpp b/engine/src/Utils/PropertyInterpretation/DistributionInterpreter.cpp
new file mode 100644
index 00000000..479a44cc
--- /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 00000000..7b16b836
--- /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/Property/Distributions.h b/engine/src/Utils/PropertyInterpretation/Distributions.h
similarity index 63%
rename from engine/src/Utils/Property/Distributions.h
rename to engine/src/Utils/PropertyInterpretation/Distributions.h
index 75b675a3..c4919f77 100644
--- a/engine/src/Utils/Property/Distributions.h
+++ b/engine/src/Utils/PropertyInterpretation/Distributions.h
@@ -10,9 +10,8 @@
 
 #pragma once
 
-#include "Distribution/BaseDistribution.h"
-#include "Distribution/ExponentialDistribution.h"
-#include "Distribution/GammaDistribution.h"
-#include "Distribution/LogNormalDistribution.h"
-#include "Distribution/NormalDistribution.h"
-#include "Distribution/UniformDistribution.h"
+#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 00000000..7605671f
--- /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 00000000..1e382fe7
--- /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 00000000..1aca4fc4
--- /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
+{
+
+/// Normal distribution
+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 00000000..d2a572f4
--- /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/Property/Distribution/UniformDistribution.h b/engine/src/Utils/PropertyInterpretation/Distributions/UniformDistribution.h
similarity index 56%
rename from engine/src/Utils/Property/Distribution/UniformDistribution.h
rename to engine/src/Utils/PropertyInterpretation/Distributions/UniformDistribution.h
index f0c465fd..05b4b145 100644
--- a/engine/src/Utils/Property/Distribution/UniformDistribution.h
+++ b/engine/src/Utils/PropertyInterpretation/Distributions/UniformDistribution.h
@@ -12,17 +12,18 @@
 
 #include <nlohmann/json.hpp>
 
-#include "BaseDistribution.h"
-
-namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+namespace OpenScenarioEngine::v1_3::PropertyInterpretation
 {
 
-/// @brief Uniform distribution
-struct UniformDistribution : public BaseDistribution
+/// Uniform distribution
+struct UniformDistribution
 {
-  // No additional fields needed
+  double lowerLimit;  ///!< Lower limit
+  double upperLimit;  ///!< Upper limit
 };
 
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(UniformDistribution, type, min, max);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(UniformDistribution,
+                                   lowerLimit,
+                                   upperLimit);
 
-}  // namespace OpenScenarioEngine::v1_3::Utils::Property::Distribution
+}  // 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 00000000..0df771c4
--- /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 00000000..be2baa76
--- /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 00000000..26246b48
--- /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 00000000..4071c912
--- /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 00000000..e115d630
--- /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 00000000..51c4ce2b
--- /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/JsonParserTest.cpp b/engine/tests/Utils/JsonParserTest.cpp
deleted file mode 100644
index 58ef5dff..00000000
--- a/engine/tests/Utils/JsonParserTest.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "Utils/Property/Distributions.h"
-#include "Utils/Property/JsonParser.h"
-
-using testing::DoubleEq;
-using testing::Eq;
-
-using namespace OpenScenarioEngine::v1_3::Utils::Property;
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidUniformDistribition_DeserializesToObject)
-{
-  auto uniformDistribution = ReadFromJson<Distribution::UniformDistribution>(
-      R"({"type": "uniform", "min": 1.2, "max": 3.4 })");
-
-  ASSERT_TRUE(uniformDistribution.has_value());
-  EXPECT_THAT(uniformDistribution->type, "uniform");
-  EXPECT_THAT(uniformDistribution->min, DoubleEq(1.2));
-  EXPECT_THAT(uniformDistribution->max, DoubleEq(3.4));
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringsAreEscaped_DeserializesToObject)
-{
-  auto uniformDistribution = ReadFromJson<Distribution::UniformDistribution>(
-      R"({&quot;type&quot;: &quot;uniform&quot;, &quot;min&quot;: 1.2, &quot;max&quot;: 3.4 })");
-
-  ASSERT_TRUE(uniformDistribution.has_value());
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringsAreInSingleQuotes_DeserializesToObject)
-{
-  auto uniformDistribution = ReadFromJson<Distribution::UniformDistribution>(
-      R"({'type': 'uniform', 'min': 1.2, 'max': 3.4 })");
-
-  ASSERT_TRUE(uniformDistribution.has_value());
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidNormalDistribition_DeserializesToObject)
-{
-  auto normalDistribution = ReadFromJson<Distribution::NormalDistribution>(
-      R"({"type": "normal", "min": 1.2, "max": 3.4 , "mean": 5.6, "stddev": 7.8 })");
-
-  ASSERT_TRUE(normalDistribution.has_value());
-  EXPECT_THAT(normalDistribution->type, "normal");
-  EXPECT_THAT(normalDistribution->min, DoubleEq(1.2));
-  EXPECT_THAT(normalDistribution->max, DoubleEq(3.4));
-  EXPECT_THAT(normalDistribution->mean, DoubleEq(5.6));
-  EXPECT_THAT(normalDistribution->stddev, DoubleEq(7.8));
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidLogNormalDistribition_DeserializesToObject)
-{
-  auto logNormalDistribution = ReadFromJson<Distribution::LogNormalDistribution>(
-      R"({"type": "lognormal", "min": 1.2, "max": 3.4, "mu": 5.6, "sigma": 7.8 })");
-
-  ASSERT_TRUE(logNormalDistribution.has_value());
-  EXPECT_THAT(logNormalDistribution->type, "lognormal");
-  EXPECT_THAT(logNormalDistribution->min, DoubleEq(1.2));
-  EXPECT_THAT(logNormalDistribution->max, DoubleEq(3.4));
-  EXPECT_THAT(logNormalDistribution->mu, DoubleEq(5.6));
-  EXPECT_THAT(logNormalDistribution->sigma, DoubleEq(7.8));
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidGammaDistribition_DeserializesToObject)
-{
-  auto gammaDistribution = ReadFromJson<Distribution::GammaDistribution>(
-      R"({"type": "gamma", "min": 1.2, "max": 3.4, "shape": 5.6, "scale": 7.8 })");
-
-  ASSERT_TRUE(gammaDistribution.has_value());
-  EXPECT_THAT(gammaDistribution->type, "gamma");
-  EXPECT_THAT(gammaDistribution->min, DoubleEq(1.2));
-  EXPECT_THAT(gammaDistribution->max, DoubleEq(3.4));
-  EXPECT_THAT(gammaDistribution->shape, DoubleEq(5.6));
-  EXPECT_THAT(gammaDistribution->scale, DoubleEq(7.8));
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringIsValidExponentialDistribition_DeserializesToObject)
-{
-  auto exponentialDistribution = ReadFromJson<Distribution::ExponentialDistribution>(
-      R"({"type": "exponential", "min": 1.2, "max": 3.4, "lamda": 5.6 })");
-
-  ASSERT_TRUE(exponentialDistribution.has_value());
-  EXPECT_THAT(exponentialDistribution->type, "exponential");
-  EXPECT_THAT(exponentialDistribution->min, DoubleEq(1.2));
-  EXPECT_THAT(exponentialDistribution->max, DoubleEq(3.4));
-  EXPECT_THAT(exponentialDistribution->lamda, DoubleEq(5.6));
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenStringIsInvalidJson_ReturnsEmptyOptional)
-{
-  EXPECT_THAT(ReadFromJson<Distribution::UniformDistribution>("this is not a json"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::NormalDistribution>("this is not a json"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::LogNormalDistribution>("this is not a json"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::GammaDistribution>("this is not a json"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::ExponentialDistribution>("this is not a json"), Eq(std::nullopt));
-}
-
-TEST(JsonParserTest, GivenPropertyString_WhenJsonDoesNotMatch_ReturnsEmptyOptional)
-{
-  EXPECT_THAT(ReadFromJson<Distribution::UniformDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::NormalDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::LogNormalDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::GammaDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
-  EXPECT_THAT(ReadFromJson<Distribution::ExponentialDistribution>(R"({"unknown_field": "some_value"})"), Eq(std::nullopt));
-}
diff --git a/engine/tests/Utils/JsonParsingTest.cpp b/engine/tests/Utils/JsonParsingTest.cpp
new file mode 100644
index 00000000..d6287007
--- /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 00000000..f17a955f
--- /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
-- 
GitLab


From 90239b53c938529bcf4968448f4a0bfc2b80cfdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Paris?= <rene.paris@in-tech.com>
Date: Thu, 1 May 2025 08:16:11 +0200
Subject: [PATCH 4/6] doc: add doc for Json properties

---
 README.md                                     |  2 +-
 .../exponential_distribution.json             |  0
 .../gamma_distribution.json                   |  0
 .../lognormal_distribution.json               |  0
 .../normal_distrubution.json                  |  0
 doc/json_properties/readme.md                 | 40 +++++++++++++++++++
 .../uniform_distribution.json                 |  0
 doc/json_schemas/readme.md                    | 21 ----------
 engine/.gitignore                             |  1 +
 9 files changed, 42 insertions(+), 22 deletions(-)
 rename doc/{json_schemas => json_properties}/exponential_distribution.json (100%)
 rename doc/{json_schemas => json_properties}/gamma_distribution.json (100%)
 rename doc/{json_schemas => json_properties}/lognormal_distribution.json (100%)
 rename doc/{json_schemas => json_properties}/normal_distrubution.json (100%)
 create mode 100644 doc/json_properties/readme.md
 rename doc/{json_schemas => json_properties}/uniform_distribution.json (100%)
 delete mode 100644 doc/json_schemas/readme.md

diff --git a/README.md b/README.md
index 8bc1f748..79ebdf6c 100644
--- a/README.md
+++ b/README.md
@@ -260,7 +260,7 @@ To facilitate this, the OpenScenarioEngine uses JSON to parse string-type proper
 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 provided schemas in [engine/src/Utils/Property/schemas](engine/src/Utils/Property/schemas).
+For more information on how to define these properties, refer to the documentation an provided schemas in [doc/json_properties](./doc/json_properties).
 
 # Dependencies
 
diff --git a/doc/json_schemas/exponential_distribution.json b/doc/json_properties/exponential_distribution.json
similarity index 100%
rename from doc/json_schemas/exponential_distribution.json
rename to doc/json_properties/exponential_distribution.json
diff --git a/doc/json_schemas/gamma_distribution.json b/doc/json_properties/gamma_distribution.json
similarity index 100%
rename from doc/json_schemas/gamma_distribution.json
rename to doc/json_properties/gamma_distribution.json
diff --git a/doc/json_schemas/lognormal_distribution.json b/doc/json_properties/lognormal_distribution.json
similarity index 100%
rename from doc/json_schemas/lognormal_distribution.json
rename to doc/json_properties/lognormal_distribution.json
diff --git a/doc/json_schemas/normal_distrubution.json b/doc/json_properties/normal_distrubution.json
similarity index 100%
rename from doc/json_schemas/normal_distrubution.json
rename to doc/json_properties/normal_distrubution.json
diff --git a/doc/json_properties/readme.md b/doc/json_properties/readme.md
new file mode 100644
index 00000000..6e9be80b
--- /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_schemas/uniform_distribution.json b/doc/json_properties/uniform_distribution.json
similarity index 100%
rename from doc/json_schemas/uniform_distribution.json
rename to doc/json_properties/uniform_distribution.json
diff --git a/doc/json_schemas/readme.md b/doc/json_schemas/readme.md
deleted file mode 100644
index 9e2391a1..00000000
--- a/doc/json_schemas/readme.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# JSON Schemas for Property Strings
-
-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.
-
-## Usage Example
-
-Here is an example of how to use these schemas within an OpenSCENARIO description:
-
-```xml
-<Property key="MyCustomProperty" value="{'type': 'NormalDistribution', 'min': 0.0, 'max': 10.0, 'mean': 7.5, 'stddev': 4.0 }">
-```
-
-In this example, a property with the key `MyCustomProperty` is defined using a JSON string that specifies a `NormalDistribution` with the following parameters:
-- `min`: 0.0
-- `max`: 10.0
-- `mean`: 7.5
-- `stddev`: 4.0
-
-Refer to the JSON schemas in this folder for more details on how to define other types of distributions and properties.
-```
\ No newline at end of file
diff --git a/engine/.gitignore b/engine/.gitignore
index 193cf270..f3febcbe 100644
--- a/engine/.gitignore
+++ b/engine/.gitignore
@@ -4,3 +4,4 @@
 build
 bin
 bazel-*
+external
-- 
GitLab


From 72cf9bb0c4509be71f89eb21aab95a3663b77f20 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Paris?= <rene.paris@in-tech.com>
Date: Fri, 2 May 2025 08:04:32 +0200
Subject: [PATCH 5/6] fix(bazel): establish compatiblity with internal CI

Relates to https://gitlab.eclipse.org/eclipse/openpass/openscenario1_engine/-/issues/71
---
 engine/BUILD.bazel | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/engine/BUILD.bazel b/engine/BUILD.bazel
index c53cf6dd..b3f3450b 100644
--- a/engine/BUILD.bazel
+++ b/engine/BUILD.bazel
@@ -28,8 +28,8 @@ cc_library(
     ],
     visibility = ["//visibility:public"],
     deps = [
+        "@//third_party/nlohmann_json",
         "@mantle_api",
-        "@nlohmann_json",
         "@open_scenario_parser",
         "@stochastics_library",
         "@units_nhh",
@@ -182,9 +182,9 @@ cc_test(
         ":open_scenario_engine",
         ":open_scenario_engine_test_utils",
         ":test_utils",
+        "@//third_party/nlohmann_json",
         "@googletest//:gtest_main",
         "@mantle_api//:test_utils",
-        "@nlohmann_json",
     ],
 )
 
-- 
GitLab


From 32c17e81984dd093d2c0855de2bb664442f139fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Paris?= <rene.paris@in-tech.com>
Date: Thu, 15 May 2025 07:15:03 +0000
Subject: [PATCH 6/6] fix: copyrights and doxygen documentation

---
 engine/src/Utils/JsonParsing.h                               | 2 +-
 .../Distributions/LogNormalDistribution.h                    | 2 +-
 utils/ci/conan/recipe/nlohmann_json/all/conandata.yml        | 5 +----
 utils/ci/conan/recipe/nlohmann_json/all/conanfile.py         | 2 +-
 utils/ci/conan/recipe/nlohmann_json/config.yml               | 5 +----
 utils/ci/scripts/20_configure.sh                             | 2 +-
 6 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/engine/src/Utils/JsonParsing.h b/engine/src/Utils/JsonParsing.h
index ba716982..99435d4e 100644
--- a/engine/src/Utils/JsonParsing.h
+++ b/engine/src/Utils/JsonParsing.h
@@ -60,4 +60,4 @@ std::optional<DeserializedType> Deserialize(std::string_view value)
 
 }  // namespace json
 
-}  // namespace OpenScenarioEngine::v1_3
\ No newline at end of file
+}  // namespace OpenScenarioEngine::v1_3
diff --git a/engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h b/engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h
index 1aca4fc4..ba2397ae 100644
--- a/engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h
+++ b/engine/src/Utils/PropertyInterpretation/Distributions/LogNormalDistribution.h
@@ -15,7 +15,7 @@
 namespace OpenScenarioEngine::v1_3::PropertyInterpretation
 {
 
-/// Normal distribution
+/// LogNormalDistribution
 struct LogNormalDistribution
 {
   double lowerLimit;     ///!< Lower limit
diff --git a/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml b/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml
index 3a9de20a..346f60a2 100644
--- a/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml
+++ b/utils/ci/conan/recipe/nlohmann_json/all/conandata.yml
@@ -1,5 +1,5 @@
 ################################################################################
-# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+# 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
@@ -16,6 +16,3 @@ sources:
   "v3.9.1":
     url: https://github.com/nlohmann/json/archive/refs/tags/v3.9.1.tar.gz
     sha256: "4cf0df69731494668bdd6460ed8cb269b68de9c19ad8c27abc24cd72605b2d5b"
-  "v3.11.3":
-    url: https://github.com/nlohmann/json/archive/refs/tags/v3.11.3.tar.gz
-    sha256: "0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406"
diff --git a/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py b/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py
index 2a50d134..b3da81e2 100644
--- a/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py
+++ b/utils/ci/conan/recipe/nlohmann_json/all/conanfile.py
@@ -1,5 +1,5 @@
 ################################################################################
-# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+# 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
diff --git a/utils/ci/conan/recipe/nlohmann_json/config.yml b/utils/ci/conan/recipe/nlohmann_json/config.yml
index c2ea0f20..84d55e73 100644
--- a/utils/ci/conan/recipe/nlohmann_json/config.yml
+++ b/utils/ci/conan/recipe/nlohmann_json/config.yml
@@ -1,5 +1,5 @@
 ################################################################################
-# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+# 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
@@ -15,6 +15,3 @@
 versions:
   "v3.9.1":
     folder: "all"
-
-  "v3.11.3":
-    folder: "all"
diff --git a/utils/ci/scripts/20_configure.sh b/utils/ci/scripts/20_configure.sh
index 8dc0b896..673e1384 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
-- 
GitLab