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:07 UTC

[isis] branch 2877_dto_rountrip_using_value_semantics created (now 8b51771)

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

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


      at 8b51771  ISIS-2877: refactor CommonDtoUtils into its own Service

This branch includes the following new commits:

     new 8b51771  ISIS-2877: refactor CommonDtoUtils into its own Service

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


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

Posted by ah...@apache.org.
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;
     }