You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by pa...@apache.org on 2017/04/23 11:07:34 UTC

[4/7] polygene-java git commit: POLYGENE-247 Change Entity state JSON serialization format

POLYGENE-247 Change Entity state JSON serialization format

JSONEntityState now serialize entity states just like values.

Before:

{
  reference: \u201cid\u201d,
  application_version: \u201c..\u201d,
  type: \u201c..\u201d,
  version: \u201c..\u201d,
  modified: \u201c..\u201d,
  properties: {
    someProp: \u201c..\u201d
  },
  associations: {
    someAssoc: \u201c..\u201d
  },
  manyassociations: {
    someManyAssoc: [ \u201c..\u201d, \u201c..\u201d ]
  },
  namedassociations: {
    someNamedAssoc: { key: \u201cvalue\u201d }
  }
}

After:

{
  reference: \u201cid\u201d,
  application_version: \u201c..\u201d,
  type: \u201c..\u201d,
  version: \u201c..\u201d,
  modified: \u201c..\u201d,
  value: {
    someProp: \u201c..\u201d,
    someAssoc: \u201c..\u201d,
    someManyAssoc: [ \u201c..\u201d, \u201c..\u201d ],
    someNamedAssoc: { key: \u201cvalue\u201d }
  }
}

Fixed a few aspects of serialization implementation along the way.
Most notable change is that we don\u2019t fallback to Java Serialization by
default, for correctness and safety. A JavaSerialization converter is
provided if needed.

Performance increased by reusing JSON factories and other minor
optimizations.


Project: http://git-wip-us.apache.org/repos/asf/polygene-java/repo
Commit: http://git-wip-us.apache.org/repos/asf/polygene-java/commit/b2ff271b
Tree: http://git-wip-us.apache.org/repos/asf/polygene-java/tree/b2ff271b
Diff: http://git-wip-us.apache.org/repos/asf/polygene-java/diff/b2ff271b

Branch: refs/heads/develop
Commit: b2ff271bf3dcc85e8afacbc8b3c9d12117c13273
Parents: 718fa57
Author: Paul Merlin <pa...@apache.org>
Authored: Sun Apr 23 13:05:22 2017 +0200
Committer: Paul Merlin <pa...@apache.org>
Committed: Sun Apr 23 13:05:22 2017 +0200

----------------------------------------------------------------------
 .../StatefulAssociationCompositeDescriptor.java |   3 +
 .../polygene/api/entity/EntityDescriptor.java   |   4 +
 .../polygene/api/serialization/ConvertedBy.java |   7 +-
 .../polygene/api/serialization/Converters.java  |  50 +-
 .../JavaSerializationConverter.java             |  88 +++
 .../polygene/api/serialization/Serializer.java  |  61 +-
 .../polygene/api/type/EntityCompositeType.java  |  63 ++
 .../api/type/StatefulAssociationValueType.java  |  75 +++
 .../polygene/api/type/ValueCompositeType.java   |  40 +-
 .../polygene/api/value/ValueDescriptor.java     |   1 +
 .../polygene/api/type/ValueTypeFactoryTest.java |  28 +-
 .../polygene/api/value/ValueCompositeTest.java  |  27 +-
 .../DefaultSerializationAssembler.java          |  12 +-
 .../composite/FunctionStateResolver.java        |  28 +-
 .../polygene/runtime/entity/EntityInstance.java |   6 +-
 .../polygene/runtime/entity/EntityModel.java    |  10 +-
 .../runtime/entity/EntityStateInstance.java     |   8 +-
 .../runtime/type/ValueTypeFactoryInstance.java  |  13 +
 .../polygene/runtime/value/ValueInstance.java   |   4 +-
 .../entity/EntityCompositeToStringTest.java     |  90 +++
 .../memory/MemoryMapEntityStoreMixin.java       |  11 +-
 .../serialization/javaxjson/JavaxJson.java      |  49 --
 .../javaxjson/JavaxJsonAdapter.java             |   4 +-
 .../javaxjson/JavaxJsonAdapters.java            |  72 ++-
 .../javaxjson/JavaxJsonDeserializer.java        |  43 +-
 .../javaxjson/JavaxJsonFactories.java           | 101 +++
 .../javaxjson/JavaxJsonSerializer.java          |  41 +-
 .../entitystore/helpers/JSONEntityState.java    | 346 ++++-------
 .../spi/entitystore/helpers/JSONKeys.java       |  16 +-
 .../helpers/JSONManyAssociationState.java       |  14 +-
 .../helpers/JSONMapEntityStoreMixin.java        |  80 +--
 .../helpers/JSONNamedAssociationState.java      |  14 +-
 .../helpers/MapEntityStoreMixin.java            |  74 +--
 .../spi/serialization/AbstractDeserializer.java |  23 +-
 .../spi/serialization/AbstractSerializer.java   |  18 -
 .../spi/serialization/JsonDeserializer.java     |   5 +-
 .../spi/serialization/XmlDeserializer.java      |   5 +-
 .../helpers/JSONManyAssociationStateTest.java   |  47 +-
 .../helpers/JsonNamedAssociationStateTest.java  |  30 +-
 .../AbstractPlainValueSerializationTest.java    |  17 +
 ...AbstractValueCompositeSerializationTest.java |  56 +-
 .../entitystore/sql/SQLMapEntityStoreMixin.java |  10 +-
 .../sql/SQLMapEntityStoreService.java           |   2 -
 .../elasticsearch/ElasticSearchIndexer.java     |  80 ++-
 .../solr/internal/SolrEntityIndexerMixin.java   |  14 +-
 .../skeletons/SQLCompatEntityStateWrapper.java  |  13 +
 .../polygene/migration/MigrationService.java    | 609 +++++++------------
 .../polygene/migration/MigrationTest.java       |   4 +-
 .../JavaxJsonSerializationAssembler.java        |   5 +-
 .../javaxjson/CustomJsonAdapterTest.java        |  16 +-
 ...avaxJsonValueCompositeSerializationTest.java |  28 +-
 .../javaxxml/JavaxXmlDeserializer.java          |  44 +-
 .../javaxxml/JavaxXmlSerializer.java            |  31 +-
 .../JavaxXmlSerializationAssembler.java         |   5 +-
 ...JavaxXmlValueCompositeSerializationTest.java |  20 +-
 .../messagepack/MessagePackDeserializer.java    |  57 +-
 .../messagepack/MessagePackSerializer.java      |  35 +-
 .../MessagePackSerializationAssembler.java      |   7 +-
 .../responsereader/JSONResponseReader.java      |  12 +-
 .../responsereader/TableResponseReader.java     |  11 +-
 .../rest/server/api/ContextResource.java        |   6 +-
 .../requestreader/DefaultRequestReader.java     |   7 +-
 .../responsewriter/FormResponseWriter.java      |   9 +-
 .../responsewriter/LinksResponseWriter.java     |  14 +-
 .../responsewriter/ResourceResponseWriter.java  |   9 +-
 .../responsewriter/TableResponseWriter.java     |  39 +-
 .../ValueCompositeResponseWriter.java           |   8 +-
 .../ValueDescriptorResponseWriter.java          |  13 +-
 68 files changed, 1531 insertions(+), 1261 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/composite/StatefulAssociationCompositeDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/StatefulAssociationCompositeDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/StatefulAssociationCompositeDescriptor.java
index c3c5a61..88a1bf1 100644
--- a/core/api/src/main/java/org/apache/polygene/api/composite/StatefulAssociationCompositeDescriptor.java
+++ b/core/api/src/main/java/org/apache/polygene/api/composite/StatefulAssociationCompositeDescriptor.java
@@ -20,12 +20,15 @@
 package org.apache.polygene.api.composite;
 
 import org.apache.polygene.api.association.AssociationStateDescriptor;
+import org.apache.polygene.api.type.StatefulAssociationValueType;
 
 /**
  * Stateful Association Composite Descriptor.
  */
 public interface StatefulAssociationCompositeDescriptor extends StatefulCompositeDescriptor
 {
+    StatefulAssociationValueType<?> valueType();
+
     @Override
     AssociationStateDescriptor state();
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/entity/EntityDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/entity/EntityDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/entity/EntityDescriptor.java
index bac90e1..83f5f5d 100644
--- a/core/api/src/main/java/org/apache/polygene/api/entity/EntityDescriptor.java
+++ b/core/api/src/main/java/org/apache/polygene/api/entity/EntityDescriptor.java
@@ -22,6 +22,7 @@ package org.apache.polygene.api.entity;
 
 import org.apache.polygene.api.composite.CompositeDescriptor;
 import org.apache.polygene.api.composite.StatefulAssociationCompositeDescriptor;
+import org.apache.polygene.api.type.EntityCompositeType;
 
 /**
  * Entity Descriptor.
@@ -29,5 +30,8 @@ import org.apache.polygene.api.composite.StatefulAssociationCompositeDescriptor;
 public interface EntityDescriptor
     extends CompositeDescriptor, StatefulAssociationCompositeDescriptor
 {
+    @Override
+    EntityCompositeType valueType();
+
     boolean queryable();
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/serialization/ConvertedBy.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/serialization/ConvertedBy.java b/core/api/src/main/java/org/apache/polygene/api/serialization/ConvertedBy.java
index 3559314..15c77f6 100644
--- a/core/api/src/main/java/org/apache/polygene/api/serialization/ConvertedBy.java
+++ b/core/api/src/main/java/org/apache/polygene/api/serialization/ConvertedBy.java
@@ -26,12 +26,17 @@ import java.lang.annotation.Target;
 /**
  * Convert this type or Property with the given {@link Converter}.
  *
- * The {@link Converter} must be assembled as an object and will automatically be instantiated by the Polygene runtime.
+ * {@link Converter}s must have a no-args constructor and will be instantiated by the serialization SPI.
  */
 @Retention( RetentionPolicy.RUNTIME )
 @Target( { ElementType.TYPE, ElementType.METHOD } )
 @Documented
 public @interface ConvertedBy
 {
+    /**
+     * The serialization converter type.
+     *
+     * @return the serialization converter type
+     */
     Class<? extends Converter> value();
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/serialization/Converters.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/serialization/Converters.java b/core/api/src/main/java/org/apache/polygene/api/serialization/Converters.java
index 62e76a5..77206c5 100644
--- a/core/api/src/main/java/org/apache/polygene/api/serialization/Converters.java
+++ b/core/api/src/main/java/org/apache/polygene/api/serialization/Converters.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import org.apache.polygene.api.mixin.Mixins;
 import org.apache.polygene.api.type.HasTypes;
 import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.api.util.Annotations;
 
 import static org.apache.polygene.api.type.HasTypesCollectors.closestType;
 
@@ -62,28 +63,59 @@ public interface Converters
     class Mixin implements Converters
     {
         private final Map<ValueType, Converter<?>> converters = new LinkedHashMap<>();
+        private final Map<ValueType, Converter<?>> resolvedConvertersCache = new HashMap<>();
 
         @Override
         public void registerConverter( ValueType valueType, Converter<?> converter )
         {
             converters.put( valueType, converter );
+            resolvedConvertersCache.put( valueType, converter );
         }
 
         @Override
-        @SuppressWarnings( "unchecked" )
         public <T> Converter<T> converterFor( ValueType valueType )
         {
-            Converter<T> converter = castConverter( converters.keySet().stream()
-                                                              .collect( closestType( valueType ) )
-                                                              .map( converters::get )
-                                                              .orElse( null ) );
-            if( converter != null )
+            if( resolvedConvertersCache.containsKey( valueType ) )
+            {
+                return castConverter( resolvedConvertersCache.get( valueType ) );
+            }
+            Converter<T> converter = lookupConverter( valueType );
+            resolvedConvertersCache.put( valueType, converter );
+            return converter;
+        }
+
+        @SuppressWarnings( "unchecked" )
+        private <T> Converter<T> lookupConverter( ValueType valueType )
+        {
+            Converter<T> converter = lookupConvertedByConverter( valueType );
+            if( converter == null )
+            {
+                converter = castConverter( converters.keySet().stream()
+                                                     .collect( closestType( valueType ) )
+                                                     .map( converters::get )
+                                                     .orElse( null ) );
+            }
+            if( converter == null && valueType.primaryType().isEnum() )
             {
-                return converter;
+                converter = new EnumConverter( valueType.primaryType() );
             }
-            if( valueType.primaryType().isEnum() )
+            return converter;
+        }
+
+        private <T> Converter<T> lookupConvertedByConverter( ValueType valueType )
+        {
+            ConvertedBy convertedBy = Annotations.annotationOn( valueType.primaryType(), ConvertedBy.class );
+            if( convertedBy != null )
             {
-                return new EnumConverter( valueType.primaryType() );
+                Class<? extends Converter> converterClass = convertedBy.value();
+                try
+                {
+                    return castConverter( converterClass.newInstance() );
+                }
+                catch( IllegalAccessException | InstantiationException e )
+                {
+                    throw new IllegalArgumentException( "Cannot use class " + converterClass + " as Converter", e );
+                }
             }
             return null;
         }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/serialization/JavaSerializationConverter.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/serialization/JavaSerializationConverter.java b/core/api/src/main/java/org/apache/polygene/api/serialization/JavaSerializationConverter.java
new file mode 100644
index 0000000..792ec99
--- /dev/null
+++ b/core/api/src/main/java/org/apache/polygene/api/serialization/JavaSerializationConverter.java
@@ -0,0 +1,88 @@
+/*
+ *  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.polygene.api.serialization;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Base64;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * A Converter based on Java Serialization.
+ *
+ * Note that this Converter works with String representations.
+ * The serialized payload is encoded using Base64.
+ * This is suitable for text based formats, if you are using a binary format, see the serialization extension.
+ *
+ * Prefer using it with the {@link ConvertedBy} annotation.
+ * If you register it as a serialization converter, it will catch all types.
+ * Or you could extend it and override {@link #type()}.
+ */
+public class JavaSerializationConverter implements Converter<Object>
+{
+    @Override
+    public Class<Object> type()
+    {
+        return Object.class;
+    }
+
+    @Override
+    public String toString( Object object )
+    {
+        byte[] bytes = Base64.getEncoder().encode( serializeJava( object ) );
+        return new String( bytes, UTF_8 );
+    }
+
+    @Override
+    public Object fromString( String string )
+    {
+        byte[] bytes = Base64.getDecoder().decode( string );
+        return deserializeJava( bytes );
+    }
+
+    protected byte[] serializeJava( Object object )
+    {
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        try( ObjectOutputStream out = new ObjectOutputStream( bout ) )
+        {
+            out.writeUnshared( object );
+            out.flush();
+            return bout.toByteArray();
+        }
+        catch( IOException ex )
+        {
+            throw new SerializationException( "Unable to serialize using Java serialization", ex );
+        }
+    }
+
+    protected Object deserializeJava( byte[] bytes )
+    {
+        try( ObjectInputStream oin = new ObjectInputStream( new ByteArrayInputStream( bytes ) ) )
+        {
+            return oin.readObject();
+        }
+        catch( IOException | ClassNotFoundException ex )
+        {
+            throw new SerializationException( "Unable to deserialize using Java serialization", ex );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/serialization/Serializer.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/serialization/Serializer.java b/core/api/src/main/java/org/apache/polygene/api/serialization/Serializer.java
index 39e15b1..0ece5a1 100644
--- a/core/api/src/main/java/org/apache/polygene/api/serialization/Serializer.java
+++ b/core/api/src/main/java/org/apache/polygene/api/serialization/Serializer.java
@@ -82,20 +82,29 @@ public interface Serializer
      * All options provided by the builder are safe to use with all serialization extensions. Serialization extensions
      * might provide more options, see {@link #option(String)} and the respective extension documentation.
      */
-    // TODO rootTypeInfo / nestedTypeInfo
     interface Options
     {
         /**
          * Default state serializer options.
          *
-         * {@link #includeTypeInfo()} set to {@literal true}.
+         * {@link #rootTypeInfo()} set to {@literal false}.
+         * {@link #nestedTypeInfo()} set to {@literal true}.
          */
-        Options DEFAULT = builder().build();
+        Options DEFAULT = builder().withoutRootTypeInfo().withNestedTypeInfo().build();
 
         /**
-         * Default state serializer options with {@link #includeTypeInfo()} set to {@literal false}.
+         * State serializer options with both {@link #rootTypeInfo()} and {@link #nestedTypeInfo()}
+         * set to {@literal false}.
          */
-        Options NO_TYPE_INFO = builder().withoutTypeInfo().build();
+        Options NO_TYPE_INFO = builder().withoutRootTypeInfo().withoutNestedTypeInfo().build();
+
+        /**
+         * State serializer options with both {@link #rootTypeInfo()} and {@link #nestedTypeInfo()}
+         * set to {@literal true}.
+         */
+        Options ALL_TYPE_INFO = builder().withRootTypeInfo().withNestedTypeInfo().build();
+
+        boolean rootTypeInfo();
 
         /**
          * Include type information in the serialized form of nested values.
@@ -126,12 +135,12 @@ public interface Serializer
          * If the deserializer can't know it use the type information from the
          * {@link org.apache.polygene.api.type.ValueType} provided at deserialization time.
          *
-         * Disable it using {@link Builder#withoutTypeInfo()} if you are sure you don't need this.
+         * Disable it using {@link Builder#withoutNestedTypeInfo()} if you are sure you don't need this.
          *
          * @return {@literal true} if type information must be included in the serialized form of nested values,
          *         {@literal false} otherwise
          */
-        boolean includeTypeInfo();
+        boolean nestedTypeInfo();
 
         /**
          * Query for an option's value.
@@ -158,7 +167,8 @@ public interface Serializer
          */
         final class Builder
         {
-            private static final String INCLUDE_TYPE_INFO = "includeTypeInfo";
+            private static final String ROOT_TYPE_INFO = "rootTypeInfo";
+            private static final String NESTED_TYPE_INFO = "nestedTypeInfo";
 
             private static class Instance implements Options
             {
@@ -170,9 +180,15 @@ public interface Serializer
                 }
 
                 @Override
-                public boolean includeTypeInfo()
+                public boolean rootTypeInfo()
+                {
+                    return "true".equals( options.get( ROOT_TYPE_INFO ) );
+                }
+
+                @Override
+                public boolean nestedTypeInfo()
                 {
-                    return "true".equals( options.get( INCLUDE_TYPE_INFO ) );
+                    return "true".equals( options.get( NESTED_TYPE_INFO ) );
                 }
 
                 @Override
@@ -184,17 +200,28 @@ public interface Serializer
 
             private final Map<String, String> options = new HashMap<String, String>()
             {{
-                put( INCLUDE_TYPE_INFO, "true" );
+                put( ROOT_TYPE_INFO, "false" );
+                put( NESTED_TYPE_INFO, "true" );
             }};
 
+            public Builder withRootTypeInfo()
+            {
+                return withOption( ROOT_TYPE_INFO, "true" );
+            }
+
+            public Builder withoutRootTypeInfo()
+            {
+                return withOption( ROOT_TYPE_INFO, "false" );
+            }
+
             /**
              * Include type information in the serialized form of nested values.
              *
              * @return this builder
              */
-            public Builder withTypeInfo()
+            public Builder withNestedTypeInfo()
             {
-                return withOption( INCLUDE_TYPE_INFO, "true" );
+                return withOption( NESTED_TYPE_INFO, "true" );
             }
 
             /**
@@ -203,14 +230,14 @@ public interface Serializer
              * <strong>WARNING</strong>
              * Without this, {@link Deserializer}s will use the provided
              * {@link org.apache.polygene.api.type.ValueType} for instantiation potentially breaking polymorphism,
-             * see {@link Options#includeTypeInfo()}.
+             * see {@link Options#nestedTypeInfo()}.
              *
              * @return this builder
-             * @see Builder#withTypeInfo()
+             * @see Builder#withNestedTypeInfo()
              */
-            public Builder withoutTypeInfo()
+            public Builder withoutNestedTypeInfo()
             {
-                return withOption( INCLUDE_TYPE_INFO, "false" );
+                return withOption( NESTED_TYPE_INFO, "false" );
             }
 
             /**

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/type/EntityCompositeType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/EntityCompositeType.java b/core/api/src/main/java/org/apache/polygene/api/type/EntityCompositeType.java
new file mode 100644
index 0000000..27e5c2d
--- /dev/null
+++ b/core/api/src/main/java/org/apache/polygene/api/type/EntityCompositeType.java
@@ -0,0 +1,63 @@
+/*
+ *  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.polygene.api.type;
+
+import java.lang.reflect.Type;
+import java.util.Objects;
+import org.apache.polygene.api.entity.EntityComposite;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.util.Classes;
+
+/**
+ * EntityComposite ValueType.
+ */
+public class EntityCompositeType extends StatefulAssociationValueType<EntityDescriptor>
+{
+    public static EntityCompositeType of( EntityDescriptor model )
+    {
+        return new EntityCompositeType( model );
+    }
+
+    public static boolean isEntityComposite( Type type )
+    {
+        return EntityComposite.class.isAssignableFrom( Classes.RAW_CLASS.apply( type ) );
+    }
+
+    public EntityCompositeType( EntityDescriptor model )
+    {
+        super( model );
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if( this == o ) { return true; }
+        if( o == null || getClass() != o.getClass() ) { return false; }
+        if( !super.equals( o ) ) { return false; }
+        EntityCompositeType that = (EntityCompositeType) o;
+        return Objects.equals( model, that.model );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash( super.hashCode(), model );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/type/StatefulAssociationValueType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/StatefulAssociationValueType.java b/core/api/src/main/java/org/apache/polygene/api/type/StatefulAssociationValueType.java
new file mode 100644
index 0000000..ae2a2d9
--- /dev/null
+++ b/core/api/src/main/java/org/apache/polygene/api/type/StatefulAssociationValueType.java
@@ -0,0 +1,75 @@
+/*
+ *  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.polygene.api.type;
+
+import java.lang.reflect.Type;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.polygene.api.association.AssociationDescriptor;
+import org.apache.polygene.api.composite.CompositeDescriptor;
+import org.apache.polygene.api.composite.StatefulAssociationCompositeDescriptor;
+import org.apache.polygene.api.entity.EntityComposite;
+import org.apache.polygene.api.property.PropertyDescriptor;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.util.Classes;
+import org.apache.polygene.api.value.ValueComposite;
+
+public abstract class StatefulAssociationValueType<M extends StatefulAssociationCompositeDescriptor & CompositeDescriptor>
+    extends ValueType
+{
+    public static boolean isStatefulAssociationValue( Type type )
+    {
+        Class<?> rawClass = Classes.RAW_CLASS.apply( type );
+        return ValueComposite.class.isAssignableFrom( rawClass ) || EntityComposite.class.isAssignableFrom( rawClass );
+    }
+
+    protected final M model;
+
+    protected StatefulAssociationValueType( M model )
+    {
+        super( model.types().collect( Collectors.toList() ) );
+        this.model = model;
+    }
+
+    public ModuleDescriptor module()
+    {
+        return model.module();
+    }
+
+    public Stream<? extends PropertyDescriptor> properties()
+    {
+        return model.state().properties();
+    }
+
+    public Stream<? extends AssociationDescriptor> associations()
+    {
+        return model.state().associations();
+    }
+
+    public Stream<? extends AssociationDescriptor> manyAssociations()
+    {
+        return model.state().manyAssociations();
+    }
+
+    public Stream<? extends AssociationDescriptor> namedAssociations()
+    {
+        return model.state().namedAssociations();
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java b/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
index bbeaf06..648a4d3 100644
--- a/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
+++ b/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
@@ -21,20 +21,14 @@ package org.apache.polygene.api.type;
 
 import java.lang.reflect.Type;
 import java.util.Objects;
-import java.util.stream.Stream;
-import org.apache.polygene.api.association.AssociationDescriptor;
-import org.apache.polygene.api.property.PropertyDescriptor;
-import org.apache.polygene.api.structure.ModuleDescriptor;
 import org.apache.polygene.api.util.Classes;
 import org.apache.polygene.api.value.ValueComposite;
 import org.apache.polygene.api.value.ValueDescriptor;
 
-import static java.util.stream.Collectors.toList;
-
 /**
  * ValueComposite ValueType.
  */
-public final class ValueCompositeType extends ValueType
+public final class ValueCompositeType extends StatefulAssociationValueType<ValueDescriptor>
 {
     public static ValueCompositeType of( ValueDescriptor model )
     {
@@ -46,41 +40,13 @@ public final class ValueCompositeType extends ValueType
         return ValueComposite.class.isAssignableFrom( Classes.RAW_CLASS.apply( type ) );
     }
 
-    private final ValueDescriptor model;
-
     public ValueCompositeType( ValueDescriptor model )
     {
-        super( model.types().collect( toList() ) );
-        this.model = model;
-    }
-
-    public ModuleDescriptor module()
-    {
-        return model.module();
-    }
-
-    public Stream<? extends PropertyDescriptor> properties()
-    {
-        return model.state().properties();
-    }
-
-    public Stream<? extends AssociationDescriptor> associations()
-    {
-        return model.state().associations();
-    }
-
-    public Stream<? extends AssociationDescriptor> manyAssociations()
-    {
-        return model.state().manyAssociations();
-    }
-
-    public Stream<? extends AssociationDescriptor> namedAssociations()
-    {
-        return model.state().namedAssociations();
+        super( model );
     }
 
     @Override
-    public boolean equals( final Object o )
+    public boolean equals( Object o )
     {
         if( this == o ) { return true; }
         if( o == null || getClass() != o.getClass() ) { return false; }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
index 8407fa9..c96340d 100644
--- a/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
+++ b/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
@@ -30,5 +30,6 @@ import org.apache.polygene.api.type.ValueCompositeType;
 public interface ValueDescriptor
     extends CompositeDescriptor, StatefulAssociationCompositeDescriptor
 {
+    @Override
     ValueCompositeType valueType();
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java
----------------------------------------------------------------------
diff --git a/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java b/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java
index dfce2fd..7f093af 100644
--- a/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java
+++ b/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java
@@ -27,12 +27,15 @@ import java.util.Map;
 import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
 import org.apache.polygene.api.common.UseDefaults;
+import org.apache.polygene.api.identity.StringIdentity;
 import org.apache.polygene.api.property.Property;
+import org.apache.polygene.api.unitofwork.UnitOfWork;
 import org.apache.polygene.api.value.ValueDescriptor;
 import org.apache.polygene.bootstrap.ModuleAssembly;
 import org.apache.polygene.spi.module.ModuleSpi;
 import org.apache.polygene.spi.type.ValueTypeFactory;
 import org.apache.polygene.test.AbstractPolygeneTest;
+import org.apache.polygene.test.EntityTestAssembler;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -47,10 +50,12 @@ public class ValueTypeFactoryTest extends AbstractPolygeneTest
     @Override
     public void assemble( ModuleAssembly module )
     {
-        module.values( SomeValue.class );
+        module.values( SomeState.class );
+        module.entities( SomeState.class );
+        new EntityTestAssembler().assemble( module );
     }
 
-    interface SomeValue
+    interface SomeState
     {
         @UseDefaults
         Property<List<String>> list();
@@ -106,16 +111,29 @@ public class ValueTypeFactoryTest extends AbstractPolygeneTest
     @Test
     public void valueComposites()
     {
-        assertThat( valueTypeFactory.valueTypeOf( module, SomeValue.class ),
+        assertThat( valueTypeFactory.valueTypeOf( module, SomeState.class ),
                     instanceOf( ValueCompositeType.class ) );
-        assertThat( valueTypeFactory.valueTypeOf( module, valueBuilderFactory.newValue( SomeValue.class ) ),
+        assertThat( valueTypeFactory.valueTypeOf( module, valueBuilderFactory.newValue( SomeState.class ) ),
                     instanceOf( ValueCompositeType.class ) );
     }
 
     @Test
+    public void entityComposites()
+    {
+        assertThat( valueTypeFactory.valueTypeOf( module, SomeState.class ),
+                    instanceOf( StatefulAssociationValueType.class ) );
+        try( UnitOfWork uow = unitOfWorkFactory.newUnitOfWork() )
+        {
+            assertThat(
+                valueTypeFactory.valueTypeOf( module, uow.newEntity( SomeState.class, new StringIdentity( "abc" ) ) ),
+                instanceOf( EntityCompositeType.class ) );
+        }
+    }
+
+    @Test
     public void genericsAreResolvedOnValueCompositeProperties()
     {
-        ValueDescriptor descriptor = module.typeLookup().lookupValueModel( SomeValue.class );
+        ValueDescriptor descriptor = module.typeLookup().lookupValueModel( SomeState.class );
         assertThat( descriptor.state().findPropertyModelByName( "list" ).valueType(),
                     equalTo( CollectionType.listOf( ValueType.STRING ) ) );
         assertThat( descriptor.state().findPropertyModelByName( "map" ).valueType(),

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/api/src/test/java/org/apache/polygene/api/value/ValueCompositeTest.java
----------------------------------------------------------------------
diff --git a/core/api/src/test/java/org/apache/polygene/api/value/ValueCompositeTest.java b/core/api/src/test/java/org/apache/polygene/api/value/ValueCompositeTest.java
index ffe500c..f925623 100644
--- a/core/api/src/test/java/org/apache/polygene/api/value/ValueCompositeTest.java
+++ b/core/api/src/test/java/org/apache/polygene/api/value/ValueCompositeTest.java
@@ -39,7 +39,9 @@ import org.apache.polygene.library.constraints.annotation.MaxLength;
 import org.apache.polygene.test.AbstractPolygeneTest;
 import org.apache.polygene.test.EntityTestAssembler;
 
-import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
@@ -227,7 +229,8 @@ public class ValueCompositeTest
             entityBuilder.instance().someValue().set( some );
             SomeEntity entity = entityBuilder.newInstance();
 
-            ValueBuilder<AssociationValue> associationBuilder = valueBuilderFactory.newValueBuilder( AssociationValue.class );
+            ValueBuilder<AssociationValue> associationBuilder = valueBuilderFactory.newValueBuilder(
+                AssociationValue.class );
             associationBuilder.prototype().some().set( entity );
             associationValue = associationBuilder.newInstance();
 
@@ -237,7 +240,8 @@ public class ValueCompositeTest
 
             unitOfWork = unitOfWorkFactory.newUnitOfWork();
 
-            AssociationValue newAssociationValue = valueBuilderFactory.newValueFromSerializedState( AssociationValue.class, json );
+            AssociationValue newAssociationValue = valueBuilderFactory.newValueFromSerializedState(
+                AssociationValue.class, json );
 
             Assert.assertEquals( associationValue.some().get(), newAssociationValue.some().get() );
         }
@@ -259,9 +263,24 @@ public class ValueCompositeTest
         }
     }
 
+    @Test
+    public void givenValueWhenToStringThenNoTypeInfo()
+    {
+        ValueBuilder<AnotherValue> anotherBuilder = valueBuilderFactory.newValueBuilder( AnotherValue.class );
+        anotherBuilder.prototype().val1().set( "foo" );
+        AnotherValue another = anotherBuilder.newInstance();
+        ValueBuilder<SomeValue> builder = valueBuilderFactory.newValueBuilder( SomeValue.class );
+        builder.prototype().another().set( another );
+        SomeValue some = builder.newInstance();
+        String toString = some.toString();
+        System.out.println( toString );
+        assertThat( toString, not( containsString( "_type" ) ) );
+    }
+
     public enum TestEnum
     {
-        somevalue, anothervalue
+        somevalue,
+        anothervalue
     }
 
     public interface SomeValue

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/bootstrap/src/main/java/org/apache/polygene/bootstrap/serialization/DefaultSerializationAssembler.java
----------------------------------------------------------------------
diff --git a/core/bootstrap/src/main/java/org/apache/polygene/bootstrap/serialization/DefaultSerializationAssembler.java b/core/bootstrap/src/main/java/org/apache/polygene/bootstrap/serialization/DefaultSerializationAssembler.java
index 4a6f4b8..db9d0a6 100644
--- a/core/bootstrap/src/main/java/org/apache/polygene/bootstrap/serialization/DefaultSerializationAssembler.java
+++ b/core/bootstrap/src/main/java/org/apache/polygene/bootstrap/serialization/DefaultSerializationAssembler.java
@@ -17,12 +17,15 @@
  */
 package org.apache.polygene.bootstrap.serialization;
 
+import org.apache.polygene.api.serialization.Converters;
 import org.apache.polygene.api.serialization.Deserializer;
 import org.apache.polygene.api.serialization.Serialization;
 import org.apache.polygene.api.serialization.Serializer;
 import org.apache.polygene.bootstrap.Assembler;
 import org.apache.polygene.bootstrap.AssemblyException;
 import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.serialization.javaxjson.JavaxJsonAdapters;
+import org.apache.polygene.serialization.javaxjson.JavaxJsonFactories;
 import org.apache.polygene.serialization.javaxjson.JavaxJsonSerialization;
 import org.apache.polygene.spi.serialization.JsonDeserializer;
 import org.apache.polygene.spi.serialization.JsonSerialization;
@@ -35,8 +38,13 @@ public class DefaultSerializationAssembler
     public void assemble( ModuleAssembly module ) throws AssemblyException
     {
         module.services( JavaxJsonSerialization.class )
-              .withTypes( Serialization.class, Serializer.class, Deserializer.class,
-                          JsonSerialization.class, JsonSerializer.class, JsonDeserializer.class )
+              .withTypes( Serialization.class,
+                          Serializer.class, Deserializer.class,
+                          Converters.class,
+                          JsonSerialization.class,
+                          JsonSerializer.class, JsonDeserializer.class,
+                          JavaxJsonAdapters.class,
+                          JavaxJsonFactories.class )
               .taggedWith( Serialization.Format.JSON );
     }
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java
index 1357568..0b2a2ce 100644
--- a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java
@@ -36,10 +36,10 @@ import org.apache.polygene.spi.entity.NamedAssociationState;
 public class FunctionStateResolver
     implements StateResolver
 {
-    final Function<PropertyDescriptor, Object> propertyFunction;
-    final Function<AssociationDescriptor, EntityReference> associationFunction;
-    final Function<AssociationDescriptor, Stream<EntityReference>> manyAssociationFunction;
-    final Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction;
+    private final Function<PropertyDescriptor, Object> propertyFunction;
+    private final Function<AssociationDescriptor, EntityReference> associationFunction;
+    private final Function<AssociationDescriptor, Stream<EntityReference>> manyAssociationFunction;
+    private final Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction;
 
     public FunctionStateResolver( Function<PropertyDescriptor, Object> propertyFunction,
                                   Function<AssociationDescriptor, EntityReference> associationFunction,
@@ -53,28 +53,27 @@ public class FunctionStateResolver
     }
 
     @Override
-    public Object getPropertyState( PropertyDescriptor propertyDescriptor )
+    public Object getPropertyState( PropertyDescriptor descriptor )
     {
-        return propertyFunction.apply( propertyDescriptor );
+        return propertyFunction.apply( descriptor );
     }
 
     @Override
-    public EntityReference getAssociationState( AssociationDescriptor associationDescriptor )
+    public EntityReference getAssociationState( AssociationDescriptor descriptor )
     {
-        return associationFunction.apply( associationDescriptor );
+        return associationFunction.apply( descriptor );
     }
 
     @Override
-    public Stream<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor )
+    public Stream<EntityReference> getManyAssociationState( AssociationDescriptor descriptor )
     {
-        return manyAssociationFunction.apply( associationDescriptor );
+        return manyAssociationFunction.apply( descriptor );
     }
 
     @Override
-    public Stream<Map.Entry<String, EntityReference>> getNamedAssociationState(
-        AssociationDescriptor associationDescriptor )
+    public Stream<Map.Entry<String, EntityReference>> getNamedAssociationState( AssociationDescriptor descriptor )
     {
-        return namedAssociationFunction.apply( associationDescriptor );
+        return namedAssociationFunction.apply( descriptor );
     }
 
     public void populateState( EntityModel model, EntityState state )
@@ -98,8 +97,7 @@ public class FunctionStateResolver
                 // First clear existing ones
                 associationState.forEach( associationState::remove );
                 // then add the new ones.
-                getManyAssociationState( manyAssDesc )
-                    .forEach( ref -> associationState.add( 0, ref ) );
+                getManyAssociationState( manyAssDesc ).forEach( ref -> associationState.add( 0, ref ) );
             } );
         model.state().namedAssociations().forEach(
             namedAssDesc ->

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java
index 22432ec..b143086 100644
--- a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java
@@ -28,10 +28,10 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.polygene.api.association.AssociationDescriptor;
 import org.apache.polygene.api.association.AssociationStateDescriptor;
-import org.apache.polygene.api.composite.CompositeDescriptor;
 import org.apache.polygene.api.composite.CompositeInstance;
 import org.apache.polygene.api.constraint.ConstraintViolationException;
 import org.apache.polygene.api.entity.EntityComposite;
+import org.apache.polygene.api.entity.EntityDescriptor;
 import org.apache.polygene.api.entity.EntityReference;
 import org.apache.polygene.api.identity.HasIdentity;
 import org.apache.polygene.api.structure.ModuleDescriptor;
@@ -98,7 +98,7 @@ public final class EntityInstance
     }
 
     @Override
-    public CompositeDescriptor descriptor()
+    public EntityDescriptor descriptor()
     {
         return entityModel;
     }
@@ -232,7 +232,7 @@ public final class EntityInstance
     {
         if( Boolean.getBoolean( "polygene.entity.print.state" ) )
         {
-            return state.toString();
+            return state().toString();
         }
         else
         {

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityModel.java
index b468be6..e6f9086 100644
--- a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityModel.java
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityModel.java
@@ -32,6 +32,7 @@ import org.apache.polygene.api.entity.Queryable;
 import org.apache.polygene.api.property.PropertyDescriptor;
 import org.apache.polygene.api.property.StateHolder;
 import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.EntityCompositeType;
 import org.apache.polygene.api.unitofwork.EntityCompositeAlreadyExistsException;
 import org.apache.polygene.api.util.Annotations;
 import org.apache.polygene.runtime.composite.CompositeMethodsModel;
@@ -51,7 +52,7 @@ import static org.apache.polygene.api.identity.HasIdentity.IDENTITY_METHOD;
 public final class EntityModel extends CompositeModel
     implements EntityDescriptor
 {
-
+    private final EntityCompositeType valueType;
     private final boolean queryable;
 
     public EntityModel( ModuleDescriptor module,
@@ -65,6 +66,7 @@ public final class EntityModel extends CompositeModel
     {
         super( module, types, visibility, info, mixinsModel, stateModel, compositeMethodsModel );
 
+        this.valueType = EntityCompositeType.of( this );
         this.queryable = types.stream()
             .flatMap( Annotations.ANNOTATIONS_OF )
             .filter( Annotations.isType( Queryable.class ) )
@@ -74,6 +76,12 @@ public final class EntityModel extends CompositeModel
     }
 
     @Override
+    public EntityCompositeType valueType()
+    {
+        return valueType;
+    }
+
+    @Override
     public boolean queryable()
     {
         return queryable;

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityStateInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityStateInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityStateInstance.java
index 27660cc..3f1a05c 100644
--- a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityStateInstance.java
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityStateInstance.java
@@ -20,12 +20,10 @@
 package org.apache.polygene.runtime.entity;
 
 import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.function.BiFunction;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.polygene.api.association.Association;
 import org.apache.polygene.api.association.AssociationStateHolder;
@@ -223,10 +221,6 @@ public final class EntityStateInstance
     @Override
     public String toString()
     {
-        return "EntityState[" + state.entrySet().stream()
-            .map( entry -> ((Method) entry.getKey()).getName() + "=" + entry.getValue())
-            .collect( Collectors.joining("\n  ", "  ", "\n") )
-            + "]"
-            ;
+        return entityState.toString();
     }
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/runtime/src/main/java/org/apache/polygene/runtime/type/ValueTypeFactoryInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/type/ValueTypeFactoryInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/type/ValueTypeFactoryInstance.java
index 21134e8..b9b8e47 100644
--- a/core/runtime/src/main/java/org/apache/polygene/runtime/type/ValueTypeFactoryInstance.java
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/type/ValueTypeFactoryInstance.java
@@ -24,6 +24,9 @@ import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import org.apache.polygene.api.common.InvalidApplicationException;
+import org.apache.polygene.api.composite.AmbiguousTypeException;
+import org.apache.polygene.api.entity.EntityComposite;
+import org.apache.polygene.api.entity.EntityDescriptor;
 import org.apache.polygene.api.structure.ModuleDescriptor;
 import org.apache.polygene.api.type.ArrayType;
 import org.apache.polygene.api.type.CollectionType;
@@ -34,6 +37,7 @@ import org.apache.polygene.api.type.ValueType;
 import org.apache.polygene.api.util.Classes;
 import org.apache.polygene.api.value.ValueComposite;
 import org.apache.polygene.api.value.ValueDescriptor;
+import org.apache.polygene.runtime.entity.EntityInstance;
 import org.apache.polygene.runtime.value.ValueInstance;
 import org.apache.polygene.spi.type.ValueTypeFactory;
 
@@ -53,6 +57,10 @@ public class ValueTypeFactoryInstance implements ValueTypeFactory
         {
             return ValueInstance.valueInstanceOf( (ValueComposite) object ).descriptor().valueType();
         }
+        if( object instanceof EntityComposite )
+        {
+            return EntityInstance.entityInstanceOf( (EntityComposite) object ).descriptor().valueType();
+        }
         if( object instanceof Enum )
         {
             return EnumType.of( ( (Enum) object ).getDeclaringClass() );
@@ -68,6 +76,11 @@ public class ValueTypeFactoryInstance implements ValueTypeFactory
         {
             return valueDescriptor.valueType();
         }
+        EntityDescriptor entityDescriptor = module.typeLookup().lookupEntityModel( type );
+        if( entityDescriptor != null )
+        {
+            return entityDescriptor.valueType();
+        }
         return newValueType( type, type, type, module );
     }
 

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java
index 04846e9..fe2c838 100644
--- a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java
@@ -21,6 +21,7 @@ package org.apache.polygene.runtime.value;
 
 import java.lang.reflect.Proxy;
 import org.apache.polygene.api.composite.CompositeInstance;
+import org.apache.polygene.api.serialization.Serializer;
 import org.apache.polygene.api.value.ValueComposite;
 import org.apache.polygene.runtime.composite.MixinsInstance;
 import org.apache.polygene.runtime.composite.TransientInstance;
@@ -164,6 +165,7 @@ public final class ValueInstance
     @Override
     public String toString()
     {
-        return ( (ModuleSpi) module().instance() ).serialization().serialize( proxy() );
+        Serializer serialization = ( (ModuleSpi) module().instance() ).serialization();
+        return serialization.serialize( Serializer.Options.NO_TYPE_INFO, proxy() );
     }
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/runtime/src/test/java/org/apache/polygene/runtime/entity/EntityCompositeToStringTest.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/test/java/org/apache/polygene/runtime/entity/EntityCompositeToStringTest.java b/core/runtime/src/test/java/org/apache/polygene/runtime/entity/EntityCompositeToStringTest.java
new file mode 100644
index 0000000..05a8757
--- /dev/null
+++ b/core/runtime/src/test/java/org/apache/polygene/runtime/entity/EntityCompositeToStringTest.java
@@ -0,0 +1,90 @@
+/*
+ *  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.polygene.runtime.entity;
+
+import org.apache.polygene.api.entity.EntityBuilder;
+import org.apache.polygene.api.identity.HasIdentity;
+import org.apache.polygene.api.property.Property;
+import org.apache.polygene.api.unitofwork.UnitOfWork;
+import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.test.AbstractPolygeneTest;
+import org.apache.polygene.test.EntityTestAssembler;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+public class EntityCompositeToStringTest extends AbstractPolygeneTest
+{
+    @Override
+    public void assemble( ModuleAssembly module )
+    {
+        module.entities( Some.class );
+        new EntityTestAssembler().assemble( module );
+    }
+
+    @Test
+    public void givenEntityWhenToStringExpectStringIdentity()
+    {
+        try( UnitOfWork uow = unitOfWorkFactory.newUnitOfWork() )
+        {
+            Some some = createSome( uow );
+            assertThat( some.toString(), equalTo( some.identity().get().toString() ) );
+        }
+    }
+
+    @Test
+    public void givenEntityWhenPrintStateSystemPropertyAndToStringExpectState()
+    {
+        String propertyName = "polygene.entity.print.state";
+        String previous = System.getProperty( propertyName, null );
+        try( UnitOfWork uow = unitOfWorkFactory.newUnitOfWork() )
+        {
+            System.setProperty( propertyName, "true" );
+            Some some = createSome( uow );
+            assertThat( some.toString(), allOf( containsString( "someString" ), containsString( "foo" ) ) );
+        }
+        finally
+        {
+            if( previous != null )
+            {
+                System.setProperty( propertyName, previous );
+            }
+            else
+            {
+                System.clearProperty( propertyName );
+            }
+        }
+    }
+
+    private Some createSome( UnitOfWork uow )
+    {
+        EntityBuilder<Some> builder = uow.newEntityBuilder( Some.class );
+        builder.instance().someString().set( "foo" );
+        return builder.newInstance();
+    }
+
+    interface Some extends HasIdentity
+    {
+        Property<String> someString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/spi/src/main/java/org/apache/polygene/entitystore/memory/MemoryMapEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/entitystore/memory/MemoryMapEntityStoreMixin.java b/core/spi/src/main/java/org/apache/polygene/entitystore/memory/MemoryMapEntityStoreMixin.java
index 98b4f8c..71c9ad1 100644
--- a/core/spi/src/main/java/org/apache/polygene/entitystore/memory/MemoryMapEntityStoreMixin.java
+++ b/core/spi/src/main/java/org/apache/polygene/entitystore/memory/MemoryMapEntityStoreMixin.java
@@ -27,9 +27,10 @@ import java.io.Writer;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.stream.Stream;
-import javax.json.Json;
 import org.apache.polygene.api.entity.EntityDescriptor;
 import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.injection.scope.Service;
+import org.apache.polygene.serialization.javaxjson.JavaxJsonFactories;
 import org.apache.polygene.spi.entitystore.BackupRestore;
 import org.apache.polygene.spi.entitystore.EntityAlreadyExistsException;
 import org.apache.polygene.spi.entitystore.EntityNotFoundException;
@@ -46,6 +47,9 @@ public class MemoryMapEntityStoreMixin
 {
     private final Map<EntityReference, String> store;
 
+    @Service
+    private JavaxJsonFactories jsonFactories;
+
     public MemoryMapEntityStoreMixin()
     {
         store = new HashMap<>();
@@ -96,8 +100,8 @@ public class MemoryMapEntityStoreMixin
         stream.forEach(
             item ->
             {
-                String id = Json.createReader( new StringReader( item ) )
-                                .readObject().getString( JSONKeys.IDENTITY );
+                String id = jsonFactories.readerFactory().createReader( new StringReader( item ) )
+                                         .readObject().getString( JSONKeys.IDENTITY );
                 store.put( EntityReference.parseEntityReference( id ), item );
             } );
     }
@@ -160,5 +164,4 @@ public class MemoryMapEntityStoreMixin
 //            }
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJson.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJson.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJson.java
index 3e7ad6f..eb4961d 100644
--- a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJson.java
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJson.java
@@ -17,13 +17,9 @@
  */
 package org.apache.polygene.serialization.javaxjson;
 
-import java.util.Map;
-import javax.json.Json;
 import javax.json.JsonArray;
-import javax.json.JsonArrayBuilder;
 import javax.json.JsonException;
 import javax.json.JsonObject;
-import javax.json.JsonObjectBuilder;
 import javax.json.JsonString;
 import javax.json.JsonStructure;
 import javax.json.JsonValue;
@@ -33,51 +29,6 @@ import javax.json.JsonValue;
  */
 public class JavaxJson
 {
-    public static JsonValue EMPTY_STRING = toJsonString( "" );
-
-    /**
-     * Create a {@link JsonObjectBuilder} populated with the state of a {@link JsonObject}.
-     *
-     * @param jo the JsonObject
-     * @return the builder
-     */
-    public static JsonObjectBuilder toBuilder( JsonObject jo )
-    {
-        JsonObjectBuilder job = Json.createObjectBuilder();
-        for( Map.Entry<String, JsonValue> entry : jo.entrySet() )
-        {
-            job.add( entry.getKey(), entry.getValue() );
-        }
-        return job;
-    }
-
-    /**
-     * Create a {@link JsonArrayBuilder} populated with the state of a {@link JsonArray}.
-     *
-     * @param ja the JsonArray
-     * @return the builder
-     */
-    public static JsonArrayBuilder toBuilder( JsonArray ja )
-    {
-        JsonArrayBuilder job = Json.createArrayBuilder();
-        for( JsonValue value : ja )
-        {
-            job.add( value );
-        }
-        return job;
-    }
-
-    /**
-     * Create a {@link JsonString} from {@link Object#toString()}.
-     *
-     * @param object the Object
-     * @return the JsonString
-     */
-    public static JsonString toJsonString( Object object )
-    {
-        return Json.createObjectBuilder().add( "value", object.toString() ).build().getJsonString( "value" );
-    }
-
     /**
      * Get a {@link String} out of a {@link JsonValue}.
      *

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapter.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapter.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapter.java
index 9b17f37..c55690b 100644
--- a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapter.java
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapter.java
@@ -38,12 +38,12 @@ public interface JavaxJsonAdapter<T>
     /**
      * Serialize.
      *
-     * @param builderFactory Factory to create JSON
+     * @param jsonFactories Factories to create JSON
      * @param object Object to serialize, never null
      * @param serialize Serialization function for nested structure serialization
      * @return Serialized JSON representation
      */
-    JsonValue serialize( JsonBuilderFactory builderFactory, Object object, Function<Object, JsonValue> serialize );
+    JsonValue serialize( JavaxJsonFactories jsonFactories, Object object, Function<Object, JsonValue> serialize );
 
     /**
      * Deserialize.

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapters.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapters.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapters.java
index fdb3e53..75359f2 100644
--- a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapters.java
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonAdapters.java
@@ -17,11 +17,11 @@
  */
 package org.apache.polygene.serialization.javaxjson;
 
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.function.BiFunction;
 import java.util.function.Function;
-import javax.json.JsonBuilderFactory;
 import javax.json.JsonNumber;
 import javax.json.JsonString;
 import javax.json.JsonValue;
@@ -53,6 +53,7 @@ public interface JavaxJsonAdapters
     class Mixin implements JavaxJsonAdapters, Initializable
     {
         private final Map<ValueType, JavaxJsonAdapter<?>> adapters = new LinkedHashMap<>();
+        private final Map<ValueType, JavaxJsonAdapter<?>> resolvedAdaptersCache = new HashMap<>();
 
         @Uses
         private ServiceDescriptor descriptor;
@@ -78,15 +79,22 @@ public interface JavaxJsonAdapters
         public void registerAdapter( ValueType valueType, JavaxJsonAdapter<?> adapter )
         {
             adapters.put( valueType, adapter );
+            resolvedAdaptersCache.put( valueType, adapter );
         }
 
         @Override
         public <T> JavaxJsonAdapter<T> adapterFor( ValueType valueType )
         {
-            return castAdapter( adapters.keySet().stream()
-                                        .collect( closestType( valueType ) )
-                                        .map( adapters::get )
-                                        .orElse( null ) );
+            if( resolvedAdaptersCache.containsKey( valueType ) )
+            {
+                return castAdapter( resolvedAdaptersCache.get( valueType ) );
+            }
+            JavaxJsonAdapter<T> adapter = castAdapter( adapters.keySet().stream()
+                                                               .collect( closestType( valueType ) )
+                                                               .map( adapters::get )
+                                                               .orElse( null ) );
+            resolvedAdaptersCache.put( valueType, adapter );
+            return adapter;
         }
 
         @SuppressWarnings( "unchecked" )
@@ -112,10 +120,10 @@ public interface JavaxJsonAdapters
         private static abstract class ToJsonStringAdapter<T> implements JavaxJsonAdapter<T>
         {
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory, Object object,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories, Object object,
                                         Function<Object, JsonValue> serialize )
             {
-                return JavaxJson.toJsonString( object );
+                return jsonFactories.toJsonString( object );
             }
         }
 
@@ -150,7 +158,7 @@ public interface JavaxJsonAdapters
             public Class<Boolean> type() { return Boolean.class; }
 
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory, Object object,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories, Object object,
                                         Function<Object, JsonValue> serialize )
             {
                 return type().cast( object ) ? JsonValue.TRUE : JsonValue.FALSE;
@@ -183,11 +191,13 @@ public interface JavaxJsonAdapters
             public Class<Integer> type() { return Integer.class; }
 
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories,
                                         Object object, Function<Object, JsonValue> serialize )
             {
-                return builderFactory.createObjectBuilder().add( "value", type().cast( object ) ).build()
-                                     .getJsonNumber( "value" );
+                return jsonFactories.builderFactory().createObjectBuilder()
+                                    .add( "value", type().cast( object ) )
+                                    .build()
+                                    .getJsonNumber( "value" );
             }
 
             @Override
@@ -214,11 +224,13 @@ public interface JavaxJsonAdapters
             public Class<Long> type() { return Long.class; }
 
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories,
                                         Object object, Function<Object, JsonValue> serialize )
             {
-                return builderFactory.createObjectBuilder().add( "value", type().cast( object ) ).build()
-                                     .getJsonNumber( "value" );
+                return jsonFactories.builderFactory().createObjectBuilder()
+                                    .add( "value", type().cast( object ) )
+                                    .build()
+                                    .getJsonNumber( "value" );
             }
 
             @Override
@@ -245,11 +257,13 @@ public interface JavaxJsonAdapters
             public Class<Short> type() { return Short.class; }
 
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories,
                                         Object object, Function<Object, JsonValue> serialize )
             {
-                return builderFactory.createObjectBuilder().add( "value", type().cast( object ) ).build()
-                                     .getJsonNumber( "value" );
+                return jsonFactories.builderFactory().createObjectBuilder()
+                                    .add( "value", type().cast( object ) )
+                                    .build()
+                                    .getJsonNumber( "value" );
             }
 
             @Override
@@ -276,11 +290,13 @@ public interface JavaxJsonAdapters
             public Class<Byte> type() { return Byte.class; }
 
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories,
                                         Object object, Function<Object, JsonValue> serialize )
             {
-                return builderFactory.createObjectBuilder().add( "value", type().cast( object ) ).build()
-                                     .getJsonNumber( "value" );
+                return jsonFactories.builderFactory().createObjectBuilder()
+                                    .add( "value", type().cast( object ) )
+                                    .build()
+                                    .getJsonNumber( "value" );
             }
 
             @Override
@@ -307,11 +323,13 @@ public interface JavaxJsonAdapters
             public Class<Float> type() { return Float.class; }
 
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories,
                                         Object object, Function<Object, JsonValue> serialize )
             {
-                return builderFactory.createObjectBuilder().add( "value", type().cast( object ) ).build()
-                                     .getJsonNumber( "value" );
+                return jsonFactories.builderFactory().createObjectBuilder()
+                                    .add( "value", type().cast( object ) )
+                                    .build()
+                                    .getJsonNumber( "value" );
             }
 
             @Override
@@ -338,11 +356,13 @@ public interface JavaxJsonAdapters
             public Class<Double> type() { return Double.class; }
 
             @Override
-            public JsonValue serialize( JsonBuilderFactory builderFactory,
+            public JsonValue serialize( JavaxJsonFactories jsonFactories,
                                         Object object, Function<Object, JsonValue> serialize )
             {
-                return builderFactory.createObjectBuilder().add( "value", type().cast( object ) ).build()
-                                     .getJsonNumber( "value" );
+                return jsonFactories.builderFactory().createObjectBuilder()
+                                    .add( "value", type().cast( object ) )
+                                    .build()
+                                    .getJsonNumber( "value" );
             }
 
             @Override

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDeserializer.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDeserializer.java
index be3699e..557490b 100644
--- a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDeserializer.java
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDeserializer.java
@@ -37,11 +37,14 @@ import java.util.stream.Stream;
 import javax.json.JsonArray;
 import javax.json.JsonObject;
 import javax.json.JsonReader;
+import javax.json.JsonString;
 import javax.json.JsonStructure;
 import javax.json.JsonValue;
 import javax.json.stream.JsonParser;
 import javax.json.stream.JsonParsingException;
 import org.apache.polygene.api.association.AssociationDescriptor;
+import org.apache.polygene.api.composite.CompositeDescriptor;
+import org.apache.polygene.api.composite.StatefulAssociationCompositeDescriptor;
 import org.apache.polygene.api.entity.EntityReference;
 import org.apache.polygene.api.injection.scope.This;
 import org.apache.polygene.api.injection.scope.Uses;
@@ -56,11 +59,10 @@ import org.apache.polygene.api.structure.ModuleDescriptor;
 import org.apache.polygene.api.type.ArrayType;
 import org.apache.polygene.api.type.CollectionType;
 import org.apache.polygene.api.type.MapType;
-import org.apache.polygene.api.type.ValueCompositeType;
+import org.apache.polygene.api.type.StatefulAssociationValueType;
 import org.apache.polygene.api.type.ValueType;
 import org.apache.polygene.api.util.Annotations;
 import org.apache.polygene.api.value.ValueBuilder;
-import org.apache.polygene.api.value.ValueDescriptor;
 import org.apache.polygene.spi.serialization.AbstractTextDeserializer;
 import org.apache.polygene.spi.serialization.JsonDeserializer;
 
@@ -92,11 +94,14 @@ public class JavaxJsonDeserializer extends AbstractTextDeserializer
     private ServiceDescriptor descriptor;
 
     private JavaxJsonSettings settings;
+    private JsonString emptyJsonString;
 
     @Override
     public void initialize() throws Exception
     {
         settings = JavaxJsonSettings.orDefault( descriptor.metaInfo( JavaxJsonSettings.class ) );
+        emptyJsonString = jsonFactories.builderFactory().createObjectBuilder().add( "s", "" ).build()
+                                       .getJsonString( "s" );
     }
 
     @Override
@@ -170,7 +175,7 @@ public class JavaxJsonDeserializer extends AbstractTextDeserializer
             return outOfStructureFunction.apply( stateString );
         }
         // Empty state string?
-        return fromJson( module, valueType, JavaxJson.EMPTY_STRING );
+        return fromJson( module, valueType, emptyJsonString );
     }
 
     @Override
@@ -215,9 +220,10 @@ public class JavaxJsonDeserializer extends AbstractTextDeserializer
         {
             return (T) deserializeMap( module, (MapType) valueType, requireJsonStructure( json ) );
         }
-        if( ValueCompositeType.class.isAssignableFrom( valueTypeClass ) )
+        if( StatefulAssociationValueType.class.isAssignableFrom( valueTypeClass ) )
         {
-            return (T) deserializeValueComposite( module, (ValueCompositeType) valueType, requireJsonObject( json ) );
+            return (T) deserializeStatefulAssociationValue( module, (StatefulAssociationValueType<?>) valueType,
+                                                            requireJsonObject( json ) );
         }
         return doGuessDeserialize( module, valueType, json );
     }
@@ -254,23 +260,12 @@ public class JavaxJsonDeserializer extends AbstractTextDeserializer
                 JsonObject object = (JsonObject) json;
                 String typeInfo = object.getString( settings.getTypeInfoPropertyName(),
                                                     valueType.primaryType().getName() );
-                ValueDescriptor valueDescriptor = module.valueDescriptor( typeInfo );
-                if( valueDescriptor != null )
+                StatefulAssociationCompositeDescriptor descriptor = statefulCompositeDescriptorFor( module, typeInfo );
+                if( descriptor != null )
                 {
-                    return (T) deserializeValueComposite( valueDescriptor.module(),
-                                                          valueDescriptor.valueType(),
-                                                          object );
-                }
-            case STRING:
-                byte[] bytes = Base64.getDecoder().decode( asString( json ).getBytes( UTF_8 ) );
-                try
-                {
-                    return (T) deserializeJava( bytes );
-                }
-                catch( SerializationException ex )
-                {
-                    throw new SerializationException( "Don't know how to deserialize " + valueType + " from " + json,
-                                                      ex );
+                    return (T) deserializeStatefulAssociationValue( ( (CompositeDescriptor) descriptor ).module(),
+                                                                    descriptor.valueType(),
+                                                                    object );
                 }
             default:
                 throw new SerializationException( "Don't know how to deserialize " + valueType + " from " + json );
@@ -317,13 +312,15 @@ public class JavaxJsonDeserializer extends AbstractTextDeserializer
         throw new SerializationException( "Don't know how to deserialize " + mapType + " from " + json );
     }
 
-    private Object deserializeValueComposite( ModuleDescriptor module, ValueCompositeType valueType, JsonObject json )
+    private Object deserializeStatefulAssociationValue( ModuleDescriptor module,
+                                                        StatefulAssociationValueType<?> valueType,
+                                                        JsonObject json )
     {
         String typeInfoName = settings.getTypeInfoPropertyName();
         String typeInfo = json.getString( typeInfoName, null );
         if( typeInfo != null )
         {
-            ValueDescriptor descriptor = module.valueDescriptor( typeInfo );
+            StatefulAssociationCompositeDescriptor descriptor = statefulCompositeDescriptorFor( module, typeInfo );
             if( descriptor == null )
             {
                 throw new SerializationException(

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/b2ff271b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonFactories.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonFactories.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonFactories.java
index de5c79b..83eca3c 100644
--- a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonFactories.java
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonFactories.java
@@ -17,9 +17,18 @@
  */
 package org.apache.polygene.serialization.javaxjson;
 
+import java.util.Arrays;
+import java.util.List;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
 import javax.json.JsonBuilderFactory;
 import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
 import javax.json.JsonReaderFactory;
+import javax.json.JsonString;
+import javax.json.JsonValue;
 import javax.json.JsonWriterFactory;
 import javax.json.spi.JsonProvider;
 import javax.json.stream.JsonGeneratorFactory;
@@ -42,6 +51,18 @@ public interface JavaxJsonFactories
 
     JsonWriterFactory writerFactory();
 
+    JsonString toJsonString( Object object );
+
+    JsonObjectBuilder cloneBuilder( JsonObject jsonObject );
+
+    JsonObjectBuilder cloneBuilderInclude( JsonObject jsonObject, String... keys );
+
+    JsonObjectBuilder cloneBuilderExclude( JsonObject jsonObject, String... keys );
+
+    JsonArrayBuilder cloneBuilder( JsonArray jsonArray );
+
+    JsonArrayBuilder cloneBuilderExclude( JsonArray jsonArray, JsonValue... values );
+
     class Mixin implements JavaxJsonFactories, Initializable
     {
         @Uses
@@ -118,5 +139,85 @@ public interface JavaxJsonFactories
         {
             return writerFactory;
         }
+
+        @Override
+        public JsonString toJsonString( Object object )
+        {
+            return builderFactory.createObjectBuilder().add( "value", object.toString() ).build()
+                                 .getJsonString( "value" );
+        }
+
+        @Override
+        public JsonObjectBuilder cloneBuilder( JsonObject jsonObject )
+        {
+            JsonObjectBuilder builder = builderFactory.createObjectBuilder();
+            for( String key : jsonObject.keySet() )
+            {
+                builder.add( key, jsonObject.get( key ) );
+            }
+            return builder;
+        }
+
+        @Override
+        public JsonObjectBuilder cloneBuilderInclude( JsonObject jsonObject, String... keys )
+        {
+            List<String> includes = Arrays.asList( keys );
+            JsonObjectBuilder builder = builderFactory.createObjectBuilder();
+            for( String include : includes )
+            {
+                if( jsonObject.containsKey( include ) )
+                {
+                    builder.add( include, jsonObject.get( include ) );
+                }
+            }
+            return builder;
+        }
+
+        @Override
+        public JsonObjectBuilder cloneBuilderExclude( JsonObject jsonObject, String... keys )
+        {
+            List<String> excludes = Arrays.asList( keys );
+            JsonObjectBuilder builder = builderFactory.createObjectBuilder();
+            for( String key : jsonObject.keySet() )
+            {
+                if( !excludes.contains( key ) )
+                {
+                    builder.add( key, jsonObject.get( key ) );
+                }
+            }
+            return builder;
+        }
+
+        /**
+         * Create a {@link JsonArrayBuilder} populated with the state of a {@link JsonArray}.
+         *
+         * @param jsonArray the JsonArray
+         * @return the builder
+         */
+        @Override
+        public JsonArrayBuilder cloneBuilder( JsonArray jsonArray )
+        {
+            JsonArrayBuilder builder = builderFactory.createArrayBuilder();
+            for( JsonValue entry : jsonArray )
+            {
+                builder.add( entry );
+            }
+            return builder;
+        }
+
+        @Override
+        public JsonArrayBuilder cloneBuilderExclude( JsonArray jsonArray, JsonValue... values )
+        {
+            List<JsonValue> excludes = Arrays.asList( values );
+            JsonArrayBuilder job = builderFactory.createArrayBuilder();
+            for( JsonValue entry : jsonArray )
+            {
+                if( !excludes.contains( entry ) )
+                {
+                    job.add( entry );
+                }
+            }
+            return job;
+        }
     }
 }