/* * Copyright (c) 2007, Rickard Öberg. All Rights Reserved. * Copyright (c) 2010, Niclas Hehdman. All Rights Reserved. * Copyright (c) 2012, Paul Merlin. All Rights Reserved. * * Licensed 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.qi4j.spi.value; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.qi4j.api.association.AssociationDescriptor; import org.qi4j.api.entity.EntityReference; import org.qi4j.api.injection.scope.Service; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.property.PropertyDescriptor; import org.qi4j.api.service.ServiceReference; import org.qi4j.api.structure.Application; import org.qi4j.api.structure.Module; import org.qi4j.api.type.CollectionType; import org.qi4j.api.type.EnumType; import org.qi4j.api.type.MapType; import org.qi4j.api.type.ValueCompositeType; import org.qi4j.api.type.ValueType; import org.qi4j.api.util.Base64Encoder; import org.qi4j.api.util.Dates; import org.qi4j.api.value.ValueBuilder; import org.qi4j.api.value.ValueDescriptor; import org.qi4j.api.value.ValueDeserializer; import org.qi4j.api.value.ValueSerializationException; import org.qi4j.functional.Function; import org.qi4j.functional.Function2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.qi4j.functional.Iterables.*; /** * Adapter for pull-parsing and tree-parsing capable ValueDeserializers. * * <p> * Among Plain values (see {@link ValueDeserializer}) some are considered primitives to underlying serialization * mechanisms and by so handed/come without conversion to/from implementations. Primitive values can be one of: * </p> * <ul> * <li>String,</li> * <li>Character or char,</li> * <li>Boolean or boolean,</li> * <li>Integer or int,</li> * <li>Long or long,</li> * <li>Short or short,</li> * <li>Byte or byte,</li> * <li>Float or float,</li> * <li>Double or double.</li> * </ul> * * @param <InputType> Implementor pull-parser type * @param <InputNodeType> Implementor tree-parser node type */ public abstract class ValueDeserializerAdapter<InputType, InputNodeType> implements ValueDeserializer { private static final Logger LOG = LoggerFactory.getLogger( ValueDeserializerAdapter.class ); private static final Logger PULL_PARSING_LOG = LoggerFactory.getLogger( ValueDeserializerAdapter.class.getName() + "#PullParsing" ); private static final Logger TREE_PARSING_LOG = LoggerFactory.getLogger( ValueDeserializerAdapter.class.getName() + "#TreeParsing" ); private static final String UTF_8 = "UTF-8"; private final Map<Class<?>, Function<Object, Object>> deserializers = new HashMap<Class<?>, Function<Object, Object>>(); private final Application application; private final Module module; private Function<Application, Module> valuesModuleFinder; private Module valuesModule; /** * Register a Plain Value type deserialization Function. * * @param <T> Plain Value parametrized Type * @param type Plain Value Type * @param deserializer Deserialization Function */ @SuppressWarnings( "unchecked" ) protected final <T> void registerDeserializer( Class<T> type, Function<Object, T> deserializer ) { deserializers.put( type, (Function<Object, Object>) deserializer ); } @SuppressWarnings( "unchecked" ) public ValueDeserializerAdapter( @Structure Application application, @Structure Module module, @Service ServiceReference<ValueDeserializer> serviceRef ) { this( application, module, serviceRef.metaInfo( Function.class ) ); } protected ValueDeserializerAdapter( Application application, Module module, Function<Application, Module> valuesModuleFinder ) { this.application = application; this.module = module; setValuesModuleFinder( valuesModuleFinder ); // Primitive Value types registerDeserializer( String.class, new Function<Object, String>() { @Override public String map( Object input ) { return input.toString(); } } ); registerDeserializer( Character.class, new Function<Object, Character>() { @Override public Character map( Object input ) { return input.toString().charAt( 0 ); } } ); registerDeserializer( Boolean.class, new Function<Object, Boolean>() { @Override public Boolean map( Object input ) { return ( input instanceof String ) ? Boolean.parseBoolean( (String) input ) : ( (Boolean) input ).booleanValue(); } } ); registerDeserializer( Integer.class, new Function<Object, Integer>() { @Override public Integer map( Object input ) { return ( input instanceof String ) ? Integer.parseInt( (String) input ) : ( (Number) input ).intValue(); } } ); registerDeserializer( Long.class, new Function<Object, Long>() { @Override public Long map( Object input ) { return ( input instanceof String ) ? Long.parseLong( (String) input ) : ( (Number) input ).longValue(); } } ); registerDeserializer( Short.class, new Function<Object, Short>() { @Override public Short map( Object input ) { return ( input instanceof String ) ? Short.parseShort( (String) input ) : ( (Number) input ).shortValue(); } } ); registerDeserializer( Byte.class, new Function<Object, Byte>() { @Override public Byte map( Object input ) { return ( input instanceof String ) ? Byte.parseByte( (String) input ) : ( (Number) input ).byteValue(); } } ); registerDeserializer( Float.class, new Function<Object, Float>() { @Override public Float map( Object input ) { return ( input instanceof String ) ? Float.parseFloat( (String) input ) : ( (Number) input ).floatValue(); } } ); registerDeserializer( Double.class, new Function<Object, Double>() { @Override public Double map( Object input ) { return ( input instanceof String ) ? Double.parseDouble( (String) input ) : ( (Number) input ).doubleValue(); } } ); // Number types registerDeserializer( BigDecimal.class, new Function<Object, BigDecimal>() { @Override public BigDecimal map( Object input ) { return new BigDecimal( input.toString() ); } } ); registerDeserializer( BigInteger.class, new Function<Object, BigInteger>() { @Override public BigInteger map( Object input ) { return new BigInteger( input.toString() ); } } ); // Date types registerDeserializer( Date.class, new Function<Object, Date>() { @Override public Date map( Object input ) { return Dates.fromString( input.toString() ); } } ); registerDeserializer( DateTime.class, new Function<Object, DateTime>() { @Override public DateTime map( Object input ) { return new DateTime( input, DateTimeZone.UTC ); } } ); registerDeserializer( LocalDateTime.class, new Function<Object, LocalDateTime>() { @Override public LocalDateTime map( Object input ) { return new LocalDateTime( input ); } } ); registerDeserializer( LocalDate.class, new Function<Object, LocalDate>() { @Override public LocalDate map( Object input ) { return new LocalDate( input ); } } ); // Other supported types registerDeserializer( EntityReference.class, new Function<Object, EntityReference>() { @Override public EntityReference map( Object input ) { return EntityReference.parseEntityReference( input.toString() ); } } ); } private void setValuesModuleFinder( Function<Application, Module> valuesModuleFinder ) { if( valuesModuleFinder != null ) { LOG.debug( "Will use the provided Function to find Module to build new ValueComposites instances: {}", valuesModuleFinder ); } this.valuesModuleFinder = valuesModuleFinder; this.valuesModule = null; } private Module valuesModule() { if( valuesModule == null ) { if( valuesModuleFinder == null ) { valuesModule = module; } else { valuesModule = valuesModuleFinder.map( application ); if( valuesModule == null ) { throw new ValueSerializationException( "Values Module provided by the finder Function was null." ); } LOG.debug( "Will use a specific Module to build new ValueComposites instances: {}", valuesModule ); } } return valuesModule; } @Override public <T> Function<String, T> deserialize( Class<T> type ) { if( CollectionType.isCollection( type ) ) { ValueType objectValueType = new ValueType( Object.class ); return deserialize( new CollectionType( type, objectValueType ) ); } if( MapType.isMap( type ) ) { ValueType objectValueType = new ValueType( Object.class ); return deserialize( new MapType( type, objectValueType, objectValueType ) ); } return deserialize( new ValueType( type ) ); } @Override public final <T> Function<String, T> deserialize( final ValueType valueType ) { return new Function<String, T>() { @Override public T map( String input ) { return deserialize( valueType, input ); } }; } @Override public final <T> Function2<ValueType, String, T> deserialize() { return new Function2<ValueType, String, T>() { @Override public T map( ValueType valueType, String input ) { return deserialize( valueType, input ); } }; } @Override public final <T> T deserialize( Class<?> type, String input ) throws ValueSerializationException { if( CollectionType.isCollection( type ) ) { ValueType objectValueType = new ValueType( Object.class ); return deserialize( new CollectionType( type, objectValueType ), input ); } if( MapType.isMap( type ) ) { ValueType objectValueType = new ValueType( Object.class ); return deserialize( new MapType( type, objectValueType, objectValueType ), input ); } return deserialize( new ValueType( type ), input ); } @Override public final <T> T deserialize( ValueType valueType, String input ) throws ValueSerializationException { try { return deserializeRoot( valueType, new ByteArrayInputStream( input.getBytes( UTF_8 ) ) ); } catch( ValueSerializationException ex ) { throw ex; } catch( Exception ex ) { throw new ValueSerializationException( "Could not deserialize value", ex ); } } @Override public final <T> T deserialize( Class<?> type, InputStream input ) throws ValueSerializationException { if( CollectionType.isCollection( type ) ) { ValueType objectValueType = new ValueType( Object.class ); return deserialize( new CollectionType( type, objectValueType ), input ); } if( MapType.isMap( type ) ) { ValueType objectValueType = new ValueType( Object.class ); return deserialize( new MapType( type, objectValueType, objectValueType ), input ); } return deserialize( new ValueType( type ), input ); } @Override public final <T> T deserialize( ValueType valueType, InputStream input ) throws ValueSerializationException { try { return deserializeRoot( valueType, input ); } catch( ValueSerializationException ex ) { throw ex; } catch( Exception ex ) { throw new ValueSerializationException( "Could not deserialize value", ex ); } } @SuppressWarnings( "unchecked" ) private <T> T deserializeRoot( ValueType valueType, InputStream input ) throws Exception { final Class<?> type = first( valueType.types() ); // Plain ValueType if( deserializers.get( type ) != null ) { Scanner scanner = new Scanner( input, UTF_8 ).useDelimiter( "\\A" ); if( !scanner.hasNext() ) { return String.class.equals( type ) ? (T) "" : null; } String string = scanner.next(); return (T) deserializers.get( type ).map( string ); } else // Array ValueType if( type.isArray() ) { Scanner scanner = new Scanner( input, UTF_8 ).useDelimiter( "\\A" ); if( !scanner.hasNext() ) { return null; } String string = scanner.next(); return (T) deserializeBase64Serialized( string ); } else // Complex ValueType { InputType adaptedInput = adaptInput( input ); onDeserializationStart( valueType, adaptedInput ); T deserialized = doDeserialize( valueType, adaptedInput ); onDeserializationEnd( valueType, adaptedInput ); return deserialized; } } @SuppressWarnings( "unchecked" ) private <T> T doDeserialize( ValueType valueType, InputType input ) throws Exception { final Class<?> type = first( valueType.types() ); // Registered deserializers if( deserializers.get( type ) != null ) { Object value = readPlainValue( input ); if( value == null ) { return null; } return (T) deserializers.get( type ).map( value ); } else // Explicit ValueComposite if( ValueCompositeType.class.isAssignableFrom( valueType.getClass() ) ) { PULL_PARSING_LOG.trace( "ValueCompositeType assignable - deserializeValueComposite( {} )", input ); return (T) deserializeValueComposite( valueType, input ); } else // Explicit Collections if( CollectionType.class.isAssignableFrom( valueType.getClass() ) ) { PULL_PARSING_LOG.trace( "CollectionType assignable - deserializeCollection( {} )", input ); return (T) deserializeCollection( (CollectionType) valueType, input ); } else // Explicit Map if( MapType.class.isAssignableFrom( valueType.getClass() ) ) { PULL_PARSING_LOG.trace( "MapType assignable - deserializeMap( {} )", input ); return (T) deserializeMap( (MapType) valueType, input ); } else // Enum if( EnumType.class.isAssignableFrom( valueType.getClass() ) || type.isEnum() ) { PULL_PARSING_LOG.trace( "EnumType assignable - readValue( {} )", input ); return (T) Enum.valueOf( (Class) type, readPlainValue( input ).toString() ); } else // Array if( type.isArray() ) { return (T) deserializeBase64Serialized( readPlainValue( input ).toString() ); } // Guessed Deserialization PULL_PARSING_LOG.trace( "Unknown ValueType - deserializeGuessed( {} )", input ); return (T) deserializeGuessed( valueType, input ); } private <T> Function<InputType, T> buildDeserializeInputFunction( final ValueType valueType ) { return new Function<InputType, T>() { @Override public T map( InputType input ) { try { return doDeserialize( valueType, input ); } catch( ValueSerializationException ex ) { throw ex; } catch( Exception ex ) { throw new ValueSerializationException( ex ); } } }; } private <T> Collection<T> deserializeCollection( CollectionType collectionType, InputType input ) throws Exception { Collection<T> collection; Class<?> collectionMainType = first( collectionType.types() ); if( Set.class.equals( collectionMainType ) ) { collection = new LinkedHashSet<T>(); } else { collection = new ArrayList<T>(); } return readArrayInCollection( input, this.<T>buildDeserializeInputFunction( collectionType.collectedType() ), collection ); } private <K, V> Map<K, V> deserializeMap( MapType mapType, InputType input ) throws Exception { return readMapInMap( input, this.<K>buildDeserializeInputFunction( mapType.keyType() ), this.<V>buildDeserializeInputFunction( mapType.valueType() ), new HashMap<K, V>() ); } private <T> T deserializeValueComposite( ValueType valueType, InputType input ) throws Exception { PULL_PARSING_LOG.trace( "Switching to TREE PARSING @( {} )", input ); InputNodeType inputNode = readObjectTree( input ); TREE_PARSING_LOG.trace( "Switched to TREE PARSING @( {} )", input ); TREE_PARSING_LOG.trace( "ObjectNode is {}", inputNode ); if( inputNode == null ) { return null; } return deserializeNodeValueComposite( valueType, inputNode ); } private <T> T deserializeNodeValueComposite( ValueType valueType, InputNodeType inputNode ) throws Exception { ValueCompositeType valueCompositeType = (ValueCompositeType) valueType; Class<?> valueBuilderType = first( valueCompositeType.types() ); String typeInfo = this.<String>getObjectFieldValue( inputNode, "_type", this.<String>buildDeserializeInputNodeFunction( new ValueType( String.class ) ) ); TREE_PARSING_LOG.trace( "In deserializeNodeValueComposite(), getObjectFieldValue( {} ) returned '{}'", inputNode, typeInfo ); if( typeInfo != null ) { ValueDescriptor valueDescriptor = valuesModule().valueDescriptor( typeInfo ); if( valueDescriptor == null ) { throw new ValueSerializationException( "Specified value type could not be resolved: " + typeInfo ); } valueCompositeType = valueDescriptor.valueType(); valueBuilderType = Class.forName( typeInfo ); if( !valueType.equals( valueCompositeType ) ) { TREE_PARSING_LOG.debug( "Overriding {} with {} as defined in _type field.", valueType, valueCompositeType ); } } return deserializeValueComposite( valueCompositeType, valueBuilderType, inputNode ); } private <T> T deserializeValueComposite( ValueCompositeType valueCompositeType, Class<?> valueBuilderType, InputNodeType inputNode ) throws Exception { final Map<String, Object> stateMap = new HashMap<String, Object>(); // Properties for( PropertyDescriptor property : valueCompositeType.properties() ) { String propertyName = property.qualifiedName().name(); Object value; if( objectHasField( inputNode, propertyName ) ) { value = getObjectFieldValue( inputNode, propertyName, buildDeserializeInputNodeFunction( property.valueType() ) ); TREE_PARSING_LOG.trace( "In deserializeValueComposite(), getObjectFieldValue( {} ) for key {} returned '{}' of class {}", inputNode, propertyName, value, value == null ? "N/A" : value.getClass() ); if( property.isImmutable() ) { if( value instanceof Set ) { value = Collections.unmodifiableSet( (Set<?>) value ); } else if( value instanceof List ) { value = Collections.unmodifiableList( (List<?>) value ); } else if( value instanceof Map ) { value = Collections.unmodifiableMap( (Map<?, ?>) value ); } } TREE_PARSING_LOG.trace( "Property {}#{}( {} ) deserialized value is '{}' of class {}", property.qualifiedName().type(), property.qualifiedName().name(), property.valueType(), value, value == null ? "N/A" : value.getClass() ); } else { // Serialized object does not contain the field, try to default it value = property.initialValue( valuesModule() ); TREE_PARSING_LOG.trace( "Property {} was not defined in serialized object and has been defaulted to '{}'", property.qualifiedName(), value ); } stateMap.put( propertyName, value ); } // Associations for( AssociationDescriptor association : valueCompositeType.associations() ) { String associationName = association.qualifiedName().name(); if( objectHasField( inputNode, associationName ) ) { Object value = getObjectFieldValue( inputNode, associationName, buildDeserializeInputNodeFunction( new ValueType( EntityReference.class ) ) ); stateMap.put( associationName, value ); } } // ManyAssociations for( AssociationDescriptor manyAssociation : valueCompositeType.manyAssociations() ) { String manyAssociationName = manyAssociation.qualifiedName().name(); if( objectHasField( inputNode, manyAssociationName ) ) { Object value = getObjectFieldValue( inputNode, manyAssociationName, buildDeserializeInputNodeFunction( new CollectionType( Collection.class, new ValueType( EntityReference.class ) ) ) ); stateMap.put( manyAssociationName, value ); } } ValueBuilder<?> valueBuilder = buildNewValueBuilderWithState( valueBuilderType, stateMap ); return (T) valueBuilder.newInstance(); // Unchecked cast because the builder could use a type != T } private <T> Function<InputNodeType, T> buildDeserializeInputNodeFunction( final ValueType valueType ) { return new Function<InputNodeType, T>() { @Override public T map( InputNodeType inputNode ) { try { return doDeserializeInputNodeValue( valueType, inputNode ); } catch( ValueSerializationException ex ) { throw ex; } catch( Exception ex ) { throw new ValueSerializationException( ex ); } } }; } @SuppressWarnings( "unchecked" ) private <T> T doDeserializeInputNodeValue( ValueType valueType, InputNodeType inputNode ) throws Exception { if( inputNode == null ) { return null; } final Class<?> type = first( valueType.types() ); // Registered deserializers if( deserializers.get( type ) != null ) { Object value = asSimpleValue( inputNode ); TREE_PARSING_LOG.trace( "While registered deserializer attempt, asSimpleValue( {} ) returned '{}'", inputNode, value ); if( value == null ) { return null; } return (T) deserializers.get( type ).map( value ); } else // Explicit ValueComposite if( ValueCompositeType.class.isAssignableFrom( valueType.getClass() ) ) { return (T) deserializeNodeValueComposite( (ValueCompositeType) valueType, inputNode ); } else // Explicit Collections if( CollectionType.class.isAssignableFrom( valueType.getClass() ) ) { return (T) deserializeNodeCollection( (CollectionType) valueType, inputNode ); } else // Explicit Map if( MapType.class.isAssignableFrom( valueType.getClass() ) ) { return (T) deserializeNodeMap( (MapType) valueType, inputNode ); } else // Enum if( EnumType.class.isAssignableFrom( valueType.getClass() ) || type.isEnum() ) { Object value = asSimpleValue( inputNode ); TREE_PARSING_LOG.trace( "While Enum deserialize attempt, asSimpleValue( {} ) returned '{}'", inputNode, value ); if( value == null ) { return null; } return (T) Enum.valueOf( (Class) type, value.toString() ); } // Guessed deserialization return (T) deserializeNodeGuessed( valueType, inputNode ); } private ValueBuilder<?> buildNewValueBuilderWithState( Class<?> type, final Map<String, Object> stateMap ) { return valuesModule().newValueBuilderWithState( type, new Function<PropertyDescriptor, Object>() { @Override public Object map( PropertyDescriptor property ) { return stateMap.get( property.qualifiedName().name() ); } }, new Function<AssociationDescriptor, EntityReference>() { @Override public EntityReference map( AssociationDescriptor association ) { Object entityRef = stateMap.get( association.qualifiedName().name() ); if( entityRef == null ) { return null; } else { return (EntityReference) entityRef; } } }, new Function<AssociationDescriptor, Iterable<EntityReference>>() { @Override @SuppressWarnings( "unchecked" ) public Iterable<EntityReference> map( AssociationDescriptor manyAssociation ) { Object entityRefs = stateMap.get( manyAssociation.qualifiedName().name() ); if( entityRefs == null ) { return empty(); } else { return (Iterable<EntityReference>) entityRefs; } } } ); } @SuppressWarnings( "unchecked" ) private <T> T deserializeGuessed( ValueType valueType, InputType input ) throws Exception { InputNodeType inputNode = readObjectTree( input ); if( inputNode == null ) { return null; } return deserializeNodeGuessed( valueType, inputNode ); } private <T> Collection<T> deserializeNodeCollection( CollectionType collectionType, InputNodeType inputNode ) throws Exception { Collection<T> collection; Class<?> collectionMainType = first( collectionType.types() ); if( Set.class.equals( collectionMainType ) ) { collection = new LinkedHashSet<T>(); } else { collection = new ArrayList<T>(); } putArrayNodeInCollection( inputNode, this.<T>buildDeserializeInputNodeFunction( collectionType.collectedType() ), collection ); return collection; } private <K, V> Map<K, V> deserializeNodeMap( MapType mapType, InputNodeType inputNode ) throws Exception { Map<K, V> map = new HashMap<K, V>(); putArrayNodeInMap( inputNode, this.<K>buildDeserializeInputNodeFunction( mapType.keyType() ), this.<V>buildDeserializeInputNodeFunction( mapType.valueType() ), map ); return map; } @SuppressWarnings( "unchecked" ) private <T> T deserializeNodeGuessed( ValueType valueType, InputNodeType inputNode ) throws Exception { if( isObjectValue( inputNode ) ) { // Attempt ValueComposite deserialization ValueCompositeType valueCompositeType; if( objectHasField( inputNode, "_type" ) ) // with _type info { String typeInfo = this.<String>getObjectFieldValue( inputNode, "_type", this.<String>buildDeserializeInputNodeFunction( new ValueType( String.class ) ) ); TREE_PARSING_LOG.trace( "In deserializeNodeGuessed(), getObjectFieldValue( {} ) returned '{}'", inputNode, typeInfo ); ValueDescriptor valueDescriptor = valuesModule().valueDescriptor( typeInfo ); if( valueDescriptor == null ) { throw new ValueSerializationException( "Specified value type could not be resolved: " + typeInfo ); } valueCompositeType = valueDescriptor.valueType(); TREE_PARSING_LOG.debug( "Overriding {} with {} as defined in _type field.", valueType, valueCompositeType ); } else // without _type info { ValueDescriptor valueDescriptor = valuesModule().valueDescriptor( first( valueType.types() ).getName() ); if( valueDescriptor == null ) { throw new ValueSerializationException( "Don't know how to deserialize " + inputNode ); } valueCompositeType = valueDescriptor.valueType(); TREE_PARSING_LOG.debug( "Overriding {} with {} as found in available ValueComposites.", valueType, valueCompositeType ); } Class<?> valueBuilderType = first( valueCompositeType.types() ); return deserializeValueComposite( valueCompositeType, valueBuilderType, inputNode ); } // Last resort : base64 java deserialization return (T) deserializeBase64Serialized( inputNode ); } @SuppressWarnings( "unchecked" ) private <T> T deserializeBase64Serialized( InputNodeType inputNode ) throws Exception { Object value = asSimpleValue( inputNode ); TREE_PARSING_LOG.trace( "While Base64 deserialize attempt, asSimpleValue( {} ) returned '{}'", inputNode, value ); if( value == null ) { return null; } String base64 = value.toString(); return deserializeBase64Serialized( base64 ); } @SuppressWarnings( "unchecked" ) private <T> T deserializeBase64Serialized( String inputString ) throws Exception { byte[] bytes = inputString.getBytes( UTF_8 ); bytes = Base64Encoder.decode( bytes ); ObjectInputStream oin = new ObjectInputStream( new ByteArrayInputStream( bytes ) ); Object result = oin.readObject(); oin.close(); return (T) result; } // // Deserialization Extension Points // /** * Called by the adapter on deserialization start, after {@link #adaptInput(java.io.InputStream)}. * * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected void onDeserializationStart( ValueType valueType, InputType input ) throws Exception { // NOOP } /** * Called by the adapter on deserialization end. * * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected void onDeserializationEnd( ValueType valueType, InputType input ) throws Exception { // NOOP } // // Pull Parsing Deserialization // /** * This method is always called first, this is a chance to wrap the input type. * * @param input InputStream to adapt * @return Adapted input * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected abstract InputType adaptInput( InputStream input ) throws Exception; /** * @return a Plain Value read from the input * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected abstract Object readPlainValue( InputType input ) throws Exception; /** * @return The filled collection or null if no array * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected abstract <T> Collection<T> readArrayInCollection( InputType input, Function<InputType, T> deserializer, Collection<T> collection ) throws Exception; /** * A Map<K,V> is serialized in an array of entries objects. * * <p>Here is an example in JSON:</p> * <pre> * [ * { "key": "foo", "value": "bar" }, * { "key": "cathedral", "value": "bazar" } * ] * </pre> * <p>And an empty Map:</p> * <pre>[]</pre> * <p> * This allow to use any type as keys and values while keeping the Map order at the cost of having * non-predictible order of key/value inside an entry object. * </p> * * @return The filled map or null if no array * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected abstract <K, V> Map<K, V> readMapInMap( InputType input, Function<InputType, K> keyDeserializer, Function<InputType, V> valueDeserializer, Map<K, V> map ) throws Exception; /** * @return an InputNodeType or null if the value was null * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected abstract InputNodeType readObjectTree( InputType input ) throws Exception; // // Tree Parsing Deserialization // protected abstract Object asSimpleValue( InputNodeType inputNode ) throws Exception; protected abstract boolean isObjectValue( InputNodeType inputNode ) throws Exception; protected abstract boolean objectHasField( InputNodeType inputNode, String key ) throws Exception; /** * Return null if the field do not exists. * @throws Exception that will be wrapped in a {@link ValueSerializationException} */ protected abstract <T> T getObjectFieldValue( InputNodeType inputNode, String key, Function<InputNodeType, T> valueDeserializer ) throws Exception; protected abstract <T> void putArrayNodeInCollection( InputNodeType inputNode, Function<InputNodeType, T> deserializer, Collection<T> collection ) throws Exception; protected abstract <K, V> void putArrayNodeInMap( InputNodeType inputNode, Function<InputNodeType, K> keyDeserializer, Function<InputNodeType, V> valueDeserializer, Map<K, V> map ) throws Exception; }