You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/11/28 17:24:08 UTC

[isis] 01/01: ISIS-2877: refactor CommonDtoUtils into its own Service

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 2877_dto_rountrip_using_value_semantics
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 8b517712e7a32658dcfc335fd1197a785d7b23ef
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sun Nov 28 18:23:49 2021 +0100

    ISIS-2877: refactor CommonDtoUtils into its own Service
    
    - so we can serialize InteractionDtos using ValueSemantics, while not
    having to rely on static value-type mappings
---
 .../JavaTimeXMLGregorianCalendarMarshalling.java   |  24 +-
 .../services/schema/SchemaValueMarshaller.java     |  60 +++
 .../isis/applib/util/schema/CommonDtoUtils.java    | 528 +-------------------
 .../applib/util/schema/InteractionDtoUtils.java    |  24 +-
 .../semantics/Converter.java}                      |  22 +-
 .../value/semantics/ValueSemanticsAbstract.java    |   6 +
 .../value/semantics/ValueSemanticsProvider.java    |   5 +
 .../applib/util/schema/CommonDtoUtils_Test.java    |  39 +-
 .../schema/CommonDtoUtils_setValueOn_Test.java     |  82 ++--
 .../apache/isis/applib/util/schema/Roundtrip.java  |  65 ++-
 .../services/metamodel/MetaModelExporter.java      |   4 +-
 .../valuesemantics/BookmarkValueSemantics.java     |   8 -
 .../LocalResourcePathValueSemantics.java           |   2 +-
 .../valuesemantics/MarkupValueSemantics.java       |   2 +-
 .../valuesemantics/OidDtoValueSemantics.java       |   2 +-
 .../valuesemantics/URLValueSemantics.java          |   2 +-
 .../valuesemantics/UUIDValueSemantics.java         |   2 +-
 .../temporal/legacy/JavaSqlDateValueSemantics.java |   6 -
 .../legacy/JavaSqlTimeStampValueSemantics.java     |   6 -
 .../temporal/legacy/JavaSqlTimeValueSemantics.java |   6 -
 .../legacy/JavaUtilDateValueSemantics.java         |   6 -
 .../valuetypes/ValueSemanticsAdapter.java          |  17 +-
 .../IsisModuleCoreRuntimeServices.java             |   6 +-
 .../command/CommandDtoFactoryDefault.java          |  19 +-
 .../command/CommandExecutorServiceDefault.java     |  63 ++-
 .../runtimeservices/command/DtoContextDefault.java |  29 --
 .../command/SchemaValueMarshallerDefault.java      | 529 +++++++++------------
 .../interaction/InteractionDtoFactoryDefault.java  |   6 +-
 .../isis/extensions/restclient/ResponseDigest.java |  26 +-
 .../isis/testdomain/value/ValueSemanticsTest.java  | 209 ++++----
 .../testdomain/value/ValueSemanticsTester.java     |  21 +-
 .../valuesemantics/JodaDateTimeValueSemantics.java |   6 -
 .../JodaLocalDateTimeValueSemantics.java           |   6 -
 .../JodaLocalDateValueSemantics.java               |   6 -
 .../JodaLocalTimeValueSemantics.java               |   6 -
 35 files changed, 686 insertions(+), 1164 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/jaxb/JavaTimeXMLGregorianCalendarMarshalling.java b/api/applib/src/main/java/org/apache/isis/applib/jaxb/JavaTimeXMLGregorianCalendarMarshalling.java
index 377401c..b688899 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/jaxb/JavaTimeXMLGregorianCalendarMarshalling.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/jaxb/JavaTimeXMLGregorianCalendarMarshalling.java
@@ -37,43 +37,43 @@ import lombok.experimental.UtilityClass;
 @UtilityClass
 public final class JavaTimeXMLGregorianCalendarMarshalling {
 
-    public static LocalDate toLocalDate(XMLGregorianCalendar cal) {
+    public static LocalDate toLocalDate(final XMLGregorianCalendar cal) {
         return LocalDate.of(cal.getYear(), cal.getMonth(), cal.getDay());
     }
 
-    public static LocalTime toLocalTime(XMLGregorianCalendar cal) {
+    public static LocalTime toLocalTime(final XMLGregorianCalendar cal) {
         return LocalTime.of(cal.getHour(), cal.getMinute(), cal.getSecond(),
                 cal.getMillisecond()*1000_000);
     }
 
-    public static LocalDateTime toLocalDateTime(XMLGregorianCalendar cal) {
+    public static LocalDateTime toLocalDateTime(final XMLGregorianCalendar cal) {
         return LocalDateTime.of(cal.getYear(), cal.getMonth(), cal.getDay(),
                 cal.getHour(), cal.getMinute(), cal.getSecond(),
                 cal.getMillisecond()*1000_000);
     }
 
-    public static OffsetDateTime toOffsetDateTime(XMLGregorianCalendar cal) {
+    public static OffsetDateTime toOffsetDateTime(final XMLGregorianCalendar cal) {
         return OffsetDateTime.of(cal.getYear(), cal.getMonth(), cal.getDay(),
                 cal.getHour(), cal.getMinute(), cal.getSecond(),
                 cal.getMillisecond()*1000_000,
                 ZoneOffset.ofTotalSeconds(cal.getTimezone()*60));
     }
 
-    public static OffsetTime toOffsetTime(XMLGregorianCalendar cal) {
+    public static OffsetTime toOffsetTime(final XMLGregorianCalendar cal) {
         return OffsetTime.of(
                 cal.getHour(), cal.getMinute(), cal.getSecond(),
                 cal.getMillisecond()*1000_000,
                 ZoneOffset.ofTotalSeconds(cal.getTimezone()*60));
     }
 
-    public static ZonedDateTime toZonedDateTime(XMLGregorianCalendar cal) {
+    public static ZonedDateTime toZonedDateTime(final XMLGregorianCalendar cal) {
         return ZonedDateTime.of(cal.getYear(), cal.getMonth(), cal.getDay(),
                 cal.getHour(), cal.getMinute(), cal.getSecond(),
                 cal.getMillisecond()*1000_000,
                 ZoneOffset.ofTotalSeconds(cal.getTimezone()*60));
     }
 
-    public static XMLGregorianCalendar toXMLGregorianCalendar2(LocalDate localDate) {
+    public static XMLGregorianCalendar toXMLGregorianCalendar(final LocalDate localDate) {
         return localDate!=null
                 ? DataTypeFactory.withTypeFactoryDo(factory->factory.newXMLGregorianCalendarDate(
                         localDate.getYear(),
@@ -84,7 +84,7 @@ public final class JavaTimeXMLGregorianCalendarMarshalling {
                 : null;
     }
 
-    public static XMLGregorianCalendar toXMLGregorianCalendar2(LocalTime localTime) {
+    public static XMLGregorianCalendar toXMLGregorianCalendar(final LocalTime localTime) {
         return localTime!=null
                 ? DataTypeFactory.withTypeFactoryDo(factory->factory.newXMLGregorianCalendarTime(
                         localTime.getHour(),
@@ -96,7 +96,7 @@ public final class JavaTimeXMLGregorianCalendarMarshalling {
                 : null;
     }
 
-    public static XMLGregorianCalendar toXMLGregorianCalendar2(LocalDateTime localDateTime) {
+    public static XMLGregorianCalendar toXMLGregorianCalendar(final LocalDateTime localDateTime) {
         return localDateTime!=null
                 ? DataTypeFactory.withTypeFactoryDo(factory->factory.newXMLGregorianCalendar(
                         localDateTime.getYear(),
@@ -111,7 +111,7 @@ public final class JavaTimeXMLGregorianCalendarMarshalling {
                 : null;
     }
 
-    public static XMLGregorianCalendar toXMLGregorianCalendar2(OffsetTime offsetTime) {
+    public static XMLGregorianCalendar toXMLGregorianCalendar(final OffsetTime offsetTime) {
         return offsetTime!=null
                 ? DataTypeFactory.withTypeFactoryDo(factory->factory.newXMLGregorianCalendarTime(
                         offsetTime.getHour(),
@@ -123,7 +123,7 @@ public final class JavaTimeXMLGregorianCalendarMarshalling {
                 : null;
     }
 
-    public static XMLGregorianCalendar toXMLGregorianCalendar2(OffsetDateTime offsetDateTime) {
+    public static XMLGregorianCalendar toXMLGregorianCalendar(final OffsetDateTime offsetDateTime) {
         return offsetDateTime!=null
                 ? DataTypeFactory.withTypeFactoryDo(factory->factory.newXMLGregorianCalendar(
                         offsetDateTime.getYear(),
@@ -138,7 +138,7 @@ public final class JavaTimeXMLGregorianCalendarMarshalling {
                 : null;
     }
 
-    public static XMLGregorianCalendar toXMLGregorianCalendar2(ZonedDateTime zonedDateTime) {
+    public static XMLGregorianCalendar toXMLGregorianCalendar(final ZonedDateTime zonedDateTime) {
         return zonedDateTime!=null
                 ? DataTypeFactory.withTypeFactoryDo(factory->factory.newXMLGregorianCalendar(
                         zonedDateTime.getYear(),
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/schema/SchemaValueMarshaller.java b/api/applib/src/main/java/org/apache/isis/applib/services/schema/SchemaValueMarshaller.java
new file mode 100644
index 0000000..b28d8db
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/schema/SchemaValueMarshaller.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services.schema;
+
+import org.apache.isis.schema.cmd.v2.ParamDto;
+import org.apache.isis.schema.cmd.v2.PropertyDto;
+import org.apache.isis.schema.ixn.v2.ActionInvocationDto;
+
+/**
+ * Provides the runtime context for converting values
+ * between their XML <i>Schema</i> and their <i>Java</i> type representation.
+ *
+ * @since 2.x {@index}
+ */
+public interface SchemaValueMarshaller {
+
+    // -- RECOVER VALUES FROM DTO
+
+    Object recoverValueFrom(PropertyDto propertyDto);
+    Object recoverValueFrom(String logicalActionIdentifier, ParamDto paramDto);
+
+    // -- PUT VALUES INTO DTO
+
+    ActionInvocationDto putActionResult(
+            ActionInvocationDto invocationDto,
+            Class<?> returnType,
+            Object result);
+
+    PropertyDto putValueInto(
+            PropertyDto propertyDto,
+            Class<?> propertyType,
+            Object valuePojo);
+
+    ParamDto newParamDtoScalar(
+            String parameterName,
+            Class<?> paramType,
+            Object valuePojo);
+
+    ParamDto newParamDtoNonScalar(
+            String parameterName,
+            Class<?> paramElementType,
+            Object valuePojo);
+
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java b/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
index a4bd003..38a43f5 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
@@ -26,42 +26,18 @@ import java.time.LocalTime;
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
 import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
 
-import org.springframework.lang.Nullable;
-
-import org.apache.isis.applib.jaxb.JavaTimeXMLGregorianCalendarMarshalling;
-import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
-import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.commons.internal.base._NullSafe;
-import org.apache.isis.commons.internal.base._Refs;
-import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Maps;
-import org.apache.isis.commons.internal.context._Context;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.schema.cmd.v2.MapDto;
-import org.apache.isis.schema.cmd.v2.ParamDto;
-import org.apache.isis.schema.common.v2.BlobDto;
-import org.apache.isis.schema.common.v2.ClobDto;
-import org.apache.isis.schema.common.v2.CollectionDto;
-import org.apache.isis.schema.common.v2.EnumDto;
-import org.apache.isis.schema.common.v2.OidDto;
-import org.apache.isis.schema.common.v2.TypedTupleDto;
-import org.apache.isis.schema.common.v2.ValueDto;
 import org.apache.isis.schema.common.v2.ValueType;
-import org.apache.isis.schema.common.v2.ValueWithTypeDto;
 
 import static org.apache.isis.commons.internal.collections._Maps.entry;
 
-import lombok.NonNull;
 import lombok.val;
 
 /**
@@ -69,13 +45,8 @@ import lombok.val;
  */
 public final class CommonDtoUtils {
 
-    // -- PARAM_DTO_TO_NAME, PARAM_DTO_TO_TYPE
-
-    public static final Function<ParamDto, String> PARAM_DTO_TO_NAME = ParamDto::getName;
-    public static final Function<ParamDto, ValueType> PARAM_DTO_TO_TYPE = ParamDto::getType;
-
     // -- asValueType
-    public static final Map<Class<?>, ValueType> valueTypeByClass =
+    private static final Map<Class<?>, ValueType> valueTypeByClass =
             _Maps.unmodifiableEntries(
                     entry(Void.class, ValueType.VOID),
                     entry(String.class, ValueType.STRING),
@@ -110,474 +81,29 @@ public final class CommonDtoUtils {
                     entry(Clob.class, ValueType.CLOB)
                     );
 
-    private static final Set<Class<?>> VALUE_TYPES = valueTypeByClass.keySet();
-
-    public static ValueType asValueType(final @NonNull Class<?> type) {
-        if(Iterable.class.isAssignableFrom(type)
-                        || type.isArray()) {
-            return ValueType.COLLECTION;
-        }
-        final ValueType valueType = valueTypeByClass.get(type);
-        if (valueType != null) {
-            return valueType;
-        }
-        if (type.isEnum()) {
-            return ValueType.ENUM;
-        }
-        // assume reference otherwise
-        return ValueType.REFERENCE;
-    }
-
-
-    // -- newValueDto, setValueOn
-
-    public static ValueDto newValueDto(
-            final ValueType valueType,
-            final Object value,
-            final @NonNull DtoContext dtoContext) {
-
-        if(value == null) {
-            return null;
-        }
-
-        final ValueDto valueDto = new ValueDto();
-        return setValueOn(valueDto, valueType, value, dtoContext);
-    }
-
-    public static <T extends ValueWithTypeDto> T setValueOn(
-            final T valueWithTypeDto,
-            final ValueType valueType,
-            final Object value,
-            final @NonNull DtoContext dtoContext) {
-
-        valueWithTypeDto.setType(valueType);
-
-        setValueOn((ValueDto)valueWithTypeDto, valueType, value, dtoContext);
-        valueWithTypeDto.setNull(value == null);
-
-        return valueWithTypeDto;
-    }
-
-    public static <T extends ValueWithTypeDto> T setValueOnNonScalar(
-            final T valueWithTypeDto,
-            final ValueType elementValueType,
-            final Object value,
-            final @NonNull DtoContext dtoContext) {
-
-        valueWithTypeDto.setType(ValueType.COLLECTION);
-
-        val collectionDto = asCollectionDto(value, elementValueType, dtoContext);
-        valueWithTypeDto.setCollection(collectionDto);
-        valueWithTypeDto.setNull(value == null);
-
-        return valueWithTypeDto;
-    }
-
-    public static <T extends ValueDto> T setValueOn(
-            final T valueDto,
-            final ValueType valueType,
-            final Object pojo,
-            final @NonNull DtoContext dtoContext) {
-
-        switch (valueType) {
-        case COLLECTION: {
-            final CollectionDto collectionDto = asCollectionDto(
-                    pojo, ValueType.VOID, dtoContext);
-            valueDto.setCollection(collectionDto);
-            return valueDto;
-        }
-        case COMPOSITE: {
-            final TypedTupleDto typedTupleDto = asTypedTupleDto(pojo, dtoContext);
-            valueDto.setComposite(typedTupleDto);
-            return valueDto;
-        }
-        case STRING: {
-            final String argValue = (String) pojo;
-            valueDto.setString(argValue);
-            return valueDto;
-        }
-        case BYTE: {
-            final Byte argValue = (Byte) pojo;
-            valueDto.setByte(argValue);
-            return valueDto;
-        }
-        case SHORT: {
-            final Short argValue = (Short) pojo;
-            valueDto.setShort(argValue);
-            return valueDto;
-        }
-        case INT: {
-            final Integer argValue = (Integer) pojo;
-            valueDto.setInt(argValue);
-            return valueDto;
-        }
-        case LONG: {
-            final Long argValue = (Long) pojo;
-            valueDto.setLong(argValue);
-            return valueDto;
-        }
-        case CHAR: {
-            final Character argValue = (Character) pojo;
-            valueDto.setChar("" + argValue);
-            return valueDto;
-        }
-        case BOOLEAN: {
-            final Boolean argValue = (Boolean) pojo;
-            valueDto.setBoolean(argValue);
-            return valueDto;
-        }
-        case FLOAT: {
-            final Float argValue = (Float) pojo;
-            valueDto.setFloat(argValue);
-            return valueDto;
-        }
-        case DOUBLE: {
-            final Double argValue = (Double) pojo;
-            valueDto.setDouble(argValue);
-            return valueDto;
-        }
-        case BIG_INTEGER: {
-            final BigInteger argValue = (BigInteger) pojo;
-            valueDto.setBigInteger(argValue);
-            return valueDto;
-        }
-        case BIG_DECIMAL: {
-            final BigDecimal argValue = (BigDecimal) pojo;
-            valueDto.setBigDecimal(argValue);
-            return valueDto;
-        }
-        case LOCAL_DATE: {
-            final LocalDate argValue = (LocalDate) pojo;
-            valueDto.setLocalDate(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
-            return valueDto;
-        }
-        case LOCAL_TIME: {
-            final LocalTime argValue = (LocalTime) pojo;
-            valueDto.setLocalTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
-            return valueDto;
-        }
-        case LOCAL_DATE_TIME: {
-            final LocalDateTime argValue = (LocalDateTime) pojo;
-            valueDto.setLocalDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
-            return valueDto;
-        }
-        case OFFSET_DATE_TIME: {
-            final OffsetDateTime argValue = (OffsetDateTime) pojo;
-            valueDto.setOffsetDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
-            return valueDto;
-        }
-        case OFFSET_TIME: {
-            final OffsetTime argValue = (OffsetTime) pojo;
-            valueDto.setOffsetTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
-            return valueDto;
-        }
-        case ZONED_DATE_TIME: {
-            final ZonedDateTime argValue = (ZonedDateTime) pojo;
-            valueDto.setZonedDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
-            return valueDto;
-        }
-        case ENUM: {
-            final Enum<?> argValue = (Enum<?>) pojo;
-            if(argValue == null) {
-                return null;
-            }
-            final EnumDto enumDto = new EnumDto();
-            valueDto.setEnum(enumDto);
-            enumDto.setEnumType(argValue.getClass().getName());
-            enumDto.setEnumName(argValue.name());
-            return valueDto;
-        }
-        case REFERENCE: {
-            final Bookmark bookmark = pojo instanceof Bookmark
-                    ? (Bookmark) pojo
-                    : dtoContext.getBookmarkService()!=null
-                            ? dtoContext.getBookmarkService().bookmarkFor(pojo).orElse(null)
-                            : null;
-
-            if (bookmark != null) {
-                OidDto argValue = bookmark.toOidDto();
-                valueDto.setReference(argValue);
-            }
-            return valueDto;
-        }
-        case BLOB: {
-            final Blob blob = (Blob) pojo;
-            if(blob != null) {
-                final BlobDto blobDto = new BlobDto();
-                blobDto.setName(blob.getName());
-                blobDto.setBytes(blob.getBytes());
-                blobDto.setMimeType(blob.getMimeType().toString());
-                valueDto.setBlob(blobDto);
-            }
-            return valueDto;
-        }
-        case CLOB: {
-            final Clob clob = (Clob) pojo;
-            if(clob != null) {
-                final ClobDto clobDto = new ClobDto();
-                clobDto.setName(clob.getName());
-                clobDto.setChars(clob.getChars().toString());
-                clobDto.setMimeType(clob.getMimeType().toString());
-                valueDto.setClob(clobDto);
-            }
-            return valueDto;
-        }
-        case VOID: {
-            return null;
-        }
-        default:
-            throw _Exceptions.unmatchedCase(valueType);
-        }
-    }
-
-    private static CollectionDto asCollectionDto(
-            final @Nullable Object iterableOrArray,
-            final @NonNull  ValueType commonElementValueType,
-            final @NonNull DtoContext dtoContext) {
-
-        val collectionDto = new CollectionDto();
-        collectionDto.setType(commonElementValueType);
-
-        val needsCommonElementValueTypeAutodetect = commonElementValueType==ValueType.VOID;
-
-        val commonElementValueTypeRef = _Refs.<ValueType>objectRef(null);
-
-        _NullSafe.streamAutodetect(iterableOrArray)
-        .forEach(element->{
-            val valueDto = new ValueDto();
-            if(element==null) {
-                setValueOn(valueDto, ValueType.VOID, element, dtoContext);
-            } else {
-                val elementValueType = asValueType(element.getClass());
-                setValueOn(valueDto, elementValueType, element, dtoContext);
-
-                if(needsCommonElementValueTypeAutodetect) {
-                    commonElementValueTypeRef.update(acc->reduce(acc, elementValueType));
-                }
-
-            }
-            collectionDto.getValue().add(valueDto);
-        });
-
-        if(needsCommonElementValueTypeAutodetect) {
-            collectionDto.setType(commonElementValueTypeRef.getValueElseDefault(ValueType.VOID));
-        }
-
-        return collectionDto;
-    }
-
-    private static TypedTupleDto asTypedTupleDto(
-            final @Nullable Object composite,
-            final @NonNull DtoContext dtoContext) {
-        val typedTupleDto = new TypedTupleDto();
-        //TODO implement
-        return typedTupleDto;
-    }
-
-    // -- getValue (from valueDto)
-
-    private static ValueType reduce(final ValueType acc, final ValueType next) {
-        if(acc==null) {
-            return next;
-        }
-        if(acc==next) {
-            return acc;
-        }
-        throw _Exceptions.unsupportedOperation("mixing types within a collection is not supported yet");
-    }
-
-    public static <T> T getValue(
-            final ValueDto valueDto,
-            final ValueType valueType) {
-        return _Casts.uncheckedCast(getValueAsObject(valueDto, valueType));
-    }
-
-    private static Object getValueAsObject(
-            final ValueDto valueDto,
-            final ValueType valueType) {
-        switch(valueType) {
-        case STRING:
-            return valueDto.getString();
-        case BYTE:
-            return valueDto.getByte();
-        case SHORT:
-            return valueDto.getShort();
-        case INT:
-            return valueDto.getInt();
-        case LONG:
-            return valueDto.getLong();
-        case FLOAT:
-            return valueDto.getFloat();
-        case DOUBLE:
-            return valueDto.getDouble();
-        case BOOLEAN:
-            return valueDto.isBoolean();
-        case CHAR:
-            final String aChar = valueDto.getChar();
-            if(_Strings.isNullOrEmpty(aChar)) { return null; }
-            return aChar.charAt(0);
-        case BIG_DECIMAL:
-            return valueDto.getBigDecimal();
-        case BIG_INTEGER:
-            return valueDto.getBigInteger();
-         // JAVA TIME
-        case LOCAL_DATE:
-            return JavaTimeXMLGregorianCalendarMarshalling.toLocalDate(valueDto.getLocalDate());
-        case LOCAL_TIME:
-            return JavaTimeXMLGregorianCalendarMarshalling.toLocalTime(valueDto.getLocalTime());
-        case LOCAL_DATE_TIME:
-            return JavaTimeXMLGregorianCalendarMarshalling.toLocalDateTime(valueDto.getLocalDateTime());
-        case OFFSET_DATE_TIME:
-            return JavaTimeXMLGregorianCalendarMarshalling.toOffsetDateTime(valueDto.getOffsetDateTime());
-        case OFFSET_TIME:
-            return JavaTimeXMLGregorianCalendarMarshalling.toOffsetTime(valueDto.getOffsetTime());
-        case ZONED_DATE_TIME:
-            return JavaTimeXMLGregorianCalendarMarshalling.toZonedDateTime(valueDto.getZonedDateTime());
-//        case JAVA_SQL_TIMESTAMP:
-//            return JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(valueDto.getTimestamp());
-        case ENUM:
-            final EnumDto enumDto = valueDto.getEnum();
-            final String enumType = enumDto.getEnumType();
-            @SuppressWarnings("rawtypes")
-            final Class<? extends Enum> enumClass = loadClassElseThrow(enumType);
-            return Enum.valueOf(_Casts.uncheckedCast(enumClass), enumDto.getEnumName());
-        case REFERENCE:
-            return valueDto.getReference();
-        case COLLECTION:
-            val collectionDto = valueDto.getCollection();
-            if(_NullSafe.isEmpty(collectionDto.getValue())) {
-                return Collections.emptyList();
-            }
-            val list = new ArrayList<Object>();
-
-            val elementValueType = collectionDto.getType();
-
-            for(val elementValueDto : collectionDto.getValue()) {
-
-                if(elementValueDto instanceof ValueWithTypeDto) {
-                    list.add(getValueAsObject(elementValueDto, ((ValueWithTypeDto)elementValueDto).getType()));
-                } else {
-                    list.add(getValueAsObject(elementValueDto, elementValueType));
-                }
-
-            }
-            return list;
-        case BLOB:
-            final BlobDto blobDto = valueDto.getBlob();
-            return new Blob(blobDto.getName(), blobDto.getMimeType(), blobDto.getBytes());
-        case CLOB:
-            final ClobDto clobDto = valueDto.getClob();
-            return new Clob(clobDto.getName(), clobDto.getMimeType(), clobDto.getChars());
-        case VOID:
-            return null;
-        default:
-            throw _Exceptions.unmatchedCase(valueType);
-        }
-    }
-
-    private static <T> Class<T> loadClassElseThrow(final String className) {
-        try {
-            return _Casts.uncheckedCast(_Context.loadClassAndInitialize(className));
-        } catch (ClassNotFoundException e) {
-
-            // [ahuber] fallback to pre 2.0.0 behavior, not sure if needed
-            try {
-                return _Casts.uncheckedCast(Class.forName(className));
-            } catch (ClassNotFoundException e1) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-
-
-    // -- newValueWithTypeDto
-
-
-    public static ValueWithTypeDto newValueWithTypeDto(
-            final Class<?> type,
-            final Object val,
-            final @NonNull DtoContext dtoContext) {
-
-        final ValueWithTypeDto valueWithTypeDto = new ValueWithTypeDto();
-
-        final ValueType valueType = asValueType(type);
-        setValueOn(valueWithTypeDto, valueType, val, dtoContext);
-
-        return valueWithTypeDto;
-    }
-
-
-
-    // -- getValue (from ValueWithTypeDto)
-
-    public static <T> T getValue(final ValueWithTypeDto valueWithTypeDto) {
-        if(valueWithTypeDto.isNull()) {
-            return null;
-        }
-        final ValueType type = valueWithTypeDto.getType();
-        return CommonDtoUtils.getValue(valueWithTypeDto, type);
-    }
-
-
-
-
-
-    // -- newParamDto
-
-    public static ParamDto newParamDto(
-            final String parameterName,
-            final Class<?> parameterType,
-            final Object arg,
-            final @NonNull DtoContext dtoContext) {
-
-        val paramDto = new ParamDto();
-        paramDto.setName(parameterName);
-
-        ValueType valueType = CommonDtoUtils.asValueType(parameterType);
-        // this hack preserves previous behaviour before we were able to serialize blobs and clobs into XML
-        // however, we also don't want this new behaviour for parameter arguments
-        // (else these large objects could end up being persisted).
-        if(valueType == ValueType.BLOB) valueType = ValueType.REFERENCE;
-        if(valueType == ValueType.CLOB) valueType = ValueType.REFERENCE;
-
-        paramDto.setType(valueType);
-
-        CommonDtoUtils.setValueOn(paramDto, valueType, arg, dtoContext);
-
-        return paramDto;
-    }
-
-    public static ParamDto newParamDtoNonScalar(
-            final String parameterName,
-            final Class<?> parameterElementType,
-            final Object arg,
-            final @NonNull DtoContext dtoContext) {
-
-        val paramDto = new ParamDto();
-        paramDto.setName(parameterName);
-        paramDto.setType(ValueType.COLLECTION);
-
-        val elementValueType = CommonDtoUtils.asValueType(parameterElementType);
-        CommonDtoUtils.setValueOnNonScalar(paramDto, elementValueType, arg, dtoContext);
-
-        return paramDto;
-    }
-
-
-    // -- getValue (from ParamDto)
-
-    public static <T> T getValue(final ParamDto paramDto) {
-        if(paramDto.isNull()) {
-            return null;
-        }
-        final ValueType parameterType = paramDto.getType();
-        return CommonDtoUtils.getValue(paramDto, parameterType);
-    }
-
-
-
-    public static String getMapValue(final MapDto mapDto, final String key) {
+//    private static ValueTypeAndSemantics<?> asValueType(
+//            final @NonNull Class<?> type,
+//            final @NonNull SchemaValueMarshaller valueMarshaller) {
+//
+//        if(Iterable.class.isAssignableFrom(type)
+//                        || type.isArray()) {
+//            return ValueTypeAndSemantics.of(ValueType.COLLECTION, null);
+//        }
+////        if (type.isEnum()) {
+////            return ValueType.ENUM;
+////        }
+////        // not strictly required: an optimization to infer ValueType, when directly mapped
+////        final ValueType valueType = valueTypeByClass.get(type);
+////        if (valueType != null) {
+////            return valueType;
+////        }
+//        return ValueTypeAndSemantics.resolve(valueMarshaller, type);
+//    }
+
+
+    // -- MAP-DTO SUPPORT
+
+    static String getMapValue(final MapDto mapDto, final String key) {
         if(mapDto == null) {
             return null;
         }
@@ -585,7 +111,7 @@ public final class CommonDtoUtils {
         return entryIfAny.map(MapDto.Entry::getValue).orElse(null);
     }
 
-    public static void putMapKeyValue(final MapDto mapDto, final String key, final String value) {
+    static void putMapKeyValue(final MapDto mapDto, final String key, final String value) {
         if(mapDto == null) {
             return;
         }
@@ -607,8 +133,4 @@ public final class CommonDtoUtils {
     }
 
 
-    public static boolean isValueType(final Class<?> type) {
-        return VALUE_TYPES.contains(type);
-    }
-
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/schema/InteractionDtoUtils.java b/api/applib/src/main/java/org/apache/isis/applib/util/schema/InteractionDtoUtils.java
index c2d2cdd..20a4a7d 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/schema/InteractionDtoUtils.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/util/schema/InteractionDtoUtils.java
@@ -37,6 +37,7 @@ import javax.xml.bind.Unmarshaller;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactn.Execution;
 import org.apache.isis.applib.services.iactn.Interaction;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.applib.util.JaxbUtil;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Lists;
@@ -60,12 +61,10 @@ import lombok.NonNull;
  */
 public final class InteractionDtoUtils {
 
-
     public static void init() {
         getJaxbContext();
     }
 
-
     // -- marshalling
     static JAXBContext jaxbContext;
     static JAXBContext getJaxbContext() {
@@ -345,10 +344,10 @@ public final class InteractionDtoUtils {
             final String parameterName,
             final Class<?> parameterType,
             final Object arg,
-            final @NonNull DtoContext dtoContext) {
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
 
         final List<ParamDto> params = parameterListFor(interactionDto);
-        ParamDto paramDto = CommonDtoUtils.newParamDto(parameterName, parameterType, arg, dtoContext);
+        final ParamDto paramDto = valueMarshaller.newParamDtoScalar(parameterName, parameterType, arg);
         params.add(paramDto);
     }
 
@@ -359,17 +358,15 @@ public final class InteractionDtoUtils {
      *
      * @param returnType - to determine the value type (if any)
      * @param result - either a value type (possibly boxed primitive), or a reference type
-     * @param dtoContext - used if not a fundamental value type
+     * @param valueMarshaller - used if not a fundamental value type
      */
     public static void addReturn(
             final ActionInvocationDto invocationDto,
             final Class<?> returnType,
             final Object result,
-            final @NonNull DtoContext dtoContext) {
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
 
-        final ValueWithTypeDto returned = CommonDtoUtils
-                .newValueWithTypeDto(returnType, result, dtoContext);
-        invocationDto.setReturned(returned);
+        valueMarshaller.putActionResult(invocationDto, returnType, result);
     }
 
 
@@ -393,14 +390,14 @@ public final class InteractionDtoUtils {
     public static List<String> getParameterNames(final ActionInvocationDto ai) {
         return Collections.unmodifiableList(
                 _NullSafe.stream(getParameters(ai))
-                .map(CommonDtoUtils.PARAM_DTO_TO_NAME)
+                .map(ParamDto::getName)
                 .collect(Collectors.toList())
                 );
     }
     public static List<ValueType> getParameterTypes(final ActionInvocationDto ai) {
         return Collections.unmodifiableList(
                 _NullSafe.stream(getParameters(ai))
-                .map(CommonDtoUtils.PARAM_DTO_TO_TYPE)
+                .map(ParamDto::getType)
                 .collect(Collectors.toList())
                 );
     }
@@ -438,9 +435,10 @@ public final class InteractionDtoUtils {
 
     // -- getParameterArgValue
 
-    public static <T> T getParameterArgValue(final ActionInvocationDto ai, final int paramNum) {
+    public static <T> T getParameterArgValue(final ActionInvocationDto ai, final int paramNum,
+            @NonNull final SchemaValueMarshaller valueMarshaller) {
         final ParamDto paramDto = getParameter(ai, paramNum);
-        return CommonDtoUtils.getValue(paramDto);
+        return (T) valueMarshaller.recoverValueFrom(ai.getLogicalMemberIdentifier(), paramDto);
     }
 
     // -- debugging (dump)
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/schema/DtoContext.java b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/Converter.java
similarity index 66%
rename from api/applib/src/main/java/org/apache/isis/applib/util/schema/DtoContext.java
rename to api/applib/src/main/java/org/apache/isis/applib/value/semantics/Converter.java
index 816a465..74bda7d 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/schema/DtoContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/Converter.java
@@ -16,20 +16,24 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.util.schema;
-
-import org.apache.isis.applib.services.bookmark.BookmarkService;
-import org.apache.isis.applib.value.semantics.ValueSemanticsResolver;
+package org.apache.isis.applib.value.semantics;
 
 /**
- * Provides the runtime context for converting <i>Schema</i> DTOs
- * between their XML and their Java type representation.
+ * Provides forth and back conversion between 2 types.
+ *
+ * @param <T> - value-type
+ * @param <D> - value-type, the <i>delegate</i>
+ *
+ * @see DefaultsProvider
+ * @see Parser
+ * @see EncoderDecoder
+ * @see ValueSemanticsProvider
  *
  * @since 2.x {@index}
  */
-public interface DtoContext {
+public interface Converter<T, D> {
 
-    BookmarkService getBookmarkService();
-    ValueSemanticsResolver getValueSemanticsResolver();
+    public abstract T fromDelegateValue(D value);
+    public abstract D toDelegateValue(T value);
 
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsAbstract.java
index 89f36ec..9adb8c2 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsAbstract.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsAbstract.java
@@ -60,6 +60,12 @@ implements
 
     @SuppressWarnings("unchecked")
     @Override
+    public Converter<T, ?> getConverter() {
+        return this instanceof Converter ? (Converter<T, ?>)this : null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
     public Renderer<T> getRenderer() {
         return this instanceof Renderer ? (Renderer<T>)this : null;
     }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsProvider.java b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsProvider.java
index 2524887..3745b0c 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsProvider.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/ValueSemanticsProvider.java
@@ -71,6 +71,11 @@ public interface ValueSemanticsProvider<T> {
     OrderRelation<T, ?> getOrderRelation();
 
     /**
+     * The {@link Converter}, if any.
+     */
+    Converter<T, ?> getConverter();
+
+    /**
      * The {@link Renderer}, if any.
      */
     Renderer<T> getRenderer();
diff --git a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_Test.java b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_Test.java
index d6f9643..e94cb2a 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_Test.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_Test.java
@@ -20,18 +20,16 @@ package org.apache.isis.applib.util.schema;
 
 import org.jmock.auto.Mock;
 import org.jmock.integration.junit4.JUnitRuleMockery;
-import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 
 import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.fail;
 
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.schema.cmd.v2.MapDto;
-import org.apache.isis.schema.common.v2.ValueDto;
-import org.apache.isis.schema.common.v2.ValueType;
 
 public class CommonDtoUtils_Test {
 
@@ -39,7 +37,7 @@ public class CommonDtoUtils_Test {
     public JUnitRuleMockery context = new JUnitRuleMockery();
 
     @Mock
-    private DtoContext mockDtoContext;
+    private SchemaValueMarshaller mockDtoContext;
 
     @Test
     public void enums() {
@@ -57,20 +55,23 @@ public class CommonDtoUtils_Test {
 
     private void test(final Enum<?> enumVal) {
 
-        // when
-        final ValueType valueType = CommonDtoUtils.asValueType(enumVal.getClass());
-
-        // then
-        assertThat(valueType, is(ValueType.ENUM));
-
-        // and when
-        final ValueDto valueDto = CommonDtoUtils.newValueDto(valueType, enumVal, mockDtoContext);
-
-        // then
-        Object value = CommonDtoUtils.getValue(valueDto, valueType);
-        assertThat(value, is(notNullValue()));
-
-        Assert.assertEquals(value, enumVal);
+        fail("not implemented");
+
+//        // when
+//        val valueTypeAndSemantics = CommonDtoUtils.asValueType(enumVal.getClass(), mockDtoContext);
+//        final ValueType valueType = valueTypeAndSemantics.getValueType();
+//
+//        // then
+//        assertThat(valueType, is(ValueType.ENUM));
+//
+//        // and when
+//        final ValueDto valueDto = CommonDtoUtils.newValueDto(valueTypeAndSemantics, enumVal, mockDtoContext);
+//
+//        // then
+//        Object value = CommonDtoUtils.getValue(valueDto, valueTypeAndSemantics);
+//        assertThat(value, is(notNullValue()));
+//
+//        Assert.assertEquals(value, enumVal);
     }
 
     @Test
diff --git a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_setValueOn_Test.java b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_setValueOn_Test.java
index 30e5ae5..a0823af 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_setValueOn_Test.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommonDtoUtils_setValueOn_Test.java
@@ -20,21 +20,14 @@ package org.apache.isis.applib.util.schema;
 
 import org.jmock.auto.Mock;
 import org.jmock.integration.junit4.JUnitRuleMockery;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.jupiter.api.Assertions.fail;
 
-import org.apache.isis.applib.value.Blob;
-import org.apache.isis.applib.value.Clob;
-import org.apache.isis.schema.common.v2.BlobDto;
-import org.apache.isis.schema.common.v2.ClobDto;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.schema.common.v2.ValueDto;
-import org.apache.isis.schema.common.v2.ValueType;
 
 public class CommonDtoUtils_setValueOn_Test {
 
@@ -42,7 +35,7 @@ public class CommonDtoUtils_setValueOn_Test {
     public JUnitRuleMockery context = new JUnitRuleMockery();
 
     @Mock
-    private DtoContext mockDtoContext;
+    private SchemaValueMarshaller mockDtoContext;
 
     ValueDto valueDto;
     @Before
@@ -51,38 +44,43 @@ public class CommonDtoUtils_setValueOn_Test {
     }
 
     @Test
-    public void when_blob_is_null() {
-        CommonDtoUtils.setValueOn(valueDto, ValueType.BLOB, null, mockDtoContext);
-        final BlobDto blobDto = valueDto.getBlob();
-        Assert.assertThat(blobDto, is(nullValue()));
+    public void not_implemented() {
+        fail("setValueOn not implemented");
     }
 
-    @Test
-    public void when_blob_is_not_null() {
-        final Blob val = new Blob("image.png", "image/png", new byte[]{1,2,3,4,5});
-        CommonDtoUtils.setValueOn(valueDto, ValueType.BLOB, val, mockDtoContext);
-        final BlobDto blobDto = valueDto.getBlob();
-        Assert.assertThat(blobDto, is(notNullValue()));
-        Assert.assertThat(blobDto.getBytes(), is(val.getBytes()));
-        Assert.assertThat(blobDto.getName(), is(val.getName()));
-        Assert.assertThat(blobDto.getMimeType(), is(val.getMimeType().toString()));
-    }
-
-    @Test
-    public void when_clob_is_null() {
-        CommonDtoUtils.setValueOn(valueDto, ValueType.CLOB, null, mockDtoContext);
-        final ClobDto clobDto = valueDto.getClob();
-        Assert.assertThat(clobDto, is(nullValue()));
-    }
-
-    @Test
-    public void when_clob_is_not_null() {
-        final Clob val = new Clob("image.png", "image/png", new char[]{1,2,3,4,5});
-        CommonDtoUtils.setValueOn(valueDto, ValueType.CLOB, val, mockDtoContext);
-        final ClobDto clobDto = valueDto.getClob();
-        Assert.assertThat(clobDto, is(notNullValue()));
-        Assert.assertThat(clobDto.getChars(), is(val.getChars()));
-        Assert.assertThat(clobDto.getName(), is(val.getName()));
-        Assert.assertThat(clobDto.getMimeType(), is(val.getMimeType().toString()));
-    }
+//    @Test
+//    public void when_blob_is_null() {
+//        CommonDtoUtils.setValueOn(valueDto, ValueTypeAndSemantics.of(ValueType.BLOB, null), null, mockDtoContext);
+//        final BlobDto blobDto = valueDto.getBlob();
+//        Assert.assertThat(blobDto, is(nullValue()));
+//    }
+//
+//    @Test
+//    public void when_blob_is_not_null() {
+//        final Blob val = new Blob("image.png", "image/png", new byte[]{1,2,3,4,5});
+//        CommonDtoUtils.setValueOn(valueDto, ValueTypeAndSemantics.of(ValueType.BLOB, null), val, mockDtoContext);
+//        final BlobDto blobDto = valueDto.getBlob();
+//        Assert.assertThat(blobDto, is(notNullValue()));
+//        Assert.assertThat(blobDto.getBytes(), is(val.getBytes()));
+//        Assert.assertThat(blobDto.getName(), is(val.getName()));
+//        Assert.assertThat(blobDto.getMimeType(), is(val.getMimeType().toString()));
+//    }
+//
+//    @Test
+//    public void when_clob_is_null() {
+//        CommonDtoUtils.setValueOn(valueDto, ValueTypeAndSemantics.of(ValueType.CLOB, null), null, mockDtoContext);
+//        final ClobDto clobDto = valueDto.getClob();
+//        Assert.assertThat(clobDto, is(nullValue()));
+//    }
+//
+//    @Test
+//    public void when_clob_is_not_null() {
+//        final Clob val = new Clob("image.png", "image/png", new char[]{1,2,3,4,5});
+//        CommonDtoUtils.setValueOn(valueDto, ValueTypeAndSemantics.of(ValueType.CLOB, null), val, mockDtoContext);
+//        final ClobDto clobDto = valueDto.getClob();
+//        Assert.assertThat(clobDto, is(notNullValue()));
+//        Assert.assertThat(clobDto.getChars(), is(val.getChars()));
+//        Assert.assertThat(clobDto.getName(), is(val.getName()));
+//        Assert.assertThat(clobDto.getMimeType(), is(val.getMimeType().toString()));
+//    }
 }
\ No newline at end of file
diff --git a/api/applib/src/test/java/org/apache/isis/applib/util/schema/Roundtrip.java b/api/applib/src/test/java/org/apache/isis/applib/util/schema/Roundtrip.java
index 77ff2fb..e971581 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/util/schema/Roundtrip.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/util/schema/Roundtrip.java
@@ -42,13 +42,13 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
-import org.apache.isis.applib.value.semantics.ValueSemanticsResolver;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.schema.cmd.v2.ParamDto;
+import org.apache.isis.schema.cmd.v2.PropertyDto;
 import org.apache.isis.schema.common.v2.InteractionType;
 import org.apache.isis.schema.common.v2.OidDto;
 import org.apache.isis.schema.common.v2.ValueType;
@@ -90,17 +90,57 @@ public class Roundtrip {
 
     private static void addArg(final InteractionDto interactionDto, final Object sampleValue) {
 
-        val dtoContext = new DtoContext() {
-            @Override public BookmarkService getBookmarkService() {
-                return null; }
-            @Override public ValueSemanticsResolver getValueSemanticsResolver() {
-                return null; }
-        };
+        val valueMarshaller = schemaValueMarshaller();
 
         val type = sampleValue.getClass();
         val name = type.getSimpleName();
-        InteractionDtoUtils.addParamArg(interactionDto, "a"+name, type, sampleValue, dtoContext);
-        InteractionDtoUtils.addParamArg(interactionDto, "null"+name, type, type.cast(null), dtoContext);
+        InteractionDtoUtils.addParamArg(interactionDto, "a"+name, type, sampleValue, valueMarshaller);
+        InteractionDtoUtils.addParamArg(interactionDto, "null"+name, type, type.cast(null), valueMarshaller);
+    }
+
+    private static SchemaValueMarshaller schemaValueMarshaller() {
+        return new SchemaValueMarshaller() {
+
+            @Override
+            public Object recoverValueFrom(final PropertyDto propertyDto) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+
+            @Override
+            public ActionInvocationDto putActionResult(final ActionInvocationDto invocationDto, final Class<?> returnType,
+                    final Object result) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public PropertyDto putValueInto(final PropertyDto propertyDto, final Class<?> propertyType, final Object valuePojo) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public ParamDto newParamDtoScalar(final String parameterName, final Class<?> paramType, final Object valuePojo) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public ParamDto newParamDtoNonScalar(final String parameterName, final Class<?> paramElementType, final Object valuePojo) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+
+            @Override
+            public Object recoverValueFrom(final String logicalMemberIdentifier, final ParamDto paramDto) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+        };
     }
 
     private static void testArg(
@@ -108,6 +148,7 @@ public class Roundtrip {
             final LongAdder paramIndex,
             final ValueType valueType,
             final Object expectedValue) {
+
         testArg(invocationDto, paramIndex, valueType, expectedValue, null);
     }
 
@@ -118,6 +159,8 @@ public class Roundtrip {
             final Object expectedValue,
             final String nameOverride) {
 
+        val valueMarshaller = schemaValueMarshaller();
+
         paramIndex.increment();
         int param = paramIndex.intValue();
 
@@ -127,7 +170,7 @@ public class Roundtrip {
         assertThat(InteractionDtoUtils.getParameterType(invocationDto, param), Matchers.is(valueType));
         assertThat(InteractionDtoUtils.isNull(invocationDto, param), is(false));
 
-        val actualValue = InteractionDtoUtils.getParameterArgValue(invocationDto, param);
+        val actualValue = InteractionDtoUtils.getParameterArgValue(invocationDto, param, valueMarshaller);
 
         // equals test, some types need special checks ...
         if(expectedValue instanceof OidDto) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
index 03372f4..03d1fe8 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
@@ -29,7 +29,6 @@ import java.util.regex.Pattern;
 import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
 import org.apache.isis.applib.services.metamodel.Config;
 import org.apache.isis.applib.spec.Specification;
-import org.apache.isis.applib.util.schema.CommonDtoUtils;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
@@ -454,10 +453,9 @@ class MetaModelExporter {
     }
 
     private boolean isValueType(final ObjectSpecification specification) {
-        return CommonDtoUtils.isValueType(specification.getCorrespondingClass());
+        return specification.getBeanSort().isValue();
     }
 
-
     private String asStr(final Object attributeObj) {
         String str;
         if(attributeObj instanceof Method) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/BookmarkValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/BookmarkValueSemantics.java
index c07f79f..823cfb5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/BookmarkValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/BookmarkValueSemantics.java
@@ -27,7 +27,6 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuetypes.ValueSemanticsAdapter;
 import org.apache.isis.schema.common.v2.OidDto;
-import org.apache.isis.schema.common.v2.ValueType;
 
 @Component
 @Named("isis.val.BookmarkValueSemantics")
@@ -42,11 +41,6 @@ extends ValueSemanticsAdapter<Bookmark, OidDto, Void> {
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
-    }
-
-    @Override
     public ValueSemanticsAbstract<OidDto> getDelegate() {
         return oidDtoValueSemantics;
     }
@@ -61,6 +55,4 @@ extends ValueSemanticsAdapter<Bookmark, OidDto, Void> {
         return value!=null ? value.toOidDto() : null;
     }
 
-
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/LocalResourcePathValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/LocalResourcePathValueSemantics.java
index 92b1773..cfb8fd0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/LocalResourcePathValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/LocalResourcePathValueSemantics.java
@@ -50,7 +50,7 @@ implements
 
     @Override
     public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
+        return ValueType.STRING; // this type can be easily converted to string and back
     }
 
     // -- ENCODER DECODER
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java
index 76dd090..eac8f1a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java
@@ -46,7 +46,7 @@ implements
 
     @Override
     public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
+        return ValueType.STRING; // this type can be easily converted to string and back;
     }
 
     // -- ENCODER DECODER
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java
index 906da73..43dfa80 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java
@@ -55,7 +55,7 @@ implements
 
     @Override
     public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
+        return ValueType.REFERENCE;
     }
 
     // -- ORDER RELATION
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/URLValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/URLValueSemantics.java
index 599bb68..5f74870 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/URLValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/URLValueSemantics.java
@@ -50,7 +50,7 @@ implements
 
     @Override
     public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
+        return ValueType.STRING; // this type can be easily converted to string and back
     }
 
     // -- ENCODER DECODER
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/UUIDValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/UUIDValueSemantics.java
index afdf140..0be0511 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/UUIDValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/UUIDValueSemantics.java
@@ -50,7 +50,7 @@ implements
 
     @Override
     public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
+        return ValueType.STRING; // this type can be easily converted to string and back
     }
 
     // -- ENCODER DECODER
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlDateValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlDateValueSemantics.java
index 966a97e..2c1420b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlDateValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlDateValueSemantics.java
@@ -29,7 +29,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.LocalDateValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 
 /**
  * An adapter that handles {@link java.sql.Date} with only date component.
@@ -50,11 +49,6 @@ extends TemporalSemanticsAdapter<Date, LocalDate> {
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
-    }
-
-    @Override
     public ValueSemanticsAbstract<LocalDate> getDelegate() {
         return localDateValueSemantics;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeStampValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeStampValueSemantics.java
index fb448be..997dd86 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeStampValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeStampValueSemantics.java
@@ -29,7 +29,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.LocalDateTimeValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 
 @Component
 @Named("isis.val.JavaSqlTimeStampValueSemantics")
@@ -44,11 +43,6 @@ extends TemporalSemanticsAdapter<Timestamp, LocalDateTime> {
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return ValueType.LOCAL_DATE_TIME;
-    }
-
-    @Override
     public ValueSemanticsAbstract<LocalDateTime> getDelegate() {
         return localDateTimeValueSemantics;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeValueSemantics.java
index 8200432..2abba15 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaSqlTimeValueSemantics.java
@@ -29,7 +29,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.LocalTimeValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 
 /**
  * Treats {@link java.sql.Time} as a time-only value type.
@@ -48,11 +47,6 @@ extends TemporalSemanticsAdapter<Time, LocalTime>  {
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
-    }
-
-    @Override
     public ValueSemanticsAbstract<LocalTime> getDelegate() {
         return localTimeValueSemantics;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaUtilDateValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaUtilDateValueSemantics.java
index 08fff1d..f32c240 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaUtilDateValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/legacy/JavaUtilDateValueSemantics.java
@@ -30,7 +30,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.LocalDateTimeValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 
 /**
  * An adapter that handles {@link java.util.Date} as both a date AND time
@@ -52,11 +51,6 @@ extends TemporalSemanticsAdapter<Date, LocalDateTime>  {
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return UNREPRESENTED;
-    }
-
-    @Override
     public ValueSemanticsAbstract<LocalDateTime> getDelegate() {
         return localDateTimeValueSemantics;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueSemanticsAdapter.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueSemanticsAdapter.java
index 4910f19..9b2aead 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueSemanticsAdapter.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueSemanticsAdapter.java
@@ -18,12 +18,14 @@
  */
 package org.apache.isis.core.metamodel.valuetypes;
 
+import org.apache.isis.applib.value.semantics.Converter;
 import org.apache.isis.applib.value.semantics.EncoderDecoder;
 import org.apache.isis.applib.value.semantics.OrderRelation;
 import org.apache.isis.applib.value.semantics.Parser;
 import org.apache.isis.applib.value.semantics.Renderer;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
+import org.apache.isis.schema.common.v2.ValueType;
 
 import lombok.val;
 
@@ -33,12 +35,19 @@ implements
     OrderRelation<T, E>,
     EncoderDecoder<T>,
     Parser<T>,
-    Renderer<T> {
+    Renderer<T>,
+    Converter<T, D>{
 
     public abstract ValueSemanticsAbstract<D> getDelegate();
 
-    public abstract T fromDelegateValue(D value);
-    public abstract D toDelegateValue(T value);
+    /**
+     * By design, adapters always share their <i>SchemaValueType</i> with their delegate.
+     * @see ValueSemanticsProvider#getSchemaValueType()
+     */
+    @Override
+    public final ValueType getSchemaValueType() {
+        return getDelegate().getSchemaValueType();
+    }
 
     // -- ORDER RELATION
 
@@ -59,6 +68,8 @@ implements
                 .equals(toDelegateValue(a), toDelegateValue(b), epsilon);
     }
 
+    // -- CONVERTER
+
 
     // -- ENCODER DECODER
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
index 914e044..da7db926 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
@@ -31,7 +31,7 @@ import org.apache.isis.core.runtime.IsisModuleCoreRuntime;
 import org.apache.isis.core.runtimeservices.bookmarks.BookmarkServiceDefault;
 import org.apache.isis.core.runtimeservices.command.CommandDtoFactoryDefault;
 import org.apache.isis.core.runtimeservices.command.CommandExecutorServiceDefault;
-import org.apache.isis.core.runtimeservices.command.DtoContextDefault;
+import org.apache.isis.core.runtimeservices.command.SchemaValueMarshallerDefault;
 import org.apache.isis.core.runtimeservices.email.EmailServiceDefault;
 import org.apache.isis.core.runtimeservices.eventbus.EventBusServiceSpring;
 import org.apache.isis.core.runtimeservices.executor.MemberExecutorServiceDefault;
@@ -107,9 +107,7 @@ import org.apache.isis.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDefaul
         XmlServiceDefault.class,
         XmlSnapshotServiceDefault.class,
         ObjectLifecyclePublisherDefault.class,
-
-        // @Component's
-        DtoContextDefault.class,
+        SchemaValueMarshallerDefault.class,
 
         // @Controller
         RoutingServiceDefault.class,
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoFactoryDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoFactoryDefault.java
index 0c8f396..67894ab 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoFactoryDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoFactoryDefault.java
@@ -30,10 +30,9 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.clock.ClockService;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.applib.services.user.UserService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.applib.util.schema.CommonDtoUtils;
-import org.apache.isis.applib.util.schema.DtoContext;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
@@ -66,7 +65,7 @@ import lombok.val;
 @Qualifier("Default")
 public class CommandDtoFactoryDefault implements CommandDtoFactory {
 
-    @Inject private DtoContext dtoContext;
+    @Inject private SchemaValueMarshaller valueMarshaller;
     @Inject private ClockService clockService;
     @Inject private UserService userService;
 
@@ -127,18 +126,16 @@ public class CommandDtoFactoryDefault implements CommandDtoFactory {
             val paramTypeOrElementType = actionParameter.getElementType().getCorrespondingClass();
 
             val paramDto = actionParameter.getFeatureType() == FeatureType.ACTION_PARAMETER_COLLECTION
-                    ? CommonDtoUtils.newParamDtoNonScalar(
+                    ? valueMarshaller.newParamDtoNonScalar(
                             actionParameter.getStaticFriendlyName()
                                 .orElseThrow(_Exceptions::unexpectedCodeReach),
                             paramTypeOrElementType,
-                            arg,
-                            dtoContext)
-                    : CommonDtoUtils.newParamDto(
+                            arg)
+                    : valueMarshaller.newParamDtoScalar(
                             actionParameter.getStaticFriendlyName()
                                 .orElseThrow(_Exceptions::unexpectedCodeReach),
                             paramTypeOrElementType,
-                            arg,
-                            dtoContext);
+                            arg);
 
             CommandDtoUtils.parametersFor(actionDto)
                 .getParameter()
@@ -158,9 +155,7 @@ public class CommandDtoFactoryDefault implements CommandDtoFactory {
         val valueSpec = property.getElementType();
         val valueType = valueSpec.getCorrespondingClass();
 
-        val newValue = CommonDtoUtils.newValueWithTypeDto(
-                valueType, UnwrapUtil.single(valueAdapter), dtoContext);
-        propertyDto.setNewValue(newValue);
+        valueMarshaller.putValueInto(propertyDto, valueType, UnwrapUtil.single(valueAdapter));
     }
 
     // -- HELPER
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
index d74e376..69c6b5e 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
@@ -18,10 +18,19 @@
  */
 package org.apache.isis.core.runtimeservices.command;
 
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import lombok.val;
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
@@ -33,10 +42,10 @@ import org.apache.isis.applib.services.command.CommandOutcomeHandler;
 import org.apache.isis.applib.services.iactn.Execution;
 import org.apache.isis.applib.services.iactnlayer.InteractionLayerTracker;
 import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.applib.services.sudo.SudoService;
 import org.apache.isis.applib.services.xactn.TransactionService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.applib.util.schema.CommonDtoUtils;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.functional.Result;
 import org.apache.isis.commons.internal.base._NullSafe;
@@ -53,22 +62,20 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionMixedIn;
-import org.apache.isis.schema.cmd.v2.*;
+import org.apache.isis.schema.cmd.v2.ActionDto;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+import org.apache.isis.schema.cmd.v2.MemberDto;
+import org.apache.isis.schema.cmd.v2.ParamDto;
+import org.apache.isis.schema.cmd.v2.ParamsDto;
+import org.apache.isis.schema.cmd.v2.PropertyDto;
 import org.apache.isis.schema.common.v2.InteractionType;
 import org.apache.isis.schema.common.v2.OidDto;
 import org.apache.isis.schema.common.v2.OidsDto;
-import org.apache.isis.schema.common.v2.ValueWithTypeDto;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Service;
 
-import javax.annotation.Priority;
-import javax.inject.Inject;
-import javax.inject.Named;
-import java.sql.Timestamp;
-import java.util.List;
-import java.util.Optional;
-import java.util.regex.Pattern;
-import java.util.stream.Stream;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 @Service
 @Named("isis.runtimeservices.CommandExecutorServiceDefault")
@@ -86,6 +93,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
     @Inject final ClockService clockService;
     @Inject final TransactionService transactionService;
     @Inject final InteractionLayerTracker iInteractionLayerTracker;
+    @Inject final SchemaValueMarshaller valueMarshaller;
 
     @Inject @Getter final InteractionService interactionService;
     @Inject @Getter final SpecificationLoader specificationLoader;
@@ -234,7 +242,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
 
                 final OneToOneAssociation property = findOneToOneAssociation(targetAdapter, memberId);
 
-                val newValueAdapter = newValueAdapterFor(propertyDto);
+                val newValueAdapter = recoverValueFrom(propertyDto);
 
                 property.set(targetAdapter, newValueAdapter, InteractionInitiatedBy.FRAMEWORK);
 
@@ -317,17 +325,23 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
         return property;
     }
 
-    private static String localPartOf(String memberId) {
+    private static String localPartOf(final String memberId) {
         val matcher = ID_PARSER.matcher(memberId);
         return matcher.matches()
                 ? matcher.group("localId")
                 : "";
     }
 
-    private ManagedObject newValueAdapterFor(final PropertyDto propertyDto) {
-        final ValueWithTypeDto newValue = propertyDto.getNewValue();
-        final Object arg = CommonDtoUtils.getValue(newValue);
-        return adapterFor(arg);
+    private ManagedObject recoverValueFrom(final PropertyDto propertyDto) {
+        val newValue = valueMarshaller.recoverValueFrom(propertyDto);
+        return adapterFor(newValue);
+    }
+
+    private ManagedObject recoverValueFrom(
+            final ActionDto actionDto,
+            final ParamDto paramDto) {
+        val newValue = valueMarshaller.recoverValueFrom(actionDto.getLogicalMemberIdentifier(), paramDto);
+        return adapterFor(newValue);
     }
 
     private static ObjectAction findActionElseNull(
@@ -349,8 +363,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
 
     private Can<ManagedObject> argAdaptersFor(final ActionDto actionDto) {
         return streamParamDtosFrom(actionDto)
-                .map(CommonDtoUtils::getValue)
-                .map(this::adapterFor)
+                .map(paramDto->recoverValueFrom(actionDto, paramDto))
                 .collect(Can.toCan());
     }
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/DtoContextDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/DtoContextDefault.java
deleted file mode 100644
index af3dbdb..0000000
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/DtoContextDefault.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.apache.isis.core.runtimeservices.command;
-
-import javax.annotation.Priority;
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Component;
-
-import org.apache.isis.applib.annotation.PriorityPrecedence;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
-import org.apache.isis.applib.util.schema.DtoContext;
-import org.apache.isis.applib.value.semantics.ValueSemanticsResolver;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Component
-@Named("isis.runtimeservices.DtoContextDefault")
-@Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("Default")
-@Getter(onMethod_ = {@Override})
-@RequiredArgsConstructor
-public class DtoContextDefault implements DtoContext {
-
-    @Inject private BookmarkService bookmarkService;
-    @Inject private ValueSemanticsResolver valueSemanticsResolver;
-
-}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
similarity index 52%
copy from api/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
copy to core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
index a4bd003..da6b5ea 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
@@ -1,22 +1,4 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.util.schema;
+package org.apache.isis.core.runtimeservices.command;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -28,27 +10,33 @@ import java.time.OffsetTime;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
 
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Service;
 
+import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.jaxb.JavaTimeXMLGregorianCalendarMarshalling;
 import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
+import org.apache.isis.applib.value.semantics.Converter;
+import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
+import org.apache.isis.applib.value.semantics.ValueSemanticsResolver;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.base._Refs;
 import org.apache.isis.commons.internal.base._Strings;
-import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.schema.cmd.v2.MapDto;
 import org.apache.isis.schema.cmd.v2.ParamDto;
+import org.apache.isis.schema.cmd.v2.PropertyDto;
 import org.apache.isis.schema.common.v2.BlobDto;
 import org.apache.isis.schema.common.v2.ClobDto;
 import org.apache.isis.schema.common.v2.CollectionDto;
@@ -58,136 +46,182 @@ import org.apache.isis.schema.common.v2.TypedTupleDto;
 import org.apache.isis.schema.common.v2.ValueDto;
 import org.apache.isis.schema.common.v2.ValueType;
 import org.apache.isis.schema.common.v2.ValueWithTypeDto;
-
-import static org.apache.isis.commons.internal.collections._Maps.entry;
+import org.apache.isis.schema.ixn.v2.ActionInvocationDto;
 
 import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.Value;
 import lombok.val;
 
-/**
- * @since 1.x {@index}
- */
-public final class CommonDtoUtils {
-
-    // -- PARAM_DTO_TO_NAME, PARAM_DTO_TO_TYPE
-
-    public static final Function<ParamDto, String> PARAM_DTO_TO_NAME = ParamDto::getName;
-    public static final Function<ParamDto, ValueType> PARAM_DTO_TO_TYPE = ParamDto::getType;
-
-    // -- asValueType
-    public static final Map<Class<?>, ValueType> valueTypeByClass =
-            _Maps.unmodifiableEntries(
-                    entry(Void.class, ValueType.VOID),
-                    entry(String.class, ValueType.STRING),
-                    entry(byte.class, ValueType.BYTE),
-                    entry(Byte.class, ValueType.BYTE),
-                    entry(short.class, ValueType.SHORT),
-                    entry(Short.class, ValueType.SHORT),
-                    entry(int.class, ValueType.INT),
-                    entry(Integer.class, ValueType.INT),
-                    entry(long.class, ValueType.LONG),
-                    entry(Long.class, ValueType.LONG),
-                    entry(char.class, ValueType.CHAR),
-                    entry(Character.class, ValueType.CHAR),
-                    entry(boolean.class, ValueType.BOOLEAN),
-                    entry(Boolean.class, ValueType.BOOLEAN),
-                    entry(float.class, ValueType.FLOAT),
-                    entry(Float.class, ValueType.FLOAT),
-                    entry(double.class, ValueType.DOUBLE),
-                    entry(Double.class, ValueType.DOUBLE),
-                    entry(BigInteger.class, ValueType.BIG_INTEGER),
-                    entry(BigDecimal.class, ValueType.BIG_DECIMAL),
-
-                    // java.time
-                    entry(LocalDate.class, ValueType.LOCAL_DATE),
-                    entry(LocalDateTime.class, ValueType.LOCAL_DATE_TIME),
-                    entry(LocalTime.class, ValueType.LOCAL_TIME),
-                    entry(OffsetDateTime.class, ValueType.OFFSET_DATE_TIME),
-                    entry(OffsetTime.class, ValueType.OFFSET_TIME),
-                    entry(ZonedDateTime.class, ValueType.ZONED_DATE_TIME),
-
-                    entry(Blob.class, ValueType.BLOB),
-                    entry(Clob.class, ValueType.CLOB)
-                    );
-
-    private static final Set<Class<?>> VALUE_TYPES = valueTypeByClass.keySet();
-
-    public static ValueType asValueType(final @NonNull Class<?> type) {
-        if(Iterable.class.isAssignableFrom(type)
-                        || type.isArray()) {
-            return ValueType.COLLECTION;
-        }
-        final ValueType valueType = valueTypeByClass.get(type);
-        if (valueType != null) {
-            return valueType;
-        }
-        if (type.isEnum()) {
-            return ValueType.ENUM;
-        }
-        // assume reference otherwise
-        return ValueType.REFERENCE;
+@Service
+@Named("isis.runtimeservices.SchemaValueMarshallerDefault")
+@Priority(PriorityPrecedence.MIDPOINT)
+@Qualifier("Default")
+@RequiredArgsConstructor
+public class SchemaValueMarshallerDefault implements SchemaValueMarshaller {
+
+    @Inject private BookmarkService bookmarkService;
+    @Inject private ValueSemanticsResolver valueSemanticsResolver;
+
+    // -- RECOVER VALUES FROM DTO
+
+    @Override
+    public Object recoverValueFrom(final PropertyDto propertyDto) {
+        final ValueWithTypeDto valueWithTypeDto = propertyDto.getNewValue();
+        return recoverValue(valueWithTypeDto, this);
     }
 
+    @Override
+    public Object recoverValueFrom(final String logicalMemberIdentifier, final ParamDto paramDto) {
+        return recoverValue(paramDto, this);
+    }
 
-    // -- newValueDto, setValueOn
+    // -- PUT VALUES INTO DTO
+
+    @Override
+    public ActionInvocationDto putActionResult(
+            final ActionInvocationDto invocationDto,
+            final Class<?> returnType,
+            final Object result) {
+        final ValueTypeAndSemantics<?> valueTypeAndSemantics = resolve(this, returnType);
+        final ValueWithTypeDto returned = newValueWithTypeDto(valueTypeAndSemantics, result, this);
+        invocationDto.setReturned(returned);
+        return invocationDto;
+    }
 
-    public static ValueDto newValueDto(
-            final ValueType valueType,
-            final Object value,
-            final @NonNull DtoContext dtoContext) {
+    @Override
+    public PropertyDto putValueInto(
+            final PropertyDto propertyDto,
+            final Class<?> propertyType,
+            final Object valuePojo) {
+        final ValueTypeAndSemantics<?> valueTypeAndSemantics = resolve(this, propertyType);
+        final ValueWithTypeDto newValue = newValueWithTypeDto(valueTypeAndSemantics, valuePojo, this);
+        propertyDto.setNewValue(newValue);
+        return propertyDto;
+    }
 
-        if(value == null) {
-            return null;
-        }
+    @Override
+    public ParamDto newParamDtoScalar(
+            final String parameterName,
+            final Class<?> paramType,
+            final Object valuePojo) {
+        val paramDto = new ParamDto();
+        paramDto.setName(parameterName);
+
+        final ValueTypeAndSemantics<?> valueTypeAndSemantics = resolve(this, paramType);
+
+//        ValueType valueType = valueTypeAndSemantics.getValueType();
+//
+//        // this hack preserves previous behaviour before we were able to serialize blobs and clobs into XML
+//        // however, we also don't want this new behaviour for parameter arguments
+//        // (else these large objects could end up being persisted).
+//        if(valueType == ValueType.BLOB) valueType = ValueType.REFERENCE;
+//        if(valueType == ValueType.CLOB) valueType = ValueType.REFERENCE;
+
+        paramDto.setType(valueTypeAndSemantics.getValueType());
+
+        putValueInto(paramDto, valueTypeAndSemantics, valuePojo, this);
 
-        final ValueDto valueDto = new ValueDto();
-        return setValueOn(valueDto, valueType, value, dtoContext);
+        return paramDto;
     }
 
-    public static <T extends ValueWithTypeDto> T setValueOn(
-            final T valueWithTypeDto,
-            final ValueType valueType,
-            final Object value,
-            final @NonNull DtoContext dtoContext) {
+    @Override
+    public ParamDto newParamDtoNonScalar(
+            final String parameterName,
+            final Class<?> paramElementType,
+            final Object valuePojo) {
+
+        val paramDto = new ParamDto();
+        paramDto.setName(parameterName);
+        paramDto.setType(ValueType.COLLECTION);
 
-        valueWithTypeDto.setType(valueType);
+        ValueTypeAndSemantics<?> elementValueTypeAndSemantics = resolve(this, paramElementType);
 
-        setValueOn((ValueDto)valueWithTypeDto, valueType, value, dtoContext);
-        valueWithTypeDto.setNull(value == null);
+        setValueOnNonScalar(paramDto, elementValueTypeAndSemantics, valuePojo, this);
+
+        return paramDto;
 
-        return valueWithTypeDto;
     }
 
-    public static <T extends ValueWithTypeDto> T setValueOnNonScalar(
-            final T valueWithTypeDto,
-            final ValueType elementValueType,
-            final Object value,
-            final @NonNull DtoContext dtoContext) {
+    // -- HELPER
 
-        valueWithTypeDto.setType(ValueType.COLLECTION);
+    @Value(staticConstructor = "of")
+    private static class ValueTypeAndSemantics<T> {
+        final @NonNull ValueType valueType;
+        final @Nullable ValueSemanticsProvider<T> semantics;
 
-        val collectionDto = asCollectionDto(value, elementValueType, dtoContext);
-        valueWithTypeDto.setCollection(collectionDto);
-        valueWithTypeDto.setNull(value == null);
+        private final static ValueTypeAndSemantics<Void> VOID = ValueTypeAndSemantics.of(ValueType.VOID, null);
+        public static ValueTypeAndSemantics<Void> empty() {
+            return VOID;
+        }
+
+        public Converter<T, ?> getConverter() {
+            return semantics!=null
+                    ? semantics.getConverter()
+                    : null;
+        }
+
+    }
+
+    private ValueTypeAndSemantics<?> resolve(
+            @NonNull final SchemaValueMarshaller valueMarshaller,
+            @NonNull final Class<?> type) {
+        return valueSemanticsResolver.selectValueSemantics(type)
+        .getFirst()
+        .map(valueSemantics->ValueTypeAndSemantics.of(valueSemantics.getSchemaValueType(), valueSemantics))
+        // assume reference otherwise
+        .orElseGet(()->ValueTypeAndSemantics.of(ValueType.REFERENCE, null));
+    }
+
+    private ValueTypeAndSemantics<?> resolve(
+            @NonNull final SchemaValueMarshaller valueMarshaller,
+            //@NonNull final Class<?> type,
+            final ParamDto paramDto) {
+        // TODO Auto-generated method stub
+
+        return ValueTypeAndSemantics.of(paramDto.getType(), null);
+    }
+
+    private ValueTypeAndSemantics<?> resolve(
+            @NonNull final SchemaValueMarshaller valueMarshaller,
+            //@NonNull final Class<?> type,
+            final ValueWithTypeDto valueWithTypeDto) {
+        // TODO Auto-generated method stub
+
+        return ValueTypeAndSemantics.of(valueWithTypeDto.getType(), null);
+    }
+
+    private ValueWithTypeDto newValueWithTypeDto(
+            final ValueTypeAndSemantics<?> valueTypeAndSemantics,
+            final Object valuePojo,
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
+
+        final ValueWithTypeDto valueWithTypeDto = new ValueWithTypeDto();
+        putValueInto(valueWithTypeDto, valueTypeAndSemantics, valuePojo, valueMarshaller);
 
         return valueWithTypeDto;
     }
 
-    public static <T extends ValueDto> T setValueOn(
+    private <T extends ValueDto> T putValueInto(
             final T valueDto,
-            final ValueType valueType,
+            final ValueTypeAndSemantics<?> valueTypeAndSemantics,
             final Object pojo,
-            final @NonNull DtoContext dtoContext) {
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
+
+        val valueType = valueTypeAndSemantics.getValueType();
+        val semantics = valueTypeAndSemantics.getSemantics();
+        val converter = valueTypeAndSemantics.getConverter();
 
         switch (valueType) {
         case COLLECTION: {
             final CollectionDto collectionDto = asCollectionDto(
-                    pojo, ValueType.VOID, dtoContext);
+                    pojo, ValueTypeAndSemantics.empty(), valueMarshaller);
             valueDto.setCollection(collectionDto);
             return valueDto;
         }
         case COMPOSITE: {
-            final TypedTupleDto typedTupleDto = asTypedTupleDto(pojo, dtoContext);
+            final TypedTupleDto typedTupleDto = asTypedTupleDto(pojo, valueMarshaller);
             valueDto.setComposite(typedTupleDto);
             return valueDto;
         }
@@ -248,32 +282,34 @@ public final class CommonDtoUtils {
         }
         case LOCAL_DATE: {
             final LocalDate argValue = (LocalDate) pojo;
-            valueDto.setLocalDate(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
+            valueDto.setLocalDate(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case LOCAL_TIME: {
             final LocalTime argValue = (LocalTime) pojo;
-            valueDto.setLocalTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
+            valueDto.setLocalTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case LOCAL_DATE_TIME: {
-            final LocalDateTime argValue = (LocalDateTime) pojo;
-            valueDto.setLocalDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
+            final LocalDateTime argValue = converter!=null
+                    ? (LocalDateTime) converter.toDelegateValue(_Casts.uncheckedCast(pojo))
+                    : (LocalDateTime) pojo;
+            valueDto.setLocalDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case OFFSET_DATE_TIME: {
             final OffsetDateTime argValue = (OffsetDateTime) pojo;
-            valueDto.setOffsetDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
+            valueDto.setOffsetDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case OFFSET_TIME: {
             final OffsetTime argValue = (OffsetTime) pojo;
-            valueDto.setOffsetTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
+            valueDto.setOffsetTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case ZONED_DATE_TIME: {
             final ZonedDateTime argValue = (ZonedDateTime) pojo;
-            valueDto.setZonedDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
+            valueDto.setZonedDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case ENUM: {
@@ -290,8 +326,8 @@ public final class CommonDtoUtils {
         case REFERENCE: {
             final Bookmark bookmark = pojo instanceof Bookmark
                     ? (Bookmark) pojo
-                    : dtoContext.getBookmarkService()!=null
-                            ? dtoContext.getBookmarkService().bookmarkFor(pojo).orElse(null)
+                    : bookmarkService!=null
+                            ? bookmarkService.bookmarkFor(pojo).orElse(null)
                             : null;
 
             if (bookmark != null) {
@@ -330,11 +366,28 @@ public final class CommonDtoUtils {
         }
     }
 
-    private static CollectionDto asCollectionDto(
+    @Deprecated
+    private <T extends ValueWithTypeDto> T setValueOnNonScalar(
+            final T valueWithTypeDto,
+            final ValueTypeAndSemantics<?> elementValueTypeAndSemantics,
+            final Object value,
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
+
+        valueWithTypeDto.setType(ValueType.COLLECTION);
+
+        val collectionDto = asCollectionDto(value, elementValueTypeAndSemantics, valueMarshaller);
+        valueWithTypeDto.setCollection(collectionDto);
+        valueWithTypeDto.setNull(value == null);
+
+        return valueWithTypeDto;
+    }
+
+    private CollectionDto asCollectionDto(
             final @Nullable Object iterableOrArray,
-            final @NonNull  ValueType commonElementValueType,
-            final @NonNull DtoContext dtoContext) {
+            final @NonNull ValueTypeAndSemantics<?> commonElementValueTypeAndSemantics,
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
 
+        val commonElementValueType = commonElementValueTypeAndSemantics.getValueType();
         val collectionDto = new CollectionDto();
         collectionDto.setType(commonElementValueType);
 
@@ -346,13 +399,14 @@ public final class CommonDtoUtils {
         .forEach(element->{
             val valueDto = new ValueDto();
             if(element==null) {
-                setValueOn(valueDto, ValueType.VOID, element, dtoContext);
+                putValueInto(valueDto, ValueTypeAndSemantics.empty(), element, valueMarshaller);
             } else {
-                val elementValueType = asValueType(element.getClass());
-                setValueOn(valueDto, elementValueType, element, dtoContext);
+                ValueTypeAndSemantics<?> elementValueTypeAndSemantics =
+                        resolve(valueMarshaller, element.getClass());
+                putValueInto(valueDto, elementValueTypeAndSemantics, element, valueMarshaller);
 
                 if(needsCommonElementValueTypeAutodetect) {
-                    commonElementValueTypeRef.update(acc->reduce(acc, elementValueType));
+                    commonElementValueTypeRef.update(acc->reduce(acc, elementValueTypeAndSemantics.getValueType()));
                 }
 
             }
@@ -366,35 +420,52 @@ public final class CommonDtoUtils {
         return collectionDto;
     }
 
+    private static ValueType reduce(final ValueType acc, final ValueType next) {
+        if(acc==null) {
+            return next;
+        }
+        if(acc==next) {
+            return acc;
+        }
+        throw _Exceptions.unsupportedOperation("mixing types within a collection is not supported yet");
+    }
+
     private static TypedTupleDto asTypedTupleDto(
             final @Nullable Object composite,
-            final @NonNull DtoContext dtoContext) {
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
         val typedTupleDto = new TypedTupleDto();
         //TODO implement
         return typedTupleDto;
     }
 
-    // -- getValue (from valueDto)
+    // -- HELPER - RECOVERY
 
-    private static ValueType reduce(final ValueType acc, final ValueType next) {
-        if(acc==null) {
-            return next;
-        }
-        if(acc==next) {
-            return acc;
+    private <T> T recoverValue(
+            final ValueWithTypeDto valueWithTypeDto,
+            final @NonNull SchemaValueMarshaller valueMarshaller) {
+        if(valueWithTypeDto==null
+                || (valueWithTypeDto.isSetNull()
+                    && valueWithTypeDto.isNull())) {
+            return null;
         }
-        throw _Exceptions.unsupportedOperation("mixing types within a collection is not supported yet");
+        return recoverValue(valueWithTypeDto, resolve(valueMarshaller, valueWithTypeDto));
     }
 
-    public static <T> T getValue(
+    private static <T> T recoverValue(
             final ValueDto valueDto,
-            final ValueType valueType) {
-        return _Casts.uncheckedCast(getValueAsObject(valueDto, valueType));
+            final @NonNull ValueTypeAndSemantics<?> valueTypeAndSemantics) {
+        return _Casts.uncheckedCast(recoverValueAsObject(valueDto, valueTypeAndSemantics));
     }
 
-    private static Object getValueAsObject(
+    @SneakyThrows
+    private static Object recoverValueAsObject(
             final ValueDto valueDto,
-            final ValueType valueType) {
+            final ValueTypeAndSemantics<?> valueTypeAndSemantics) {
+
+        val valueType = valueTypeAndSemantics.getValueType();
+        //val semantics = valueTypeAndSemantics.getSemantics();
+        //val converter = valueTypeAndSemantics.getConverter();
+
         switch(valueType) {
         case STRING:
             return valueDto.getString();
@@ -420,7 +491,6 @@ public final class CommonDtoUtils {
             return valueDto.getBigDecimal();
         case BIG_INTEGER:
             return valueDto.getBigInteger();
-         // JAVA TIME
         case LOCAL_DATE:
             return JavaTimeXMLGregorianCalendarMarshalling.toLocalDate(valueDto.getLocalDate());
         case LOCAL_TIME:
@@ -433,13 +503,12 @@ public final class CommonDtoUtils {
             return JavaTimeXMLGregorianCalendarMarshalling.toOffsetTime(valueDto.getOffsetTime());
         case ZONED_DATE_TIME:
             return JavaTimeXMLGregorianCalendarMarshalling.toZonedDateTime(valueDto.getZonedDateTime());
-//        case JAVA_SQL_TIMESTAMP:
-//            return JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(valueDto.getTimestamp());
         case ENUM:
             final EnumDto enumDto = valueDto.getEnum();
             final String enumType = enumDto.getEnumType();
             @SuppressWarnings("rawtypes")
-            final Class<? extends Enum> enumClass = loadClassElseThrow(enumType);
+            final Class<? extends Enum> enumClass =
+                    _Casts.uncheckedCast(_Context.loadClassAndInitialize(enumType));
             return Enum.valueOf(_Casts.uncheckedCast(enumClass), enumDto.getEnumName());
         case REFERENCE:
             return valueDto.getReference();
@@ -454,11 +523,12 @@ public final class CommonDtoUtils {
 
             for(val elementValueDto : collectionDto.getValue()) {
 
-                if(elementValueDto instanceof ValueWithTypeDto) {
-                    list.add(getValueAsObject(elementValueDto, ((ValueWithTypeDto)elementValueDto).getType()));
-                } else {
-                    list.add(getValueAsObject(elementValueDto, elementValueType));
-                }
+                //FIXME[ISIS-2877]
+//                if(elementValueDto instanceof ValueWithTypeDto) {
+//                    list.add(getValueAsObject(elementValueDto, ((ValueWithTypeDto)elementValueDto).getType()));
+//                } else {
+//                    list.add(getValueAsObject(elementValueDto, elementValueType));
+//                }
 
             }
             return list;
@@ -475,140 +545,5 @@ public final class CommonDtoUtils {
         }
     }
 
-    private static <T> Class<T> loadClassElseThrow(final String className) {
-        try {
-            return _Casts.uncheckedCast(_Context.loadClassAndInitialize(className));
-        } catch (ClassNotFoundException e) {
-
-            // [ahuber] fallback to pre 2.0.0 behavior, not sure if needed
-            try {
-                return _Casts.uncheckedCast(Class.forName(className));
-            } catch (ClassNotFoundException e1) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-
-
-    // -- newValueWithTypeDto
-
-
-    public static ValueWithTypeDto newValueWithTypeDto(
-            final Class<?> type,
-            final Object val,
-            final @NonNull DtoContext dtoContext) {
-
-        final ValueWithTypeDto valueWithTypeDto = new ValueWithTypeDto();
-
-        final ValueType valueType = asValueType(type);
-        setValueOn(valueWithTypeDto, valueType, val, dtoContext);
-
-        return valueWithTypeDto;
-    }
-
-
-
-    // -- getValue (from ValueWithTypeDto)
-
-    public static <T> T getValue(final ValueWithTypeDto valueWithTypeDto) {
-        if(valueWithTypeDto.isNull()) {
-            return null;
-        }
-        final ValueType type = valueWithTypeDto.getType();
-        return CommonDtoUtils.getValue(valueWithTypeDto, type);
-    }
-
-
-
-
-
-    // -- newParamDto
-
-    public static ParamDto newParamDto(
-            final String parameterName,
-            final Class<?> parameterType,
-            final Object arg,
-            final @NonNull DtoContext dtoContext) {
-
-        val paramDto = new ParamDto();
-        paramDto.setName(parameterName);
-
-        ValueType valueType = CommonDtoUtils.asValueType(parameterType);
-        // this hack preserves previous behaviour before we were able to serialize blobs and clobs into XML
-        // however, we also don't want this new behaviour for parameter arguments
-        // (else these large objects could end up being persisted).
-        if(valueType == ValueType.BLOB) valueType = ValueType.REFERENCE;
-        if(valueType == ValueType.CLOB) valueType = ValueType.REFERENCE;
-
-        paramDto.setType(valueType);
-
-        CommonDtoUtils.setValueOn(paramDto, valueType, arg, dtoContext);
-
-        return paramDto;
-    }
-
-    public static ParamDto newParamDtoNonScalar(
-            final String parameterName,
-            final Class<?> parameterElementType,
-            final Object arg,
-            final @NonNull DtoContext dtoContext) {
-
-        val paramDto = new ParamDto();
-        paramDto.setName(parameterName);
-        paramDto.setType(ValueType.COLLECTION);
-
-        val elementValueType = CommonDtoUtils.asValueType(parameterElementType);
-        CommonDtoUtils.setValueOnNonScalar(paramDto, elementValueType, arg, dtoContext);
-
-        return paramDto;
-    }
-
-
-    // -- getValue (from ParamDto)
-
-    public static <T> T getValue(final ParamDto paramDto) {
-        if(paramDto.isNull()) {
-            return null;
-        }
-        final ValueType parameterType = paramDto.getType();
-        return CommonDtoUtils.getValue(paramDto, parameterType);
-    }
-
-
-
-    public static String getMapValue(final MapDto mapDto, final String key) {
-        if(mapDto == null) {
-            return null;
-        }
-        final Optional<MapDto.Entry> entryIfAny = entryIfAnyFor(mapDto, key);
-        return entryIfAny.map(MapDto.Entry::getValue).orElse(null);
-    }
-
-    public static void putMapKeyValue(final MapDto mapDto, final String key, final String value) {
-        if(mapDto == null) {
-            return;
-        }
-        final Optional<MapDto.Entry> entryIfAny = entryIfAnyFor(mapDto, key);
-        if(entryIfAny.isPresent()) {
-            entryIfAny.get().setValue(value);
-        } else {
-            val entry = new MapDto.Entry();
-            entry.setKey(key);
-            entry.setValue(value);
-            mapDto.getEntry().add(entry);
-        }
-    }
-
-    private static Optional<MapDto.Entry> entryIfAnyFor(final MapDto mapDto, final String key) {
-        return mapDto.getEntry().stream()
-                .filter(entry->Objects.equals(entry.getKey(), key))
-                .findFirst();
-    }
-
-
-    public static boolean isValueType(final Class<?> type) {
-        return VALUE_TYPES.contains(type);
-    }
 
 }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/interaction/InteractionDtoFactoryDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/interaction/InteractionDtoFactoryDefault.java
index 9fd5bd2..b606025 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/interaction/InteractionDtoFactoryDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/interaction/InteractionDtoFactoryDefault.java
@@ -31,9 +31,9 @@ import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.iactn.InteractionProvider;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.applib.services.user.UserService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.applib.util.schema.DtoContext;
 import org.apache.isis.applib.util.schema.InteractionDtoUtils;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.assertions._Assert;
@@ -68,7 +68,7 @@ import lombok.val;
 public class InteractionDtoFactoryDefault implements InteractionDtoFactory {
 
     @Inject private CommandDtoFactory commandDtoServiceInternal;
-    @Inject private DtoContext dtoContext;
+    @Inject private SchemaValueMarshaller valueMarshaller;
     @Inject private javax.inject.Provider<InteractionProvider> interactionProviderProvider;
     @Inject private UserService userService;
 
@@ -114,7 +114,7 @@ public class InteractionDtoFactoryDefault implements InteractionDtoFactory {
         final Class<?> returnType = returnSpec.getCorrespondingClass();
 
         InteractionDtoUtils.addReturn(
-                actionInvocationDto, returnType, resultPojo, dtoContext);
+                actionInvocationDto, returnType, resultPojo, valueMarshaller);
 
         return actionInvocationDto;
     }
diff --git a/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/ResponseDigest.java b/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/ResponseDigest.java
index bd7a671..5556294 100644
--- a/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/ResponseDigest.java
+++ b/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/ResponseDigest.java
@@ -28,7 +28,6 @@ import java.util.Optional;
 import java.util.concurrent.Future;
 import java.util.function.Function;
 
-import org.springframework.lang.Nullable;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status.Family;
@@ -37,8 +36,9 @@ import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import org.springframework.lang.Nullable;
+
 import org.apache.isis.applib.client.RepresentationTypeSimplifiedV2;
-import org.apache.isis.applib.util.schema.CommonDtoUtils;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._Strings;
@@ -107,7 +107,7 @@ public class ResponseDigest<T> {
     private Exception failureCause;
 
 
-    protected ResponseDigest(Response response, Class<T> entityType, GenericType<List<T>> genericType) {
+    protected ResponseDigest(final Response response, final Class<T> entityType, final GenericType<List<T>> genericType) {
         this.response = response;
         this.entityType = entityType;
         this.genericType = genericType;
@@ -152,7 +152,7 @@ public class ResponseDigest<T> {
      * @param failureMapper - fallback, to calculate a result from given failure exception
      * @return the result if cardinality is exactly ONE, otherwise the result of applying the failure to the {@code failureMapper}
      */
-    public T singletonOrElseMapFailure(Function<Exception, T> failureMapper) {
+    public T singletonOrElseMapFailure(final Function<Exception, T> failureMapper) {
         return isSuccess()
                 ? getEntity().orElseGet(()->failureMapper.apply(new NoSuchElementException()))
                 : failureMapper.apply(getFailureCause());
@@ -162,10 +162,10 @@ public class ResponseDigest<T> {
      * @param failureMapper - fallback, to calculate a result from given failure exception
      * @return the result of any cardinality, otherwise the result of applying the failure to the {@code failureMapper}
      */
-    public Can<T> multipleOrElseMapFailure(Function<Exception, Can<T>> failureMapper) {
+    public Can<T> multipleOrElseMapFailure(final Function<Exception, Can<T>> failureMapper) {
         return isSuccess()
                 ? getEntities()
-                        : failureMapper.apply(getFailureCause());
+                : failureMapper.apply(getFailureCause());
     }
 
     // -- HELPER
@@ -235,7 +235,7 @@ public class ResponseDigest<T> {
         log.debug("readSingle({})", reprType);
 
         if(reprType.isValue()
-                || isValueType(entityType)) {
+                || reprType.isValues()) {
             val mapper = new ObjectMapper();
             val jsonInput = response.readEntity(String.class);
             val scalarValueDto = mapper.readValue(jsonInput, ScalarValueDtoV2.class);
@@ -250,7 +250,7 @@ public class ResponseDigest<T> {
         log.debug("readList({})", reprType);
 
         if(reprType.isValues()
-                || isValueType(entityType)) {
+                || reprType.isValue()) {
             val mapper = new ObjectMapper();
             val jsonInput = response.readEntity(String.class);
             final List<ScalarValueDtoV2> scalarValueDtoList =
@@ -269,7 +269,7 @@ public class ResponseDigest<T> {
         return response.readEntity(genericType);
     }
 
-    private ResponseDigest<T> digestAsyncFailure(boolean isCancelled, Exception failure) {
+    private ResponseDigest<T> digestAsyncFailure(final boolean isCancelled, final Exception failure) {
 
         entities = Can.empty();
 
@@ -289,7 +289,7 @@ public class ResponseDigest<T> {
 
     }
 
-    private String defaultFailureMessage(Response response) {
+    private String defaultFailureMessage(final Response response) {
         String failureMessage = "non-successful JAX-RS response: " +
                 String.format("%s (Http-Status-Code: %d)",
                         response.getStatusInfo().getReasonPhrase(),
@@ -309,11 +309,7 @@ public class ResponseDigest<T> {
 
     // -- VALUE TYPE HANDLING
 
-    private boolean isValueType(Class<T> entityType) {
-        return CommonDtoUtils.isValueType(entityType);
-    }
-
-    private T extractValue(ScalarValueDtoV2 scalarValueDto)
+    private T extractValue(final ScalarValueDtoV2 scalarValueDto)
             throws JsonParseException, JsonMappingException, IOException {
         return _Casts.uncheckedCast(scalarValueDto.getValue());
     }
diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
index 5b2e80d..9569b0b 100644
--- a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
+++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
@@ -41,11 +41,16 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.iactnlayer.InteractionContext;
 import org.apache.isis.applib.services.iactnlayer.InteractionService;
 import org.apache.isis.applib.services.inject.ServiceInjector;
-import org.apache.isis.applib.util.schema.CommonDtoUtils;
+import org.apache.isis.applib.services.schema.SchemaValueMarshaller;
 import org.apache.isis.applib.value.Password;
+import org.apache.isis.applib.value.semantics.EncoderDecoder;
+import org.apache.isis.applib.value.semantics.Parser;
+import org.apache.isis.applib.value.semantics.Renderer;
+import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
 import org.apache.isis.applib.value.semantics.ValueSemanticsResolver;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.core.config.presets.IsisPresets;
@@ -59,6 +64,7 @@ import org.apache.isis.testdomain.model.valuetypes.Configuration_usingValueTypes
 import org.apache.isis.testdomain.model.valuetypes.ValueTypeExample;
 import org.apache.isis.testdomain.model.valuetypes.ValueTypeExampleService;
 import org.apache.isis.testdomain.model.valuetypes.ValueTypeExampleService.Scenario;
+import org.apache.isis.testdomain.value.ValueSemanticsTester.PropertyInteractionProbe;
 
 import lombok.val;
 
@@ -77,6 +83,28 @@ import lombok.val;
 @TestInstance(Lifecycle.PER_CLASS)
 class ValueSemanticsTest {
 
+    @Test @Disabled
+    void fullTypeCoverage() {
+
+        valueSemanticsResolver.streamClassesWithValueSemantics()
+        .forEach(valueType->System.err.printf("%s%n", valueType.getName()));
+
+        final Set<Class<?>> valueTypesCovered = valueTypeExampleProvider.streamExamples()
+        .map(ValueTypeExample::getValueType)
+        .collect(Collectors.toSet());
+
+        final Set<Class<?>> valueTypesKnown = valueSemanticsResolver.streamClassesWithValueSemantics()
+        .collect(Collectors.toSet());
+
+        val valueTypesNotCovered = _Sets.minus(valueTypesKnown, valueTypesCovered);
+
+        assertTrue(valueTypesNotCovered.isEmpty(), ()->
+            String.format("value-types not covered by tests:\n\t%s",
+                    valueTypesNotCovered.stream()
+                    .map(Class::getName)
+                    .collect(Collectors.joining("\n\t"))));
+    }
+
     @ParameterizedTest(name = "{index} {0}")
     @MethodSource("provideValueTypeExamples")
     <T> void valueTypes(
@@ -91,122 +119,114 @@ class ValueSemanticsTest {
         tester.propertyInteraction("value",
                 interactionContext(),
                 managedProp->example.getUpdateValue(),
-                (context, composer)->{
-
-                    val valueMixin = composer.getValueMixin(example.getValue());
-                    if(valueMixin!=null) {
+                new PropertyInteractionProbe<T>() {
 
-                        val spec = specLoader.specForTypeElseFail(valueMixin.getClass());
-                        val interaction = ActionInteraction
-                                .start(ManagedObject.of(spec,  valueMixin), "act", Where.ANYWHERE);
+                    @Override
+                    public void testEncoderDecoder(
+                            final ValueSemanticsProvider.Context context,
+                            final EncoderDecoder<T> composer) {
+                        val valueMixin = composer.getValueMixin(example.getValue());
+                        if(valueMixin!=null) {
 
-                        val pendingParams = interaction
-                                .startParameterNegotiation()
-                                .get();
+                            val spec = specLoader.specForTypeElseFail(valueMixin.getClass());
+                            val interaction = ActionInteraction
+                                    .start(ManagedObject.of(spec,  valueMixin), "act", Where.ANYWHERE);
 
-                        val managedAction = interaction.getManagedActionElseFail();
-                        val typedTuple = pendingParams.getParamValues();
-
-                        val recoveredValue = managedAction
-                                .invoke(typedTuple, InteractionInitiatedBy.PASS_THROUGH)
-                                .leftIfAny()
-                                .getPojo();
-
-                        tester.assertValueEquals(
-                                example.getValue(),
-                                recoveredValue,
-                                "serialization roundtrip failed");
-
-                        return;
-                    }
+                            val pendingParams = interaction
+                                    .startParameterNegotiation()
+                                    .get();
 
-                    // CoderDecoder round-trip test
-                    val serialized = composer.toEncodedString(example.getValue());
+                            val managedAction = interaction.getManagedActionElseFail();
+                            val typedTuple = pendingParams.getParamValues();
 
-                    tester.assertValueEquals(
-                            example.getValue(),
-                            composer.fromEncodedString(serialized),
-                            "serialization roundtrip failed");
+                            val recoveredValue = managedAction
+                                    .invoke(typedTuple, InteractionInitiatedBy.PASS_THROUGH)
+                                    .leftIfAny()
+                                    .getPojo();
 
-                },
-                (context, parser)->{
+                            tester.assertValueEquals(
+                                    example.getValue(),
+                                    recoveredValue,
+                                    "serialization roundtrip failed");
 
-                    // Parser round-trip test
-                    val stringified = parser.parseableTextRepresentation(context, example.getValue());
+                            return;
+                        }
 
-                    if(valueType.equals(Password.class)) {
-                        val recoveredValue = (Password)parser.parseTextRepresentation(context, stringified);
-                        assertTrue(recoveredValue.checkPassword("*"));
-
-                    } else {
-
-                        System.err.printf("using %s trying to parse '%s'%n", valueType.getName(), stringified);
+                        // CoderDecoder round-trip test
+                        val serialized = composer.toEncodedString(example.getValue());
 
                         tester.assertValueEquals(
                                 example.getValue(),
-                                parser.parseTextRepresentation(context, stringified),
-                                "parser roundtrip failed");
+                                composer.fromEncodedString(serialized),
+                                "serialization roundtrip failed");
                     }
 
-                },
-                (context, renderer)->{
+                    @Override
+                    public void testParser(
+                            final ValueSemanticsProvider.Context context,
+                            final Parser<T> parser) {
+                        // Parser round-trip test
+                        val stringified = parser.parseableTextRepresentation(context, example.getValue());
 
-                },
-                (command, codec)->{
+                        if(valueType.equals(Password.class)) {
+                            val recoveredValue = (Password)parser.parseTextRepresentation(context, stringified);
+                            assertTrue(recoveredValue.checkPassword("*"));
 
-                    val propertyDto = (PropertyDto)command.getCommandDto().getMember();
-                    val newValueRecordedDto = propertyDto.getNewValue();
+                        } else {
 
-                    //TODO needs a codec to recover values that are not directly represented by the schema
-                    val newValueRecorded = CommonDtoUtils.getValue(newValueRecordedDto);
+                            System.err.printf("using %s trying to parse '%s'%n", valueType.getName(), stringified);
 
-                    // TODO skip tests, because some value-types are not represented by the schema yet
-                    if(newValueRecorded==null
-                            || valueType.equals(Bookmark.class)) {
-                        System.err.printf("skipping command test on %s%n", valueType.getName());
-                        return;
+                            tester.assertValueEquals(
+                                    example.getValue(),
+                                    parser.parseTextRepresentation(context, stringified),
+                                    "parser roundtrip failed");
+                        }
                     }
+                    @Override
+                    public void testRenderer(
+                            final ValueSemanticsProvider.Context context,
+                            final Renderer<T> renderer) {
 
-                    assertEquals(valueType, newValueRecorded.getClass(), ()->
-                        String.format("command value parsing type mismatch '%s'",
-                                ValueSemanticsTester.valueDtoToXml(newValueRecordedDto)));
-
-                    tester.assertValueEquals(example.getUpdateValue(), newValueRecorded, "command failed");
-
-//                    //debug
-//                    System.err.printf("Value %s %s%n", name,
-//                            valueToXml(newValueRecordedDto));
-//
-//                    //debug
-//                    System.err.printf("CommandDto %s %s%n", name,
-//                            CommandDtoUtils.toXml(
-//                                    command.getCommandDto()));
+                    }
+                    @Override
+                    public void testCommand(
+                            final ValueSemanticsProvider.Context context,
+                            final Command command,
+                            final EncoderDecoder<T> codec) {
+
+                        val propertyDto = (PropertyDto)command.getCommandDto().getMember();
+                        val newValueRecordedDto = propertyDto.getNewValue();
+
+                        //TODO needs a codec to recover values that are not directly represented by the schema
+                        val newValueRecorded = valueMarshaller.recoverValueFrom(propertyDto);
+                        assertNotNull(newValueRecorded);
+
+                        // TODO skip tests, because some value-types are not represented by the schema yet
+                        if(newValueRecorded==null
+                                || valueType.equals(Bookmark.class)) {
+                            System.err.printf("skipping command test on %s%n", valueType.getName());
+                            return;
+                        }
+
+                        assertEquals(valueType, newValueRecorded.getClass(), ()->
+                            String.format("command value parsing type mismatch '%s'",
+                                    ValueSemanticsTester.valueDtoToXml(newValueRecordedDto)));
+
+                        tester.assertValueEquals(example.getUpdateValue(), newValueRecorded, "command failed");
+
+//                        //debug
+//                        System.err.printf("Value %s %s%n", name,
+//                                valueToXml(newValueRecordedDto));
+    //
+//                        //debug
+//                        System.err.printf("CommandDto %s %s%n", name,
+//                                CommandDtoUtils.toXml(
+//                                        command.getCommandDto()));
+                    }
                 });
 
     }
 
-    @Test @Disabled
-    void fullTypeCoverage() {
-
-        valueSemanticsResolver.streamClassesWithValueSemantics()
-        .forEach(valueType->System.err.printf("%s%n", valueType.getName()));
-
-        final Set<Class<?>> valueTypesCovered = valueTypeExampleProvider.streamExamples()
-        .map(ValueTypeExample::getValueType)
-        .collect(Collectors.toSet());
-
-        final Set<Class<?>> valueTypesKnown = valueSemanticsResolver.streamClassesWithValueSemantics()
-        .collect(Collectors.toSet());
-
-        val valueTypesNotCovered = _Sets.minus(valueTypesKnown, valueTypesCovered);
-
-        assertTrue(valueTypesNotCovered.isEmpty(), ()->
-            String.format("value-types not covered by tests:\n\t%s",
-                    valueTypesNotCovered.stream()
-                    .map(Class::getName)
-                    .collect(Collectors.joining("\n\t"))));
-    }
-
     // -- HELPER
 
     private InteractionContext interactionContext() {
@@ -220,6 +240,7 @@ class ValueSemanticsTest {
     @Inject InteractionService interactionService;
     @Inject ServiceInjector serviceInjector;
     @Inject ValueSemanticsResolver valueSemanticsResolver;
+    @Inject SchemaValueMarshaller valueMarshaller;
 
     Stream<Arguments> provideValueTypeExamples() {
         return valueTypeExampleProvider.streamScenarios()
diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
index f4b9770..a497efc 100644
--- a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
+++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
@@ -38,7 +38,6 @@ import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._Refs;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.commons.internal.functions._Functions.CheckedBiConsumer;
 import org.apache.isis.commons.internal.resources._Xml;
 import org.apache.isis.commons.internal.resources._Xml.WriteOptions;
 import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
@@ -73,15 +72,19 @@ public class ValueSemanticsTester<T> {
         val act = objSpec.getActionElseFail(actionId);
     }
 
+    public static interface PropertyInteractionProbe<T> {
+        void testEncoderDecoder(ValueSemanticsProvider.Context context, EncoderDecoder<T> codec);
+        void testParser(ValueSemanticsProvider.Context context, Parser<T> parser);
+        void testRenderer(ValueSemanticsProvider.Context context, Renderer<T> renderer);
+        void testCommand(ValueSemanticsProvider.Context context, Command command, EncoderDecoder<T> codec);
+    }
+
     @SneakyThrows
     public void propertyInteraction(
             final @NonNull String propertyId,
             final @NonNull InteractionContext interactionContext,
             final @NonNull Function<ManagedProperty, Object> newProperyValueProvider,
-            final @NonNull CheckedBiConsumer<ValueSemanticsProvider.Context, EncoderDecoder<T>> codecCallback,
-            final @NonNull CheckedBiConsumer<ValueSemanticsProvider.Context, Parser<T>> parserCallback,
-            final @NonNull CheckedBiConsumer<ValueSemanticsProvider.Context, Renderer<T>> renderCallback,
-            final @NonNull CheckedBiConsumer<Command, EncoderDecoder<T>> commandCallback) {
+            final @NonNull PropertyInteractionProbe<T> probe) {
 
         val objSpec = specLoader.specForTypeElseFail(domainObject.getClass());
         val prop = objSpec.getPropertyElseFail(propertyId);
@@ -91,16 +94,16 @@ public class ValueSemanticsTester<T> {
 
         val codec = codec(prop);
 
-        codecCallback.accept(context, codec);
+        probe.testEncoderDecoder(context, codec);
 
         val parserIfAny = parser(prop);
         if(parserIfAny.isPresent()) {
-            parserCallback.accept(context, parserIfAny.get());
+            probe.testParser(context, parserIfAny.get());
         }
 
         val rendererIfAny = renderer(prop);
         if(rendererIfAny.isPresent()) {
-            renderCallback.accept(context, rendererIfAny.get());
+            probe.testRenderer(context, rendererIfAny.get());
         }
 
         interactionService.run(interactionContext, ()->{
@@ -113,7 +116,7 @@ public class ValueSemanticsTester<T> {
             propInteraction.modifyProperty(managedProp->
                 ManagedObject.of(managedProp.getElementType(), newProperyValueProvider.apply(managedProp)));
 
-            commandCallback.accept(command, codec);
+            probe.testCommand(context, command, codec);
         });
     }
 
diff --git a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaDateTimeValueSemantics.java b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaDateTimeValueSemantics.java
index af50dfa..1152e6e 100644
--- a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaDateTimeValueSemantics.java
+++ b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaDateTimeValueSemantics.java
@@ -29,7 +29,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.ZonedDateTimeValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 import org.apache.isis.valuetypes.jodatime.applib.value.JodaTimeConverters;
 
 @Component
@@ -45,11 +44,6 @@ extends TemporalSemanticsAdapter<org.joda.time.DateTime, ZonedDateTime>  {
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return ValueType.ZONED_DATE_TIME;
-    }
-
-    @Override
     public ValueSemanticsAbstract<ZonedDateTime> getDelegate() {
         return zonedDateTimeValueSemantics;
     }
diff --git a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateTimeValueSemantics.java b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateTimeValueSemantics.java
index c019ca0..f97d789 100644
--- a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateTimeValueSemantics.java
+++ b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateTimeValueSemantics.java
@@ -26,7 +26,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.LocalDateTimeValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 import org.apache.isis.valuetypes.jodatime.applib.value.JodaTimeConverters;
 
 @Component
@@ -42,11 +41,6 @@ extends TemporalSemanticsAdapter<org.joda.time.LocalDateTime, java.time.LocalDat
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return ValueType.LOCAL_DATE_TIME;
-    }
-
-    @Override
     public ValueSemanticsAbstract<java.time.LocalDateTime> getDelegate() {
         return localDateTimeValueSemantics;
     }
diff --git a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateValueSemantics.java b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateValueSemantics.java
index c7f36c1..b37c6f9 100644
--- a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateValueSemantics.java
+++ b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalDateValueSemantics.java
@@ -26,7 +26,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.LocalDateValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 import org.apache.isis.valuetypes.jodatime.applib.value.JodaTimeConverters;
 
 @Component
@@ -42,11 +41,6 @@ extends TemporalSemanticsAdapter<org.joda.time.LocalDate, java.time.LocalDate>
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return ValueType.LOCAL_DATE;
-    }
-
-    @Override
     public ValueSemanticsAbstract<java.time.LocalDate> getDelegate() {
         return localDateValueSemantics;
     }
diff --git a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalTimeValueSemantics.java b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalTimeValueSemantics.java
index 84feff4..bc9b0d9 100644
--- a/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalTimeValueSemantics.java
+++ b/valuetypes/jodatime/integration/src/main/java/org/apache/isis/valuetypes/jodatime/integration/valuesemantics/JodaLocalTimeValueSemantics.java
@@ -26,7 +26,6 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.isis.core.metamodel.valuesemantics.temporal.LocalTimeValueSemantics;
 import org.apache.isis.core.metamodel.valuetypes.TemporalSemanticsAdapter;
-import org.apache.isis.schema.common.v2.ValueType;
 import org.apache.isis.valuetypes.jodatime.applib.value.JodaTimeConverters;
 
 @Component
@@ -42,11 +41,6 @@ extends TemporalSemanticsAdapter<org.joda.time.LocalTime, java.time.LocalTime>
     }
 
     @Override
-    public ValueType getSchemaValueType() {
-        return ValueType.LOCAL_TIME;
-    }
-
-    @Override
     public ValueSemanticsAbstract<java.time.LocalTime> getDelegate() {
         return localTimeValueSemantics;
     }