WIP: working on MDMEntityResponse unification for request and response

Signed-off-by: Alexander Nehmer <a.nehmer@science-computing.de>
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogAttributeResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogAttributeResource.java
index 33cd403..9f3c14c 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogAttributeResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogAttributeResource.java
@@ -19,7 +19,6 @@
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
 import static org.eclipse.mdm.businessobjects.utils.Decomposer.decompose;
-import static org.eclipse.mdm.businessobjects.utils.ServiceUtils.L;
 import static org.eclipse.mdm.businessobjects.utils.ServiceUtils.SL;
 
 import javax.ejb.EJB;
@@ -42,7 +41,6 @@
 import org.eclipse.mdm.api.dflt.model.CatalogComponent;
 import org.eclipse.mdm.businessobjects.entity.MDMEntity;
 import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
-import org.eclipse.mdm.businessobjects.entity.MDMRelation;
 import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
 import org.eclipse.mdm.businessobjects.service.EntityService;
 import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
@@ -130,22 +128,12 @@
 			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String catCompId,
 			MDMEntityResponse body) {
 		return entityService
-				.create(sourceName, CatalogAttribute.class, L(
-						decompose(body::getData).<MDMEntity>getAt(0).get(MDMEntity::getName),
-						entityService.getEnumerationValueSupplier(decompose(body::getData).<MDMEntity>getAt(0)
-								.get(MDMEntity::getAttributes) //
-								.get(attrs -> attrs.stream()
-										.filter(attr -> attr.getName().equals(CatalogAttribute.VATTR_SCALAR_TYPE))
-										.findFirst().get().getValue())),
-						entityService.find(sourceName, Unit.class,
-								// get id of related Unit
-								decompose(body::getData).<MDMEntity>getAt(0) //
-										.get(e -> e.getRelations().stream() //
-												.filter(r -> r.getEntityType().equals(Unit.class.getSimpleName())) //
-												.findFirst().get()) //
-										.get(MDMRelation::getIds).getValueAt(0)),
-						entityService.find(sourceName, CatalogComponent.class, catCompId,
-								ServiceUtils.getContextTypeSupplier(contextTypeParam))))
+				.create(sourceName, CatalogAttribute.class,
+						entityService
+								.extractArgumentsFromRequestBody(sourceName, body, List.of("Name", "@ScalarType"),
+										List.of(Unit.class))
+								.append(entityService.find(sourceName, CatalogComponent.class, catCompId,
+										ServiceUtils.getContextTypeSupplier(contextTypeParam))))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementResource.java
index ce8357d..d1d7a72 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementResource.java
@@ -17,7 +17,6 @@
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
 import static org.eclipse.mdm.businessobjects.utils.Decomposer.decompose;
-import static org.eclipse.mdm.businessobjects.utils.ServiceUtils.L;
 import static org.eclipse.mdm.businessobjects.utils.ServiceUtils.V;
 
 import javax.ejb.EJB;
@@ -46,7 +45,6 @@
 import org.eclipse.mdm.businessobjects.entity.I18NResponse;
 import org.eclipse.mdm.businessobjects.entity.MDMEntity;
 import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
-import org.eclipse.mdm.businessobjects.entity.MDMRelation;
 import org.eclipse.mdm.businessobjects.entity.SearchAttributeResponse;
 import org.eclipse.mdm.businessobjects.service.ContextService;
 import org.eclipse.mdm.businessobjects.service.EntityFileLink;
@@ -59,9 +57,7 @@
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import io.vavr.Value;
 import io.vavr.collection.List;
-import io.vavr.collection.Seq;
 import io.vavr.control.Try;
 
 /**
@@ -180,7 +176,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts")
 	public Response updateContext(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, Measurement.class, id)
 				.map(measurement -> contextService.updateContext(body, measurement)).map(ContextResponse::new)
@@ -223,7 +219,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts/unitundertest")
 	public Response updateContextUUT(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, Measurement.class, id)
 				.map(measurement -> contextService.updateContext(body, measurement, ContextType.UNITUNDERTEST))
@@ -266,7 +262,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts/testsequence")
 	public Response updateContextTSQ(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, Measurement.class, id)
 				.map(measurement -> contextService.updateContext(body, measurement, ContextType.TESTSEQUENCE))
@@ -309,7 +305,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts/testequipment")
 	public Response updateContextTEQ(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, Measurement.class, id)
 				.map(measurement -> contextService.updateContext(body, measurement, ContextType.TESTEQUIPMENT))
@@ -365,23 +361,16 @@
 	 * @return the created {@link Measurement} as {@link Response}.
 	 */
 	@POST
-	@Operation(summary = "Create a new Measurement",  responses = {
+	@Operation(summary = "Create a new Measurement", responses = {
 			@ApiResponse(description = "The created Measurement", content = @Content(schema = @Schema(implementation = MDMEntityResponse.class))),
 			@ApiResponse(responseCode = "500", description = "Error") })
 	@Produces(MediaType.APPLICATION_JSON)
 	@Consumes(MediaType.APPLICATION_JSON)
 	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, MDMEntityResponse body) {
-		Seq<Value<?>> args = L(decompose(body::getData).<MDMEntity>getAt(0).get(MDMEntity::getName),
-				// get PhysDim
-				entityService.find(sourceName, TestStep.class,
-						// get id of related TestStep
-						decompose(body::getData).<MDMEntity>getAt(0) //
-								.get(e -> e.getRelations().stream() //
-										.filter(r -> r.getEntityType().equals(TestStep.class.getSimpleName())) //
-										.findFirst().get()) //
-								.get(MDMRelation::getIds).getValueAt(0)),
-				V(new ContextRoot[] {}));
-		return entityService.create(sourceName, Measurement.class, args)
+		return entityService
+				.create(sourceName, Measurement.class,
+						entityService.extractArgumentsFromRequestBody(sourceName, body, List.of("Name"),
+								List.of(TestStep.class)).append(V(new ContextRoot[] {})))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
 	}
@@ -451,7 +440,7 @@
 	@Path("/{" + REQUESTPARAM_ID + "}/files/{remotePath}")
 	public Response streamFileLink(
 			@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@Parameter(description = "ID of the Measurement containing the file link", required = true) @PathParam(REQUESTPARAM_ID) String id, 
+			@Parameter(description = "ID of the Measurement containing the file link", required = true) @PathParam(REQUESTPARAM_ID) String id,
 			@Parameter(description = "The remote path of the file link whose content is to be retrieved", required = true) @PathParam("remotePath") String remotePath) {
 
 		return entityService.find(sourceName, Measurement.class, id).map(fa -> new EntityFileLink(fa, remotePath))
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java
index 27ac6e8..daf75be 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java
@@ -185,7 +185,8 @@
 
 		return entityService
 				.create(sourceName, Pool.class,
-						entityService.extractArgumentsFromRequestBody(sourceName, body, List.of(Project.class)))
+						entityService.extractArgumentsFromRequestBody(sourceName, body, List.of("Name"),
+								List.of(Project.class)))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestResource.java
index f8c2dd1..16a18e7 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestResource.java
@@ -193,7 +193,7 @@
 
 		return entityService
 				.create(sourceName, Test.class,
-						entityService.extractArgumentsFromRequestBody(sourceName, body,
+						entityService.extractArgumentsFromRequestBody(sourceName, body, List.of("Name"),
 								List.of(Pool.class, TemplateTest.class)))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepResource.java
index 3cc9254..2b1cc1f 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepResource.java
@@ -176,7 +176,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts")
 	public Response updateContext(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, TestStep.class, id)
 				.map(testStep -> contextService.updateContext(body, testStep)).map(ContextResponse::new)
@@ -218,7 +218,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts/unitundertest")
 	public Response updateContextUUT(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, TestStep.class, id)
 				.map(testStep -> contextService.updateContext(body, testStep, ContextType.UNITUNDERTEST))
@@ -260,7 +260,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts/testsequence")
 	public Response updateContextTSQ(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, TestStep.class, id)
 				.map(testStep -> contextService.updateContext(body, testStep, ContextType.TESTSEQUENCE))
@@ -302,7 +302,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}/contexts/testequipment")
 	public Response updateContextTEQ(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@PathParam(REQUESTPARAM_ID) String id, MDMEntityResponse body) {
+			@PathParam(REQUESTPARAM_ID) String id, ContextResponse body) {
 
 		return entityService.find(sourceName, TestStep.class, id)
 				.map(testStep -> contextService.updateContext(body, testStep, ContextType.TESTEQUIPMENT))
@@ -358,17 +358,18 @@
 	 * @return the created {@link TestStep} as {@link Response}.
 	 */
 	@POST
-	@Operation(summary = "Create a new TestStep",  responses = {
+	@Operation(summary = "Create a new TestStep", responses = {
 			@ApiResponse(description = "The created TestStep", content = @Content(schema = @Schema(implementation = MDMEntityResponse.class))),
 			@ApiResponse(responseCode = "500", description = "Error") })
 	@Produces(MediaType.APPLICATION_JSON)
 	@Consumes(MediaType.APPLICATION_JSON)
 	public Response create(
-			@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName, MDMEntityResponse body) {
+			@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			MDMEntityResponse body) {
 
 		return entityService
 				.create(sourceName, TestStep.class,
-						entityService.extractArgumentsFromRequestBody(sourceName, body,
+						entityService.extractArgumentsFromRequestBody(sourceName, body, List.of("Name"),
 								List.of(Test.class, TemplateTestStep.class)))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -391,10 +392,10 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response patch(
-			@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName, 
+			@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@Parameter(description = "ID of the TestStep", required = true) @PathParam(REQUESTPARAM_ID) String id,
 			MDMEntityResponse body) {
-	
+
 		return entityService
 				.update(sourceName, entityService.find(sourceName, TestStep.class, id),
 						decompose(body::getData).<MDMEntity>getValueAt(0))
@@ -442,7 +443,7 @@
 	@Path("/{" + REQUESTPARAM_ID + "}/files/{remotePath}")
 	public Response streamFileLink(
 			@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
-			@Parameter(description = "ID of the TestStep containing the file link", required = true) @PathParam(REQUESTPARAM_ID) String id, 
+			@Parameter(description = "ID of the TestStep containing the file link", required = true) @PathParam(REQUESTPARAM_ID) String id,
 			@Parameter(description = "The remote path of the file link whose content is to be retrieved", required = true) @PathParam("remotePath") String remotePath) {
 
 		return entityService.find(sourceName, TestStep.class, id).map(fa -> new EntityFileLink(fa, remotePath))
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextResponse.java
index bbcbc11..2286e49 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextResponse.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextResponse.java
@@ -63,6 +63,12 @@
 	}
 
 	/**
+	 * Default constructor
+	 */
+	public ContextResponse() {
+	}
+
+	/**
 	 * returns the context data
 	 * 
 	 * @return the context data
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java
index 9169ac8..954fe6c 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java
@@ -17,6 +17,7 @@
 import static org.eclipse.mdm.businessobjects.control.ContextActivity.CONTEXT_GROUP_MEASURED;
 import static org.eclipse.mdm.businessobjects.control.ContextActivity.CONTEXT_GROUP_ORDERED;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.NoSuchElementException;
@@ -25,6 +26,7 @@
 import javax.ejb.EJB;
 import javax.ejb.Stateless;
 import javax.inject.Inject;
+import javax.ws.rs.BadRequestException;
 
 import org.eclipse.mdm.api.base.Transaction;
 import org.eclipse.mdm.api.base.model.ContextComponent;
@@ -39,15 +41,19 @@
 import org.eclipse.mdm.api.dflt.model.EntityFactory;
 import org.eclipse.mdm.api.dflt.model.TemplateRoot;
 import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
-import org.eclipse.mdm.businessobjects.control.ContextActivity;
 import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException;
+import org.eclipse.mdm.businessobjects.entity.ContextCollection;
+import org.eclipse.mdm.businessobjects.entity.ContextResponse;
 import org.eclipse.mdm.businessobjects.entity.MDMAttribute;
 import org.eclipse.mdm.businessobjects.entity.MDMEntity;
-import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
+import org.eclipse.mdm.businessobjects.utils.Decomposer;
+import org.eclipse.mdm.businessobjects.utils.ISODateDeseralizer;
 import org.eclipse.mdm.businessobjects.utils.Serializer;
-import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
 import org.eclipse.mdm.connector.boundary.ConnectorService;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.google.common.base.Strings;
 
 import io.vavr.Lazy;
@@ -223,30 +229,42 @@
 	 *                           all contextTypes.
 	 * @return
 	 */
-	public DescribableContexts updateContext(MDMEntityResponse body, ContextDescribable contextDescribable,
+	public DescribableContexts updateContext(ContextResponse body, ContextDescribable contextDescribable,
 			ContextType... contextTypes) {
-		Try<Map<String, Object>> contextMap = Try.of(() -> body.getData().get(0)).map(this::transformMap);
 
-		DescribableContexts ec = updateContext(getTestStep(contextDescribable), contextMap,
+		ContextCollection contextCollection = Decomposer.decompose(body::getData).<ContextCollection>getValueAt(0);
+
+		DescribableContexts ec = updateContext(getTestStep(contextDescribable), contextCollection,
 				getContextTypes(contextTypes));
 		persist(ec);
 		return ec;
 	}
 
-	private DescribableContexts updateContext(TestStep testStep, Try<Map<String, Object>> contextMap,
+	private HashMap<String, Object> deserializeRequestBody(String body) {
+		ObjectMapper mapper = new ObjectMapper();
+		SimpleModule simpleModule = new SimpleModule();
+		simpleModule.addDeserializer(Object.class, new ISODateDeseralizer());
+		mapper.registerModule(simpleModule);
+
+		try {
+			return HashMap.ofAll(mapper.readValue(body, new TypeReference<java.util.Map<String, Object>>() {
+			}));
+		} catch (IOException e) {
+			throw new BadRequestException("Body of request could not be deserialized", e);
+		}
+	}
+
+	private DescribableContexts updateContext(TestStep testStep, ContextCollection contextCollection,
 			ContextType[] contextTypes) {
-		Map<ContextType, Object> orderedMap = contextMap.get().get(ContextActivity.CONTEXT_GROUP_ORDERED)
-				.map(this::transformMap).getOrElse(HashMap.empty())
-				.mapKeys(t -> ServiceUtils.getContextTypeSupplier(t).get())
+
+		Map<ContextType, java.util.List<MDMEntity>> orderedMap = HashMap.ofAll(contextCollection.ordered)
 				.filterKeys(t -> Arrays.asList(contextTypes).contains(t));
 
 		DescribableContexts ec = new DescribableContexts();
 		ec.setTestStep(testStep);
 		ec.setOrdered(updateContextDescribableContext(testStep, orderedMap, contextTypes));
 
-		Map<ContextType, Object> measuredMap = contextMap.get().get(ContextActivity.CONTEXT_GROUP_MEASURED)
-				.map(this::transformMap).getOrElse(HashMap.empty())
-				.mapKeys(t -> ServiceUtils.getContextTypeSupplier(t).get())
+		Map<ContextType, java.util.List<MDMEntity>> measuredMap = HashMap.ofAll(contextCollection.measured)
 				.filterKeys(t -> Arrays.asList(contextTypes).contains(t));
 
 		ec.setMeasurements(getEntityManager(testStep.getSourceName()).loadChildren(testStep, Measurement.class));
@@ -257,7 +275,8 @@
 	}
 
 	private java.util.Map<ContextType, ContextRoot> updateContextDescribableContext(
-			ContextDescribable contextDescribable, Map<ContextType, Object> rootMap, ContextType[] contextTypes) {
+			ContextDescribable contextDescribable, Map<ContextType, java.util.List<MDMEntity>> rootMap,
+			ContextType[] contextTypes) {
 		java.util.Map<ContextType, ContextRoot> existingRootMap = getEntityManager(contextDescribable.getSourceName())
 				.loadContexts(contextDescribable, contextTypes);
 
@@ -284,8 +303,8 @@
 		}
 	}
 
-	private void updateContextRoot(ContextRoot existingRoot, Object newContextRoot) {
-		transformList(newContextRoot).map(this::transformMap).map(this::transformToMDMEntity).forEach(mdmEntity -> {
+	private void updateContextRoot(ContextRoot existingRoot, java.util.List<MDMEntity> newContextRoot) {
+		newContextRoot.forEach(mdmEntity -> {
 			ContextComponent comp = existingRoot.getContextComponent(mdmEntity.getName())
 					.orElseGet(() -> getEntityFactory(existingRoot.getSourceName())
 							.createContextComponent(mdmEntity.getName(), existingRoot));
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/EntityService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/EntityService.java
index 5b5b03f..317598d 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/EntityService.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/EntityService.java
@@ -461,45 +461,77 @@
 	}
 
 	/**
-	 * see
-	 * {@link #extractArgumentsFromRequestBody(String, MDMEntityResponse, boolean, Class...)}
+	 * Extract arguments from request defaulting to extract MDMEntity.name see also
+	 * {@link #extractArgumentsFromRequestBody(String, MDMEntityResponse, List, List)}
 	 */
 	public Seq<Value<?>> extractArgumentsFromRequestBody(String sourceName, MDMEntityResponse body) {
-		return extractArgumentsFromRequestBody(sourceName, body, List.empty());
+		return extractArgumentsFromRequestBody(sourceName, body, List.of("Name"), List.empty());
 	}
 
 	/**
 	 * Extract arguments from request body to use for {@link EntityFactory} create
-	 * methods.
+	 * methods. If and argument given by attributeArguments or relationArguments
+	 * cannot be found, they are skipped.
 	 * 
-	 * @param sourceName             name of the source (MDM {@link Environment}
-	 *                               name)
-	 * @param body                   deserialized request to extract arguments from
-	 * @param relatedEntityArguments classes of related entities to extract from
-	 *                               request
-	 * @return the extracted arguments
+	 * @param sourceName         name of the source (MDM {@link Environment} name)
+	 * @param body               deserialized request to extract arguments from
+	 * @param attributeArguments list of names of attributes to extract values from
+	 * @param relationArguments  classes of related entities to extract from
+	 * @return the extracted arguments as a {@link Seq} of {@link Value}.
 	 */
 	public final Seq<Value<?>> extractArgumentsFromRequestBody(String sourceName, MDMEntityResponse body,
-			List<Class<? extends Entity>> relatedEntityArguments) {
-		List<Try<?>> name;
+			List<String> attributeArguments, List<Class<? extends Entity>> relationArguments) {
+		List<Try<?>> extractedArguments = List.empty();
 
-		// then name attribute can be omitted in some cases
-		Try<Decomposer<String>> nameTry = Try
-				.of(() -> decompose(body::getData).<MDMEntity>getAt(0).get(MDMEntity::getName));
-		if (!nameTry.isFailure()) {
-			name = List.of(nameTry.get());
-		} else {
-			name = List.empty();
+		// if there arguments to be extracted from MDMEntity.attributes
+		if (attributeArguments != null) {
+			// although the name of the entity to be created is an attribute, it could also
+			// be given as MDMEntity.name instead of MDMEntity.attributes("Name")
+			extractedArguments = extractedArguments.appendAll(
+					attributeArguments.filter(attrName -> attrName.equalsIgnoreCase("name")).map(attrName -> Try
+							.of(() -> decompose(body::getData).<MDMEntity>getAt(0).get(MDMEntity::getName)).get()));
+
+			extractedArguments = extractedArguments
+					// ignore name attr if already processed
+					.appendAll(attributeArguments.filter(attrName -> !attrName.equalsIgnoreCase("name"))//
+							.map(attrName -> Try.of(() -> //
+							decompose(body::getData) //
+									.<MDMEntity>getAt(0) //
+									.<MDMAttribute, String>getFiltered(MDMEntity::getAttributes, MDMAttribute::getName,
+											attrName) //
+									.map(attr -> {
+										Object value = attr.getValue().toString();
+										if (attr.getDataType().equals("ENUMERATION")) {
+											value = EnumRegistry.getInstance().get(EnumRegistry.VALUE_TYPE)
+													.valueOf(value.toString());
+										}
+										return value;
+									}).get())));
 		}
 
 		// wrap find in Try to handle non-existing relation (not all entityClasses must
 		// exist as a relation)
-		return relatedEntityArguments.map(clazz -> Try.of(() -> find(sourceName, clazz, //
-				decompose(body::getData).<MDMEntity>getAt(0) //
-						.<MDMRelation, String>getFiltered(MDMEntity::getRelations, MDMRelation::getEntityType,
-								clazz.getSimpleName()) //
-						.get(MDMRelation::getIds).getValueAt(0)).get()))
-				.foldLeft(name, (l, e) -> l.append(e)).filter(Try::isSuccess).map(Try::toOption);
+		if (relationArguments != null) {
+			extractedArguments = extractedArguments
+					.appendAll(relationArguments.map(clazz -> Try.of(() -> find(sourceName, clazz, //
+							// get IDs of related entities
+							decompose(body::getData).<MDMEntity>getAt(0) //
+									.<MDMRelation, String>getFiltered(MDMEntity::getRelations,
+											MDMRelation::getEntityType, clazz.getSimpleName()) //
+									.get(MDMRelation::getIds).getValueAt(0),
+							// get ContextType; if not present, find() ignores null value
+							Try.of(() -> decompose(body::getData).<MDMEntity>getAt(0) //
+									.<MDMRelation, String>getFiltered(MDMEntity::getRelations,
+											MDMRelation::getEntityType, clazz.getSimpleName()) //
+									.get(MDMRelation::getContextType)).getOrElse((Decomposer<ContextType>) null)) //
+											.get())));
+		}
+
+		// log failures as warnings
+		extractedArguments.filter(Try::isFailure)
+				.forEach(failure -> LOGGER.warn("Argument to extract not found: {}", failure.getCause().getMessage()));
+
+		return extractedArguments.filter(Try::isSuccess).map(Try::toOption);
 	}
 
 	/**
@@ -909,8 +941,7 @@
 		}
 
 		return entityClasses
-				.map(clazz -> find(sourceName, clazz,
-						requestBody.getStringValueSupplier(clazz.getSimpleName()).get()))
+				.map(clazz -> find(sourceName, clazz, requestBody.getStringValueSupplier(clazz.getSimpleName()).get()))
 				.foldLeft(name, (l, e) -> l.append(e)).filter(Try::isSuccess).map(Try::toOption);
 	}
 
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ISODateDeseralizer.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ISODateDeseralizer.java
index 5c5e691..cfeb3f0 100755
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ISODateDeseralizer.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ISODateDeseralizer.java
@@ -33,7 +33,7 @@
  * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)

  *

  */

-class ISODateDeseralizer extends UntypedObjectDeserializer {

+public class ISODateDeseralizer extends UntypedObjectDeserializer {

 

 	private static final long serialVersionUID = 1L;