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/03/13 15:28:44 UTC

[21/48] polygene-java git commit: New (de)serialization API and SPI & new implementations

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
index dc4db58..ef9816c 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
@@ -14,12 +14,9 @@
  *  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.spi.entitystore.helpers;
 
-import java.io.IOException;
 import java.io.Reader;
 import java.io.Writer;
 import java.time.Instant;
@@ -28,7 +25,15 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.stream.Stream;
+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.JsonValue;
 import org.apache.polygene.api.common.Optional;
 import org.apache.polygene.api.common.QualifiedName;
 import org.apache.polygene.api.entity.EntityDescriptor;
@@ -39,15 +44,11 @@ import org.apache.polygene.api.identity.StringIdentity;
 import org.apache.polygene.api.injection.scope.Service;
 import org.apache.polygene.api.injection.scope.Structure;
 import org.apache.polygene.api.injection.scope.This;
-import org.apache.polygene.api.service.qualifier.Tagged;
 import org.apache.polygene.api.structure.Application;
 import org.apache.polygene.api.structure.ModuleDescriptor;
-import org.apache.polygene.api.type.ValueType;
 import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException;
 import org.apache.polygene.api.usecase.Usecase;
-import org.apache.polygene.api.value.ValueSerialization;
-import org.apache.polygene.api.value.ValueSerializationException;
-import org.apache.polygene.spi.PolygeneSPI;
+import org.apache.polygene.serialization.javaxjson.JavaxJson;
 import org.apache.polygene.spi.entity.EntityState;
 import org.apache.polygene.spi.entity.EntityStatus;
 import org.apache.polygene.spi.entitystore.DefaultEntityStoreUnitOfWork;
@@ -56,11 +57,7 @@ import org.apache.polygene.spi.entitystore.EntityStoreException;
 import org.apache.polygene.spi.entitystore.EntityStoreSPI;
 import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork;
 import org.apache.polygene.spi.entitystore.StateCommitter;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONTokener;
-import org.json.JSONWriter;
+import org.apache.polygene.spi.serialization.JsonSerialization;
 
 /**
  * Implementation of EntityStore that works with an implementation of MapEntityStore.
@@ -80,14 +77,10 @@ public class MapEntityStoreMixin
     private EntityStoreSPI entityStoreSpi;
 
     @Structure
-    private PolygeneSPI spi;
-
-    @Structure
     private Application application;
 
     @Service
-    @Tagged( ValueSerialization.Formats.JSON )
-    private ValueSerialization valueSerialization;
+    private JsonSerialization jsonSerialization;
 
     @Optional
     @Service
@@ -97,58 +90,70 @@ public class MapEntityStoreMixin
     private IdentityGenerator identityGenerator;
 
     @Override
-    public void activateMapEntityStore()
-        throws Exception
-    {
-    }
+    public void activateMapEntityStore() {}
 
     // EntityStore
     @Override
-    public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, Usecase usecaseMetaInfo, Instant currentTime )
+    public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, Usecase usecase, Instant currentTime )
     {
-        return new DefaultEntityStoreUnitOfWork( module, entityStoreSpi, newUnitOfWorkId(), usecaseMetaInfo, currentTime );
+        return new DefaultEntityStoreUnitOfWork( module, entityStoreSpi, newUnitOfWorkId(),
+                                                 usecase, currentTime );
     }
 
     // EntityStoreSPI
     @Override
-    public EntityState newEntityState( EntityStoreUnitOfWork unitOfWork,
-                                       EntityReference reference,
-                                       EntityDescriptor entityDescriptor
-    )
+    public EntityState newEntityState( EntityStoreUnitOfWork uow,
+                                       EntityReference reference, EntityDescriptor entityDescriptor )
     {
-        return new DefaultEntityState( unitOfWork.currentTime(), reference, entityDescriptor );
+        return new DefaultEntityState( uow.currentTime(), reference, entityDescriptor );
     }
 
     @Override
-    public synchronized EntityState entityStateOf( EntityStoreUnitOfWork unitofwork,
-                                                   ModuleDescriptor module,
-                                                   EntityReference reference
-    )
+    public synchronized EntityState entityStateOf( EntityStoreUnitOfWork uow,
+                                                   ModuleDescriptor module, EntityReference reference )
     {
-        Reader in = mapEntityStore.get( reference );
-        return readEntityState( module, in );
+        try
+        {
+            Reader in = mapEntityStore.get( reference );
+            EntityState loadedState = readEntityState( module, in );
+            if( loadedState.status() == EntityStatus.UPDATED )
+            {
+                List<EntityState> migrated = new ArrayList<>( 1 );
+                migrated.add( loadedState );
+                synchMigratedEntities( migrated );
+            }
+            return loadedState;
+        }
+        catch( EntityStoreException ex )
+        {
+            throw ex;
+        }
+        catch( Exception ex )
+        {
+            throw new EntityStoreException( ex );
+        }
     }
 
     @Override
-    public synchronized String versionOf( EntityStoreUnitOfWork unitofwork,
-                                          EntityReference reference
-    )
+    public synchronized String versionOf( EntityStoreUnitOfWork uow, EntityReference reference )
     {
-        Reader in = mapEntityStore.get( reference );
         try
         {
-            JSONObject jsonObject = new JSONObject( new JSONTokener( in ) );
-            return jsonObject.getString( JSONKeys.VERSION );
+            Reader in = mapEntityStore.get( reference );
+            return Json.createReader( in ).readObject().getString( JSONKeys.VERSION );
         }
-        catch( JSONException e )
+        catch( EntityStoreException ex )
         {
-            throw new EntityStoreException( e );
+            throw ex;
+        }
+        catch( Exception ex )
+        {
+            throw new EntityStoreException( ex );
         }
     }
 
     @Override
-    public StateCommitter applyChanges( final EntityStoreUnitOfWork unitofwork, final Iterable<EntityState> state
-    )
+    public StateCommitter applyChanges( EntityStoreUnitOfWork uow, Iterable<EntityState> state )
         throws EntityStoreException
     {
         return new StateCommitter()
@@ -158,34 +163,47 @@ public class MapEntityStoreMixin
             {
                 try
                 {
-                    mapEntityStore.applyChanges( changer -> {
-                        for( EntityState entityState : state )
+                    mapEntityStore.applyChanges(
+                        changer ->
                         {
-                            DefaultEntityState state1 = (DefaultEntityState) entityState;
-                            if( state1.status().equals( EntityStatus.NEW ) )
+                            for( EntityState entityState : state )
                             {
-                                try (Writer writer = changer.newEntity( state1.entityReference(), state1.entityDescriptor() ))
+                                DefaultEntityState state1 = (DefaultEntityState) entityState;
+                                String newVersion = uow.identity().toString();
+                                Instant lastModified = uow.currentTime();
+                                if( state1.status().equals( EntityStatus.NEW ) )
                                 {
-                                    writeEntityState( state1, writer, unitofwork.identity().toString(), unitofwork.currentTime() );
+                                    try( Writer writer = changer.newEntity( state1.entityReference(),
+                                                                            state1.entityDescriptor() ) )
+                                    {
+                                        writeEntityState( state1, writer, newVersion, lastModified );
+                                    }
                                 }
-                            }
-                            else if( state1.status().equals( EntityStatus.UPDATED ) )
-                            {
-                                try (Writer writer = changer.updateEntity( state1.entityReference(), state1.entityDescriptor() ))
+                                else if( state1.status().equals( EntityStatus.UPDATED ) )
                                 {
-                                    writeEntityState( state1, writer, unitofwork.identity().toString(), unitofwork.currentTime() );
+                                    MapEntityStore.MapChange mapChange = new MapEntityStore.MapChange(
+                                        state1.entityReference(), state1.entityDescriptor(),
+                                        state1.version(), newVersion, lastModified
+                                    );
+                                    try( Writer writer = changer.updateEntity( mapChange ) )
+                                    {
+                                        writeEntityState( state1, writer, newVersion, lastModified );
+                                    }
+                                }
+                                else if( state1.status().equals( EntityStatus.REMOVED ) )
+                                {
+                                    changer.removeEntity( state1.entityReference(), state1.entityDescriptor() );
                                 }
                             }
-                            else if( state1.status().equals( EntityStatus.REMOVED ) )
-                            {
-                                changer.removeEntity( state1.entityReference(), state1.entityDescriptor() );
-                            }
-                        }
-                    } );
+                        } );
+                }
+                catch( EntityStoreException ex )
+                {
+                    throw ex;
                 }
-                catch( IOException e )
+                catch( Exception ex )
                 {
-                    throw new EntityStoreException( e );
+                    throw new EntityStoreException( ex );
                 }
             }
 
@@ -197,62 +215,90 @@ public class MapEntityStoreMixin
     }
 
     @Override
-    public Stream<EntityState> entityStates( final ModuleDescriptor module )
+    public Stream<EntityState> entityStates( ModuleDescriptor module )
     {
-        List<EntityState> migrated = new ArrayList<>();
-        return mapEntityStore
-            .entityStates()
-            .map( reader ->
-                  {
-                      EntityState entity = readEntityState( module, reader );
-                      if( entity.status() == EntityStatus.UPDATED )
-                      {
-                          migrated.add( entity );
-                          // Synch back 100 at a time
-                          if( migrated.size() > 100 )
-                          {
-                              try
-                              {
-                                  synchMigratedEntities( migrated );
-                              }
-                              catch( IOException e )
-                              {
-                                  throw new EntityStoreException( "Synchronization of Migrated Entities failed.", e );
-                              }
-                          }
-                      }
-                      return entity;
-                  } )
-            .onClose( () ->
-                      {
-                          // Synch any remaining migrated entities
-                          if( !migrated.isEmpty() )
-                          {
-                              try
-                              {
-                                  synchMigratedEntities( migrated );
-                              }
-                              catch( IOException e )
-                              {
-                                  throw new EntityStoreException( "Synchronization of Migrated Entities failed.", e );
-                              }
-                          }
-                      } );
+        try
+        {
+            Stream<Reader> stateStream = mapEntityStore.entityStates();
+            List<EntityState> migrated = new ArrayList<>();
+            String migrationErrorMsg = "Synchronization of Migrated Entities failed.";
+            Function<Reader, EntityState> function = reader ->
+            {
+                EntityState entity = readEntityState( module, reader );
+                if( entity.status() == EntityStatus.UPDATED )
+                {
+                    migrated.add( entity );
+                    // Sync back 100 at a time
+                    if( migrated.size() > 100 )
+                    {
+                        try
+                        {
+                            synchMigratedEntities( migrated );
+                        }
+                        catch( EntityStoreException ex )
+                        {
+                            throw ex;
+                        }
+                        catch( Exception ex )
+                        {
+                            throw new EntityStoreException( migrationErrorMsg, ex );
+                        }
+                    }
+                }
+                return entity;
+            };
+            Runnable closer = () ->
+            {
+                // Sync any remaining migrated entities
+                if( !migrated.isEmpty() )
+                {
+                    try
+                    {
+                        synchMigratedEntities( migrated );
+                    }
+                    catch( EntityStoreException ex )
+                    {
+                        throw ex;
+                    }
+                    catch( Exception ex )
+                    {
+                        throw new EntityStoreException( migrationErrorMsg, ex );
+                    }
+                }
+            };
+            return stateStream.map( function ).onClose( closer );
+        }
+        catch( EntityStoreException ex )
+        {
+            throw ex;
+        }
+        catch( Exception ex )
+        {
+            throw new EntityStoreException( ex );
+        }
     }
 
     private void synchMigratedEntities( final List<EntityState> migratedEntities )
-        throws IOException
+        throws Exception
     {
-        mapEntityStore.applyChanges( changer -> {
-            for( EntityState migratedEntity : migratedEntities )
+        mapEntityStore.applyChanges(
+            changer ->
             {
-                DefaultEntityState state = (DefaultEntityState) migratedEntity;
-                try (Writer writer = changer.updateEntity( state.entityReference(), state.entityDescriptor() ))
+                for( EntityState migratedEntity : migratedEntities )
                 {
-                    writeEntityState( state, writer, state.version(), state.lastModified() );
+                    DefaultEntityState state = (DefaultEntityState) migratedEntity;
+                    String version = state.version();
+                    Instant lastModified = state.lastModified();
+                    MapEntityStore.MapChange mapChange = new MapEntityStore.MapChange(
+                        state.entityReference(), state.entityDescriptor(),
+                        version, version, lastModified
+                    );
+                    try( Writer writer = changer.updateEntity( mapChange ) )
+                    {
+                        writeEntityState( state, writer, version, lastModified );
+                    }
                 }
-            }
-        } );
+            } );
         migratedEntities.clear();
     }
 
@@ -261,87 +307,63 @@ public class MapEntityStoreMixin
         return identityGenerator.generate( EntityStore.class );
     }
 
-    protected void writeEntityState(DefaultEntityState state, Writer writer, String version, Instant lastModified )
+    protected void writeEntityState( DefaultEntityState state, Writer writer, String version, Instant lastModified )
         throws EntityStoreException
     {
         try
         {
-            JSONWriter json = new JSONWriter( writer );
-            JSONWriter properties = json.object().
-                key( JSONKeys.IDENTITY ).value( state.entityReference().identity() ).
-                key( JSONKeys.APPLICATION_VERSION ).value( application.version() ).
-                key( JSONKeys.TYPE ).value( state.entityDescriptor().types().findFirst().get().getName() ).
-                key( JSONKeys.VERSION ).value( version ).
-                key( JSONKeys.MODIFIED ).value( lastModified.toEpochMilli() ).
-                key( JSONKeys.PROPERTIES ).object();
+            JsonObjectBuilder json = Json.createObjectBuilder();
+            json.add( JSONKeys.IDENTITY, state.entityReference().identity().toString() );
+            json.add( JSONKeys.APPLICATION_VERSION, application.version() );
+            json.add( JSONKeys.TYPE, state.entityDescriptor().primaryType().getName() );
+            json.add( JSONKeys.VERSION, version );
+            json.add( JSONKeys.MODIFIED, lastModified.toEpochMilli() );
+            JsonObjectBuilder properties = Json.createObjectBuilder();
             EntityDescriptor entityType = state.entityDescriptor();
-            entityType.state().properties().forEach( persistentProperty -> {
-                Object value = state.properties().get( persistentProperty.qualifiedName() );
-                try
-                {
-                    json.key( persistentProperty.qualifiedName().name() );
-                    if( value == null || ValueType.isPrimitiveValue( value ) )
-                    {
-                        json.value( value );
-                    }
-                    else
-                    {
-                        String serialized = valueSerialization.serialize( value );
-                        if( serialized.startsWith( "{" ) )
-                        {
-                            json.value( new JSONObject( serialized ) );
-                        }
-                        else if( serialized.startsWith( "[" ) )
-                        {
-                            json.value( new JSONArray( serialized ) );
-                        }
-                        else
-                        {
-                            json.value( serialized );
-                        }
-                    }
-                }
-                catch( JSONException e )
+            entityType.state().properties().forEach(
+                persistentProperty ->
                 {
-                    throw new ValueSerializationException( "Unable to write property " + persistentProperty, e );
-                }
-            } );
-
-            JSONWriter associations = properties.endObject().key( JSONKeys.ASSOCIATIONS ).object();
-            for( Map.Entry<QualifiedName, EntityReference> stateNameEntityReferenceEntry : state.associations()
-                .entrySet() )
+                    Object value = state.properties().get( persistentProperty.qualifiedName() );
+                    JsonValue jsonValue = jsonSerialization.toJson( value );
+                    properties.add( persistentProperty.qualifiedName().name(), jsonValue );
+                } );
+            json.add( JSONKeys.PROPERTIES, properties.build() );
+
+            JsonObjectBuilder associations = Json.createObjectBuilder();
+            for( Map.Entry<QualifiedName, EntityReference> entry : state.associations().entrySet() )
             {
-                EntityReference value = stateNameEntityReferenceEntry.getValue();
-                associations.key( stateNameEntityReferenceEntry.getKey().name() ).
-                    value( value != null ? value.identity() : null );
+                EntityReference value = entry.getValue();
+                associations.add( entry.getKey().name(), value == null ? null : value.identity().toString() );
             }
+            json.add( JSONKeys.ASSOCIATIONS, associations.build() );
 
-            JSONWriter manyAssociations = associations.endObject().key( JSONKeys.MANY_ASSOCIATIONS ).object();
-            for( Map.Entry<QualifiedName, List<EntityReference>> stateNameListEntry : state.manyAssociations()
-                .entrySet() )
+            JsonObjectBuilder manyAssociations = Json.createObjectBuilder();
+            for( Map.Entry<QualifiedName, List<EntityReference>> entry : state.manyAssociations().entrySet() )
             {
-                JSONWriter assocs = manyAssociations.key( stateNameListEntry.getKey().name() ).array();
-                for( EntityReference entityReference : stateNameListEntry.getValue() )
+                JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+                for( EntityReference entityReference : entry.getValue() )
                 {
-                    assocs.value( entityReference.identity() );
+                    arrayBuilder.add( entityReference.identity().toString() );
                 }
-                assocs.endArray();
+                manyAssociations.add( entry.getKey().name(), arrayBuilder.build() );
             }
+            json.add( JSONKeys.MANY_ASSOCIATIONS, manyAssociations.build() );
 
-            JSONWriter namedAssociations = manyAssociations.endObject().key( JSONKeys.NAMED_ASSOCIATIONS ).object();
-            for( Map.Entry<QualifiedName, Map<String, EntityReference>> stateNameMapEntry : state.namedAssociations()
-                .entrySet() )
+            JsonObjectBuilder namedAssociations = Json.createObjectBuilder();
+            for( Map.Entry<QualifiedName, Map<String, EntityReference>> entry : state.namedAssociations().entrySet() )
             {
-                JSONWriter assocs = namedAssociations.key( stateNameMapEntry.getKey().name() ).object();
-                for( Map.Entry<String, EntityReference> namedRef : stateNameMapEntry.getValue().entrySet() )
+                JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+                for( Map.Entry<String, EntityReference> namedRef : entry.getValue().entrySet() )
                 {
-                    assocs.key( namedRef.getKey() ).value( namedRef.getValue().identity() );
+                    objectBuilder.add( namedRef.getKey(), namedRef.getValue().identity().toString() );
                 }
-                assocs.endObject();
+                namedAssociations.add( entry.getKey().name(), objectBuilder.build() );
             }
-            namedAssociations.endObject().endObject();
+            json.add( JSONKeys.NAMED_ASSOCIATIONS, namedAssociations.build() );
+            JsonObject jsonState = json.build();
+            Json.createWriter( writer ).write( jsonState );
         }
-        catch( JSONException e )
+        catch( Exception e )
         {
             throw new EntityStoreException( "Could not store EntityState", e );
         }
@@ -352,31 +374,38 @@ public class MapEntityStoreMixin
     {
         try
         {
-            JSONObject jsonObject = new JSONObject( new JSONTokener( entityState ) );
+            JsonObject parsedState = Json.createReader( entityState ).readObject();
+            JsonObjectBuilder jsonStateBuilder = JavaxJson.toBuilder( parsedState );
             final EntityStatus[] status = { EntityStatus.LOADED };
 
-            String version = jsonObject.getString( JSONKeys.VERSION );
-            Instant modified = Instant.ofEpochMilli(jsonObject.getLong( JSONKeys.MODIFIED ));
-            Identity identity = new StringIdentity(jsonObject.getString( JSONKeys.IDENTITY ));
+            String version = parsedState.getString( JSONKeys.VERSION );
+            Instant modified = Instant.ofEpochMilli( parsedState.getJsonNumber( JSONKeys.MODIFIED ).longValueExact() );
+            Identity identity = new StringIdentity( parsedState.getString( JSONKeys.IDENTITY ) );
 
             // Check if version is correct
-            String currentAppVersion = jsonObject.optString( JSONKeys.APPLICATION_VERSION, "0.0" );
-            if( !currentAppVersion.equals( application.version() ) )
+            JsonObject state;
+            String currentAppVersion = parsedState.getString( JSONKeys.APPLICATION_VERSION, "0.0" );
+            if( currentAppVersion.equals( application.version() ) )
+            {
+                state = jsonStateBuilder.build();
+            }
+            else
             {
                 if( migration != null )
                 {
-                    migration.migrate( jsonObject, application.version(), this );
+                    state = migration.migrate( jsonStateBuilder.build(), application.version(), this );
                 }
                 else
                 {
                     // Do nothing - set version to be correct
-                    jsonObject.put( JSONKeys.APPLICATION_VERSION, application.version() );
+                    jsonStateBuilder.add( JSONKeys.APPLICATION_VERSION, application.version() );
+                    state = jsonStateBuilder.build();
                 }
                 // State changed
                 status[ 0 ] = EntityStatus.UPDATED;
             }
 
-            String type = jsonObject.getString( JSONKeys.TYPE );
+            String type = state.getString( JSONKeys.TYPE );
 
             EntityDescriptor entityDescriptor = module.entityDescriptor( type );
             if( entityDescriptor == null )
@@ -385,107 +414,101 @@ public class MapEntityStoreMixin
             }
 
             Map<QualifiedName, Object> properties = new HashMap<>();
-            JSONObject props = jsonObject.getJSONObject( JSONKeys.PROPERTIES );
-            entityDescriptor.state().properties().forEach( propertyDescriptor -> {
-                Object jsonValue;
-                try
+            JsonObject props = state.getJsonObject( JSONKeys.PROPERTIES );
+            entityDescriptor.state().properties().forEach(
+                property ->
                 {
-                    jsonValue = props.get( propertyDescriptor.qualifiedName().name() );
-                    if( JSONObject.NULL.equals( jsonValue ) )
+                    try
                     {
-                        properties.put( propertyDescriptor.qualifiedName(), null );
+                        JsonValue jsonValue = props.get( property.qualifiedName().name() );
+                        Object value = jsonSerialization.fromJson( module, property.valueType(), jsonValue );
+                        properties.put( property.qualifiedName(), value );
                     }
-                    else
+                    catch( JsonException e )
                     {
-                        Object value = valueSerialization.deserialize( module, propertyDescriptor.valueType(), jsonValue
-                            .toString() );
-                        properties.put( propertyDescriptor.qualifiedName(), value );
+                        // Value not found, default it
+                        Object initialValue = property.resolveInitialValue( module );
+                        properties.put( property.qualifiedName(), initialValue );
+                        status[ 0 ] = EntityStatus.UPDATED;
                     }
-                }
-                catch( JSONException e )
-                {
-                    // Value not found, default it
-                    Object initialValue = propertyDescriptor.resolveInitialValue(module);
-                    properties.put( propertyDescriptor.qualifiedName(), initialValue );
-                    status[ 0 ] = EntityStatus.UPDATED;
-                }
-            } );
+                } );
 
             Map<QualifiedName, EntityReference> associations = new HashMap<>();
-            JSONObject assocs = jsonObject.getJSONObject( JSONKeys.ASSOCIATIONS );
-            entityDescriptor.state().associations().forEach( associationType -> {
-                try
+            JsonObject assocs = state.getJsonObject( JSONKeys.ASSOCIATIONS );
+            entityDescriptor.state().associations().forEach(
+                association ->
                 {
-                    Object jsonValue = assocs.get( associationType.qualifiedName().name() );
-                    EntityReference value = jsonValue == JSONObject.NULL
-                                            ? null
-                                            : EntityReference.parseEntityReference( (String) jsonValue );
-                    associations.put( associationType.qualifiedName(), value );
-                }
-                catch( JSONException e )
-                {
-                    // Association not found, default it to null
-                    associations.put( associationType.qualifiedName(), null );
-                    status[ 0 ] = EntityStatus.UPDATED;
-                }
-            } );
+                    try
+                    {
+                        String jsonValue = assocs.getString( association.qualifiedName().name(), null );
+                        EntityReference value = jsonValue == null
+                                                ? null
+                                                : EntityReference.parseEntityReference( jsonValue );
+                        associations.put( association.qualifiedName(), value );
+                    }
+                    catch( JsonException e )
+                    {
+                        // Association not found, default it to null
+                        associations.put( association.qualifiedName(), null );
+                        status[ 0 ] = EntityStatus.UPDATED;
+                    }
+                } );
 
-            JSONObject manyAssocs = jsonObject.getJSONObject( JSONKeys.MANY_ASSOCIATIONS );
+            JsonObject manyAssocs = state.getJsonObject( JSONKeys.MANY_ASSOCIATIONS );
             Map<QualifiedName, List<EntityReference>> manyAssociations = new HashMap<>();
-            entityDescriptor.state().manyAssociations().forEach( manyAssociationType -> {
-                List<EntityReference> references = new ArrayList<>();
-                try
+            entityDescriptor.state().manyAssociations().forEach(
+                association ->
                 {
-                    JSONArray jsonValues = manyAssocs.getJSONArray( manyAssociationType.qualifiedName().name() );
-                    for( int i = 0; i < jsonValues.length(); i++ )
+                    List<EntityReference> references = new ArrayList<>();
+                    try
                     {
-                        Object jsonValue = jsonValues.getString( i );
-                        EntityReference value = jsonValue == JSONObject.NULL
-                                                ? null
-                                                : EntityReference.parseEntityReference( (String) jsonValue );
-                        references.add( value );
+                        JsonArray jsonValues = manyAssocs.getJsonArray( association.qualifiedName().name() );
+                        for( int i = 0; i < jsonValues.size(); i++ )
+                        {
+                            String jsonValue = jsonValues.getString( i, null );
+                            EntityReference value = jsonValue == null
+                                                    ? null
+                                                    : EntityReference.parseEntityReference( jsonValue );
+                            references.add( value );
+                        }
+                        manyAssociations.put( association.qualifiedName(), references );
                     }
-                    manyAssociations.put( manyAssociationType.qualifiedName(), references );
-                }
-                catch( JSONException e )
-                {
-                    // ManyAssociation not found, default to empty one
-                    manyAssociations.put( manyAssociationType.qualifiedName(), references );
-                }
-            } );
+                    catch( JsonException e )
+                    {
+                        // ManyAssociation not found, default to empty one
+                        manyAssociations.put( association.qualifiedName(), references );
+                    }
+                } );
 
-            JSONObject namedAssocs = jsonObject.getJSONObject( JSONKeys.NAMED_ASSOCIATIONS );
+            JsonObject namedAssocs = state.getJsonObject( JSONKeys.NAMED_ASSOCIATIONS );
             Map<QualifiedName, Map<String, EntityReference>> namedAssociations = new HashMap<>();
-            entityDescriptor.state().namedAssociations().forEach( namedAssociationType -> {
-                Map<String, EntityReference> references = new LinkedHashMap<>();
-                try
+            entityDescriptor.state().namedAssociations().forEach(
+                association ->
                 {
-                    JSONObject jsonValues = namedAssocs.getJSONObject( namedAssociationType.qualifiedName().name() );
-                    JSONArray names = jsonValues.names();
-                    if( names != null )
+                    Map<String, EntityReference> references = new LinkedHashMap<>();
+                    try
                     {
-                        for( int idx = 0; idx < names.length(); idx++ )
+                        JsonObject jsonValues = namedAssocs.getJsonObject( association.qualifiedName().name() );
+                        for( String name : jsonValues.keySet() )
                         {
-                            String name = names.getString( idx );
-                            Object value = jsonValues.get( name );
-                            EntityReference ref = value == JSONObject.NULL
+                            String value = jsonValues.getString( name, null );
+                            EntityReference ref = value == null
                                                   ? null
-                                                  : EntityReference.parseEntityReference( (String) value );
+                                                  : EntityReference.parseEntityReference( value );
                             references.put( name, ref );
                         }
+                        namedAssociations.put( association.qualifiedName(), references );
                     }
-                    namedAssociations.put( namedAssociationType.qualifiedName(), references );
-                }
-                catch( JSONException e )
-                {
-                    // NamedAssociation not found, default to empty one
-                    namedAssociations.put( namedAssociationType.qualifiedName(), references );
-                }
-            } );
+                    catch( JsonException e )
+                    {
+                        // NamedAssociation not found, default to empty one
+                        namedAssociations.put( association.qualifiedName(), references );
+                    }
+                } );
 
             return new DefaultEntityState( version,
                                            modified,
-                                           EntityReference.create(identity),
+                                           EntityReference.create( identity ),
                                            status[ 0 ],
                                            entityDescriptor,
                                            properties,
@@ -494,25 +517,26 @@ public class MapEntityStoreMixin
                                            namedAssociations
             );
         }
-        catch( JSONException e )
+        catch( Exception e )
         {
             throw new EntityStoreException( e );
         }
     }
 
     @Override
-    public JSONObject jsonStateOf( String id )
-        throws IOException
+    public JsonObject jsonStateOf( String id )
     {
-        JSONObject jsonObject;
-        try (Reader reader = mapEntityStore.get( EntityReference.parseEntityReference( id ) ))
+        try( Reader reader = mapEntityStore.get( EntityReference.parseEntityReference( id ) ) )
+        {
+            return Json.createReader( reader ).readObject();
+        }
+        catch( EntityStoreException ex )
         {
-            jsonObject = new JSONObject( new JSONTokener( reader ) );
+            throw ex;
         }
-        catch( JSONException e )
+        catch( Exception ex )
         {
-            throw new IOException( e );
+            throw new EntityStoreException( ex );
         }
-        return jsonObject;
     }
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
index 4b5e8e6..6a8ebfb 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
@@ -20,14 +20,14 @@
 
 package org.apache.polygene.spi.entitystore.helpers;
 
-import org.json.JSONException;
-import org.json.JSONObject;
+import javax.json.JsonException;
+import javax.json.JsonObject;
 
 /**
  * State Migration SPI.
  */
 public interface Migration
 {
-    boolean migrate( JSONObject state, String toVersion, StateStore stateStore )
-        throws JSONException;
+    JsonObject migrate( JsonObject state, String toVersion, StateStore stateStore )
+        throws JsonException;
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
index 1f1f728..8aa9e6e 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
@@ -20,14 +20,13 @@
 
 package org.apache.polygene.spi.entitystore.helpers;
 
-import java.io.IOException;
-import org.json.JSONObject;
+import javax.json.JsonObject;
+import org.apache.polygene.spi.entitystore.EntityStoreException;
 
 /**
  * StateStore SPI.
  */
 public interface StateStore
 {
-    JSONObject jsonStateOf( String id )
-        throws IOException;
+    JsonObject jsonStateOf( String id ) throws EntityStoreException;
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java b/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
index 8aa7c0d..eccc612 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
@@ -21,10 +21,10 @@ package org.apache.polygene.spi.module;
 
 import org.apache.polygene.api.identity.IdentityGenerator;
 import org.apache.polygene.api.metrics.MetricsProvider;
+import org.apache.polygene.api.serialization.Serialization;
 import org.apache.polygene.api.structure.Module;
-import org.apache.polygene.api.structure.TypeLookup;
-import org.apache.polygene.api.value.ValueSerialization;
 import org.apache.polygene.spi.entitystore.EntityStore;
+import org.apache.polygene.spi.type.ValueTypeFactory;
 
 public interface ModuleSpi extends Module
 {
@@ -32,9 +32,9 @@ public interface ModuleSpi extends Module
 
     IdentityGenerator identityGenerator();
 
-    ValueSerialization valueSerialization();
-
-    TypeLookup typeLookup();
+    Serialization serialization();
 
     MetricsProvider metricsProvider();
+
+    ValueTypeFactory valueTypeFactory();
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java
new file mode 100644
index 0000000..a8f2c2c
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java
@@ -0,0 +1,48 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+import java.util.Base64;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+
+import static java.util.stream.Collectors.joining;
+
+public abstract class AbstractBinaryDeserializer extends AbstractDeserializer
+{
+    @Override
+    public <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state )
+    {
+        String stateString;
+        try( BufferedReader buffer = new BufferedReader( state ) )
+        {
+            stateString = buffer.lines().collect( joining( "\n" ) );
+        }
+        catch( IOException ex )
+        {
+            throw new UncheckedIOException( ex );
+        }
+        byte[] decoded = Base64.getDecoder().decode( stateString );
+        return deserialize( module, valueType, new ByteArrayInputStream( decoded ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java
new file mode 100644
index 0000000..e673ad7
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java
@@ -0,0 +1,51 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.Base64;
+import org.apache.polygene.api.common.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Base Binary Serializer.
+ *
+ * Implementations work on bytes, this base serializer encode these bytes in Base64 to produce Strings.
+ */
+public abstract class AbstractBinarySerializer extends AbstractSerializer
+{
+    @Override
+    public void serialize( Options options, Writer writer, @Optional Object object )
+    {
+        try
+        {
+            ByteArrayOutputStream output = new ByteArrayOutputStream();
+            serialize( options, output, object );
+            byte[] base64 = Base64.getEncoder().encode( output.toByteArray() );
+            writer.write( new String( base64, UTF_8 ) );
+        }
+        catch( IOException ex )
+        {
+            throw new UncheckedIOException( ex );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java
new file mode 100644
index 0000000..17982f3
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java
@@ -0,0 +1,155 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.serialization.Deserializer;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.CollectionType;
+import org.apache.polygene.api.type.MapType;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.spi.module.ModuleSpi;
+
+public abstract class AbstractDeserializer implements Deserializer
+{
+    protected static final ValueType ENTITY_REF_LIST_VALUE_TYPE = CollectionType.listOf( EntityReference.class );
+    protected static final ValueType ENTITY_REF_MAP_VALUE_TYPE = MapType.of( String.class, EntityReference.class );
+
+    @Override
+    public <T> T deserialize( ModuleDescriptor module, ValueType valueType, String state )
+    {
+        return deserialize( module, valueType, new StringReader( state ) );
+    }
+
+    @Override
+    public <T> Function<String, T> deserializeFunction( ModuleDescriptor module, ValueType valueType )
+    {
+        return state -> deserialize( module, valueType, new StringReader( state ) );
+    }
+
+    @Override
+    public <T> Stream<T> deserializeEach( ModuleDescriptor module, ValueType valueType, Iterable<String> states )
+    {
+        return StreamSupport.stream( states.spliterator(), false )
+                            .map( state -> deserialize( module, valueType, new StringReader( state ) ) );
+    }
+
+    @Override
+    public <T> Stream<T> deserializeEach( ModuleDescriptor module, ValueType valueType, String... states )
+    {
+        return Stream.of( states ).map( state -> deserialize( module, valueType, new StringReader( state ) ) );
+    }
+
+    @Override
+    public <T> T fromBytes( ModuleDescriptor module, ValueType valueType, byte[] bytes )
+    {
+        return deserialize( module, valueType, new ByteArrayInputStream( bytes ) );
+    }
+
+    @Override
+    public <T> Function<byte[], T> fromBytesFunction( ModuleDescriptor module, ValueType valueType )
+    {
+        return bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) );
+    }
+
+    @Override
+    public <T> Stream<T> fromBytesEach( ModuleDescriptor module, ValueType valueType, Iterable<byte[]> states )
+    {
+        return StreamSupport.stream( states.spliterator(), false )
+                            .map( bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) ) );
+    }
+
+    @Override
+    public <T> Stream<T> fromBytesEach( ModuleDescriptor module, ValueType valueType, byte[]... states )
+    {
+        return Stream.of( states ).map( bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) ) );
+    }
+
+    @Override
+    public <T> T deserialize( ModuleDescriptor module, Class<T> type, InputStream state )
+    {
+        return deserialize( module, valueTypeOf( module, type ), state );
+    }
+
+    @Override
+    public <T> T deserialize( ModuleDescriptor module, Class<T> type, Reader state )
+    {
+        return deserialize( module, valueTypeOf( module, type ), state );
+    }
+
+    @Override
+    public <T> T deserialize( ModuleDescriptor module, Class<T> type, String state )
+    {
+        return deserialize( module, valueTypeOf( module, type ), state );
+    }
+
+    @Override
+    public <T> Function<String, T> deserializeFunction( ModuleDescriptor module, Class<T> type )
+    {
+        return deserializeFunction( module, valueTypeOf( module, type ) );
+    }
+
+    @Override
+    public <T> Stream<T> deserializeEach( ModuleDescriptor module, Class<T> type, Iterable<String> states )
+    {
+        return deserializeEach( module, valueTypeOf( module, type ), states );
+    }
+
+    @Override
+    public <T> Stream<T> deserializeEach( ModuleDescriptor module, Class<T> type, String... states )
+    {
+        return deserializeEach( module, valueTypeOf( module, type ), states );
+    }
+
+    @Override
+    public <T> T fromBytes( ModuleDescriptor module, Class<T> type, byte[] bytes )
+    {
+        return fromBytes( module, valueTypeOf( module, type ), bytes );
+    }
+
+    @Override
+    public <T> Function<byte[], T> fromBytesFunction( ModuleDescriptor module, Class<T> type )
+    {
+        return fromBytesFunction( module, valueTypeOf( module, type ) );
+    }
+
+    @Override
+    public <T> Stream<T> fromBytesEach( ModuleDescriptor module, Class<T> type, Iterable<byte[]> states )
+    {
+        return fromBytesEach( module, valueTypeOf( module, type ), states );
+    }
+
+    @Override
+    public <T> Stream<T> fromBytesEach( ModuleDescriptor module, Class<T> type, byte[]... states )
+    {
+        return fromBytesEach( module, valueTypeOf( module, type ), states );
+    }
+
+    private ValueType valueTypeOf( ModuleDescriptor module, Class<?> type )
+    {
+        // TODO Remove (ModuleSpi) cast
+        return ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java
new file mode 100644
index 0000000..b5f10ff
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java
@@ -0,0 +1,147 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.apache.polygene.api.common.Optional;
+import org.apache.polygene.api.serialization.Serializer;
+
+public abstract class AbstractSerializer implements Serializer
+{
+    @Override
+    public void serialize( Writer writer, @Optional Object object )
+    {
+        serialize( Options.DEFAULT, writer, object );
+    }
+
+    @Override
+    public void serialize( OutputStream output, @Optional Object object )
+    {
+        serialize( Options.DEFAULT, output, object );
+    }
+
+    @Override
+    public String serialize( Options options, @Optional Object object )
+    {
+        StringWriter writer = new StringWriter();
+        serialize( options, writer, object );
+        return writer.toString();
+    }
+
+    @Override
+    public String serialize( @Optional Object object )
+    {
+        return serialize( Options.DEFAULT, object );
+    }
+
+    @Override
+    public <T> Function<T, String> serializeFunction( Options options )
+    {
+        return object -> serialize( options, object );
+    }
+
+    @Override
+    public <T> Function<T, String> serializeFunction()
+    {
+        return object -> serialize( Options.DEFAULT, object );
+    }
+
+    @Override
+    public Stream<String> serializeEach( Options options, Iterable<Object> objects )
+    {
+        return StreamSupport.stream( objects.spliterator(), false )
+                            .map( object -> serialize( options, object ) );
+    }
+
+    @Override
+    public Stream<String> serializeEach( Iterable<Object> objects )
+    {
+        return StreamSupport.stream( objects.spliterator(), false )
+                            .map( object -> serialize( Options.DEFAULT, object ) );
+    }
+
+    @Override
+    public Stream<String> serializeEach( Options options, Object... objects )
+    {
+        return Stream.of( objects ).map( object -> serialize( options, object ) );
+    }
+
+    @Override
+    public Stream<String> serializeEach( Object... objects )
+    {
+        return Stream.of( objects ).map( object -> serialize( Options.DEFAULT, object ) );
+    }
+
+    @Override
+    public byte[] toBytes( Options options, @Optional Object object )
+    {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        serialize( options, output, object );
+        return output.toByteArray();
+    }
+
+    @Override
+    public byte[] toBytes( @Optional Object object )
+    {
+        return toBytes( Options.DEFAULT, object );
+    }
+
+    @Override
+    public <T> Function<T, byte[]> toBytesFunction( Options options )
+    {
+        return object -> toBytes( options, object );
+    }
+
+    @Override
+    public <T> Function<T, byte[]> toBytesFunction()
+    {
+        return object -> toBytes( Options.DEFAULT, object );
+    }
+
+    @Override
+    public Stream<byte[]> toBytesEach( Options options, Iterable<Object> objects )
+    {
+        return StreamSupport.stream( objects.spliterator(), false )
+                            .map( object -> toBytes( options, object ) );
+    }
+
+    @Override
+    public Stream<byte[]> toBytesEach( Iterable<Object> objects )
+    {
+        return StreamSupport.stream( objects.spliterator(), false )
+                            .map( object -> toBytes( Options.DEFAULT, object ) );
+    }
+
+    @Override
+    public Stream<byte[]> toBytesEach( Options options, Object... objects )
+    {
+        return Stream.of( objects ).map( object -> toBytes( options, object ) );
+    }
+
+    @Override
+    public Stream<byte[]> toBytesEach( Object... objects )
+    {
+        return Stream.of( objects ).map( object -> toBytes( Options.DEFAULT, object ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java
new file mode 100644
index 0000000..0575489
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java
@@ -0,0 +1,34 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public abstract class AbstractTextDeserializer extends AbstractDeserializer
+{
+    @Override
+    public <T> T deserialize( ModuleDescriptor module, ValueType valueType, InputStream state )
+    {
+        return deserialize( module, valueType, new InputStreamReader( state, UTF_8 ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java
new file mode 100644
index 0000000..3d8bb16
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java
@@ -0,0 +1,48 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import org.apache.polygene.api.common.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Base Text Serializer.
+ *
+ * Implementations work on Strings, this base serializer encode these strings in UTF-8 to produce bytes.
+ */
+public abstract class AbstractTextSerializer extends AbstractSerializer
+{
+    public void serialize( Options options, OutputStream output, @Optional Object object )
+    {
+        try
+        {
+            StringWriter writer = new StringWriter();
+            serialize( options, writer, object );
+            output.write( writer.toString().getBytes( UTF_8 ) );
+        }
+        catch( IOException ex )
+        {
+            throw new UncheckedIOException( ex );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java
new file mode 100644
index 0000000..84c8993
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java
@@ -0,0 +1,164 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UncheckedIOException;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+import org.apache.polygene.api.serialization.Deserializer;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.spi.module.ModuleSpi;
+
+import static java.util.stream.Collectors.joining;
+
+public interface JsonDeserializer extends Deserializer
+{
+    <T> T fromJson( ModuleDescriptor module, ValueType valueType, JsonValue state );
+
+    default <T> Function<JsonValue, T> fromJsonFunction( ModuleDescriptor module, ValueType valueType )
+    {
+        return state -> fromJson( module, valueType, state );
+    }
+
+    default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, Stream<JsonValue> states )
+    {
+        return states.map( fromJsonFunction( module, valueType ) );
+    }
+
+    default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, Iterable<JsonValue> states )
+    {
+        return fromJsonEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+    }
+
+    default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, JsonValue... states )
+    {
+        return fromJsonEach( module, valueType, Stream.of( states ) );
+    }
+
+    default <T> T fromJson( ModuleDescriptor module, Class<T> type, JsonValue state )
+    {
+        // TODO Remove (ModuleSpi) cast
+        ValueType valueType = ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type );
+        return fromJson( module, valueType, state );
+    }
+
+    default <T> Function<JsonValue, T> fromJson( ModuleDescriptor module, Class<T> type )
+    {
+        return state -> fromJson( module, type, state );
+    }
+
+    default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, Stream<JsonValue> states )
+    {
+        return states.map( fromJson( module, valueType ) );
+    }
+
+    default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, Iterable<JsonValue> states )
+    {
+        return fromJsonEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+    }
+
+    default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, JsonValue... states )
+    {
+        return fromJsonEach( module, valueType, Stream.of( states ) );
+    }
+
+    @Override
+    default <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state )
+    {
+        // JSR-353 Does not allow reading "out of structure" values
+        // See https://www.jcp.org/en/jsr/detail?id=353
+        // And commented JsonReader#readValue() method in the javax.json API
+        // BUT, it will be part of the JsonReader contract in the next version
+        // See https://www.jcp.org/en/jsr/detail?id=374
+        // Implementation by provider is optional though, so we'll always need a default implementation here.
+        // Fortunately, JsonParser has new methods allowing to read structures while parsing so it will be easy to do.
+        // In the meantime, a poor man's implementation reading the json into memory will do.
+        // TODO Revisit values out of structure JSON deserialization when JSR-374 is out
+        String stateString;
+        try( BufferedReader buffer = new BufferedReader( state ) )
+        {
+            stateString = buffer.lines().collect( joining( "\n" ) );
+        }
+        catch( IOException ex )
+        {
+            throw new UncheckedIOException( ex );
+        }
+        // We want plain Strings, BigDecimals, BigIntegers to be deserialized even when unquoted
+        Function<String, T> plainValueFunction = string ->
+        {
+            String poorMans = "{\"value\":" + string + "}";
+            JsonObject poorMansJson = Json.createReader( new StringReader( poorMans ) ).readObject();
+            JsonValue value = poorMansJson.get( "value" );
+            return fromJson( module, valueType, value );
+        };
+        Function<String, T> outOfStructureFunction = string ->
+        {
+            // Is this an unquoted plain value?
+            try
+            {
+                return plainValueFunction.apply( '"' + string + '"' );
+            }
+            catch( JsonParsingException ex )
+            {
+                return plainValueFunction.apply( string );
+            }
+        };
+        try( JsonParser parser = Json.createParser( new StringReader( stateString ) ) )
+        {
+            if( parser.hasNext() )
+            {
+                JsonParser.Event e = parser.next();
+                switch( e )
+                {
+                    case VALUE_NULL:
+                        return null;
+                    case START_ARRAY:
+                    case START_OBJECT:
+                        // JSON Structure
+                        try( JsonReader reader = Json.createReader( new StringReader( stateString ) ) )
+                        {
+                            return fromJson( module, valueType, reader.read() );
+                        }
+                    default:
+                        // JSON Value out of structure
+                        return outOfStructureFunction.apply( stateString );
+                }
+            }
+        }
+        catch( JsonParsingException ex )
+        {
+            return outOfStructureFunction.apply( stateString );
+        }
+        // Empty state string?
+        JsonValue emptyJsonString = Json.createReader( new StringReader( "{\"empty\":\"\"}" ) )
+                                        .readObject().get( "empty" );
+        return fromJson( module, valueType, emptyJsonString );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java
new file mode 100644
index 0000000..a98e70f
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java
@@ -0,0 +1,24 @@
+/*
+ *  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.spi.serialization;
+
+import org.apache.polygene.api.serialization.Serialization;
+
+public interface JsonSerialization extends Serialization, JsonSerializer, JsonDeserializer
+{
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java
new file mode 100644
index 0000000..b64f240
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java
@@ -0,0 +1,106 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import org.apache.polygene.api.common.Optional;
+import org.apache.polygene.api.serialization.Serializer;
+
+public interface JsonSerializer extends Serializer
+{
+    <T> Function<T, JsonValue> toJsonFunction( Options options );
+
+    default <T> Function<T, JsonValue> toJsonFunction()
+    {
+        return object -> toJsonFunction( Options.DEFAULT ).apply( object );
+    }
+
+    default JsonValue toJson( Options options, @Optional Object object )
+    {
+        return toJsonFunction( options ).apply( object );
+    }
+
+    default JsonValue toJson( @Optional Object object )
+    {
+        return toJsonFunction( Options.DEFAULT ).apply( object );
+    }
+
+    default <T> Stream<JsonValue> toJsonEach( Options options, Stream<T> objects )
+    {
+        return objects.map( toJsonFunction( options ) );
+    }
+
+    default <T> Stream<JsonValue> toJsonEach( Options options, Iterable<T> objects )
+    {
+        return toJsonEach( options, StreamSupport.stream( objects.spliterator(), false ) );
+    }
+
+    default <T> Stream<JsonValue> toJsonEach( Options options, Object... objects )
+    {
+        return toJsonEach( options, Stream.of( objects ) );
+    }
+
+    default <T> Stream<JsonValue> toJsonEach( Stream<T> objects )
+    {
+        return objects.map( toJsonFunction( Options.DEFAULT ) );
+    }
+
+    default <T> Stream<JsonValue> toJsonEach( Iterable<T> objects )
+    {
+        return toJsonEach( Options.DEFAULT, StreamSupport.stream( objects.spliterator(), false ) );
+    }
+
+    default <T> Stream<JsonValue> toJsonEach( Object... objects )
+    {
+        return toJsonEach( Options.DEFAULT, Stream.of( objects ) );
+    }
+
+    default void serialize( Options options, Writer writer, @Optional Object object )
+    {
+        JsonValue jsonValue = toJson( options, object );
+        if( jsonValue == null )
+        {
+            return;
+        }
+        try
+        {
+            // TODO FIX ThIS SHIT of "out of structure" value (de)serialization
+            // We want plain Strings to be serialized without quotes which is non JSON compliant
+            // See https://java.net/jira/browse/JSON_PROCESSING_SPEC-65
+            if( jsonValue.getValueType() == JsonValue.ValueType.STRING )
+            {
+                writer.write( ( (JsonString) jsonValue ).getString() );
+            }
+            else
+            {
+                writer.write( jsonValue.toString() );
+            }
+        }
+        catch( IOException ex )
+        {
+            throw new UncheckedIOException( ex );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java
new file mode 100644
index 0000000..c7ac42b
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java
@@ -0,0 +1,107 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.polygene.api.serialization.Deserializer;
+import org.apache.polygene.api.serialization.SerializationException;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.CollectionType;
+import org.apache.polygene.api.type.EnumType;
+import org.apache.polygene.api.type.MapType;
+import org.apache.polygene.api.type.ValueCompositeType;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.api.value.ValueDescriptor;
+import org.apache.polygene.spi.module.ModuleSpi;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public interface XmlDeserializer extends Deserializer
+{
+    <T> T fromXml( ModuleDescriptor module, ValueType valueType, Document state );
+
+    default <T> Function<Document, T> fromXmlFunction( ModuleDescriptor module, ValueType valueType )
+    {
+        return state -> fromXml( module, valueType, state );
+    }
+
+    default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Stream<Document> states )
+    {
+        return states.map( fromXmlFunction( module, valueType ) );
+    }
+
+    default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Iterable<Document> states )
+    {
+        return fromXmlEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+    }
+
+    default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Document... states )
+    {
+        return fromXmlEach( module, valueType, Stream.of( states ) );
+    }
+
+    default <T> T fromXml( ModuleDescriptor module, Class<T> type, Document state )
+    {
+        // TODO Remove (ModuleSpi) cast
+        ValueType valueType = ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type );
+        return fromXml( module, valueType, state );
+    }
+
+    default <T> Function<Document, T> fromXml( ModuleDescriptor module, Class<T> type )
+    {
+        return state -> fromXml( module, type, state );
+    }
+
+    default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Stream<Document> states )
+    {
+        return states.map( fromXml( module, valueType ) );
+    }
+
+    default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Iterable<Document> states )
+    {
+        return fromXmlEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+    }
+
+    default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Document... states )
+    {
+        return fromXmlEach( module, valueType, Stream.of( states ) );
+    }
+
+    @Override
+    default <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state )
+    {
+        try
+        {
+            DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+            Document doc = docBuilder.parse( new InputSource( state ) );
+            return fromXml( module, valueType, doc );
+        }
+        catch( SAXException | IOException | ParserConfigurationException ex )
+        {
+            throw new SerializationException( "Unable to read XML document", ex );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java
new file mode 100644
index 0000000..12fda54
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java
@@ -0,0 +1,24 @@
+/*
+ *  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.spi.serialization;
+
+import org.apache.polygene.api.serialization.Serialization;
+
+public interface XmlSerialization extends Serialization, XmlSerializer, XmlDeserializer
+{
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java
new file mode 100644
index 0000000..32ce539
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java
@@ -0,0 +1,123 @@
+/*
+ *  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.spi.serialization;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.polygene.api.common.Optional;
+import org.apache.polygene.api.serialization.SerializationException;
+import org.apache.polygene.api.serialization.Serializer;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * XML State Serializer.
+ */
+public interface XmlSerializer extends Serializer
+{
+    <T> Function<T, Document> toXmlFunction( Options options );
+
+    default <T> Function<T, Document> toXmlFunction()
+    {
+        return object -> toXmlFunction( Options.DEFAULT ).apply( object );
+    }
+
+    default Document toXml( Options options, @Optional Object object )
+    {
+        return toXmlFunction( options ).apply( object );
+    }
+
+    default Document toXml( @Optional Object object )
+    {
+        return toXmlFunction( Options.DEFAULT ).apply( object );
+    }
+
+    default <T> Stream<Document> toXmlEach( Options options, Stream<T> objects )
+    {
+        return objects.map( toXmlFunction( options ) );
+    }
+
+    default <T> Stream<Document> toXmlEach( Options options, Iterable<T> objects )
+    {
+        return toXmlEach( options, StreamSupport.stream( objects.spliterator(), false ) );
+    }
+
+    default <T> Stream<Document> toXmlEach( Options options, Object... objects )
+    {
+        return toXmlEach( options, Stream.of( objects ) );
+    }
+
+    default <T> Stream<Document> toXmlEach( Stream<T> objects )
+    {
+        return objects.map( toXmlFunction( Options.DEFAULT ) );
+    }
+
+    default <T> Stream<Document> toXmlEach( Iterable<T> objects )
+    {
+        return toXmlEach( Options.DEFAULT, StreamSupport.stream( objects.spliterator(), false ) );
+    }
+
+    default <T> Stream<Document> toXmlEach( Object... objects )
+    {
+        return toXmlEach( Options.DEFAULT, Stream.of( objects ) );
+    }
+
+    default void serialize( Options options, Writer writer, @Optional Object object )
+    {
+        Document xmlDocument = toXml( options, object );
+        if( xmlDocument == null )
+        {
+            return;
+        }
+        try
+        {
+            // We want plain Strings to be serialized without quotes
+            if( xmlDocument.getNodeType() == Node.TEXT_NODE )
+            {
+                writer.write( xmlDocument.getNodeValue() );
+            }
+            else
+            {
+                Transformer transformer = TransformerFactory.newInstance().newTransformer();
+                transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
+                transformer.setOutputProperty( OutputKeys.VERSION, "1.1" );
+                transformer.setOutputProperty( OutputKeys.STANDALONE, "yes" );
+                transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
+                transformer.transform( new DOMSource( xmlDocument ), new StreamResult( writer ) );
+            }
+        }
+        catch( IOException ex )
+        {
+            throw new UncheckedIOException( ex );
+        }
+        catch( TransformerException ex )
+        {
+            throw new SerializationException( "Unable to transform XML Document to String", ex );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html b/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html
new file mode 100644
index 0000000..2e2f188
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html
@@ -0,0 +1,24 @@
+<!--
+  ~  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.
+  ~
+  ~
+  -->
+<html>
+    <body>
+        <h2>Serialization SPI.</h2>
+    </body>
+</html>