Package org.qi4j.api.json

Source Code of org.qi4j.api.json.JSONDeserializer

package org.qi4j.api.json;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.qi4j.api.association.AssociationDescriptor;
import org.qi4j.api.common.QualifiedName;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.property.DefaultValues;
import org.qi4j.api.property.PropertyDescriptor;
import org.qi4j.api.structure.Module;
import org.qi4j.api.type.*;
import org.qi4j.api.unitofwork.UnitOfWork;
import org.qi4j.api.util.Dates;
import org.qi4j.api.value.ValueBuilder;
import org.qi4j.api.value.ValueDescriptor;
import org.qi4j.functional.Function;
import org.qi4j.functional.Iterables;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
* TODO
*/
public class JSONDeserializer
{
    private static Set<Class<?>> nonStringClasses = new HashSet<Class<?>>();

    /**
     * Check if the given value type should be represented as a JSON string. This can be used
     * by clients to figure out whether to add "" around a string to be deserialized.
     *
     * @param valueType
     * @return
     */
    public static boolean isString(ValueType valueType)
    {
        if (!valueType.getClass().equals( ValueType.class ))
            return false;
        else
            return !nonStringClasses.contains( valueType.type()  );
    }

    private static Map<Class<?>, Function<Object, Object>> typeFunctions = new HashMap<Class<?>, Function<Object, Object>>(  );

    public static <T> void registerDeserializer( Class<T> type, Function<Object, T> typeFunction )
    {
        typeFunctions.put( type, (Function<Object, Object>) typeFunction );
    }

    private static <T> Function<Object, T> identity()
    {
        return new Function<Object, T>()
        {
            @Override
            public T map( Object json )
            {
                return (T) json;
            }
        };
    }

    static
    {
        nonStringClasses.add( Boolean.class );
        nonStringClasses.add( Short.class );
        nonStringClasses.add( Integer.class );
        nonStringClasses.add( Long.class );
        nonStringClasses.add( Byte.class );
        nonStringClasses.add( Float.class );
        nonStringClasses.add( Double.class );

        registerDeserializer( String.class, new Function<Object, String>()
        {
            @Override
            public String map( Object o )
            {
                return o.toString();
            }
        } );

        // Primitive types
        registerDeserializer( Boolean.class, JSONDeserializer.<Boolean>identity() );
        registerDeserializer( Integer.class, new Function<Object, Integer>()
        {
            @Override
            public Integer map( Object o )
            {
                return ((Number) o).intValue();
            }
        } );

        registerDeserializer( Long.class, new Function<Object, Long>()
        {
            @Override
            public Long map( Object o )
            {
                return ((Number) o).longValue();
            }
        } );

        registerDeserializer( Short.class, new Function<Object, Short>()
        {
            @Override
            public Short map( Object o )
            {
                return ((Number) o).shortValue();
            }
        } );

        registerDeserializer( Byte.class, new Function<Object, Byte>()
        {
            @Override
            public Byte map( Object o )
            {
                return ((Number) o).byteValue();
            }
        } );

        registerDeserializer( Float.class, new Function<Object, Float>()
        {
            @Override
            public Float map( Object o )
            {
                return ((Number) o).floatValue();
            }
        } );

        registerDeserializer( Double.class, new Function<Object, Double>()
        {
            @Override
            public Double map( Object o )
            {
                return ((Number) o).doubleValue();
            }
        } );

        // Number types
        registerDeserializer( BigDecimal.class, new Function<Object, BigDecimal>()
        {
            @Override
            public BigDecimal map( Object json )
            {
                return new BigDecimal( json.toString() );
            }
        } );
        registerDeserializer( BigInteger.class, new Function<Object, BigInteger>()
        {
            @Override
            public BigInteger map( Object json )
            {
                return new BigInteger( json.toString() );
            }
        } );

        // Date types
        registerDeserializer( Date.class, new Function<Object, Date>()
        {
            @Override
            public Date map( Object json )
            {
                return Dates.fromString( json.toString() );
            }
        } );
        registerDeserializer( DateTime.class, new Function<Object, DateTime>()
        {
            @Override
            public DateTime map( Object json )
            {
                return new DateTime( json, DateTimeZone.UTC );
            }
        } );
        registerDeserializer( LocalDateTime.class, new Function<Object, LocalDateTime>()
        {
            @Override
            public LocalDateTime map( Object json )
            {
                return new LocalDateTime( json );
            }
        } );
        registerDeserializer( LocalDate.class, new Function<Object, LocalDate>()
        {
            @Override
            public LocalDate map( Object json )
            {
                return new LocalDate( json );
            }
        } );

        // Other supported types
        registerDeserializer( EntityReference.class, new Function<Object, EntityReference>()
        {
            @Override
            public EntityReference map( Object json )
            {
                return EntityReference.parseEntityReference( json.toString() );
            }
        } );
    }

    private Module module;

    public JSONDeserializer( Module module )
    {
        this.module = module;
    }

    public Object deserialize( Object json, ValueType valueType )
            throws JSONException
    {
        if (json == JSONObject.NULL)
            return null;

        Function<Object, ? extends Object> typeFunction = typeFunctions.get( valueType.type() );

        if (typeFunction != null)
            return typeFunction.map( json );

        if( valueType instanceof CollectionType )
        {
            CollectionType collectionType = (CollectionType) valueType;

            JSONArray array = (JSONArray) json;

            Collection<Object> coll;
            if( valueType.type().equals( Set.class ) )
            {
                coll = new LinkedHashSet<Object>();

                for( int i = 0; i < array.length(); i++ )
                {
                    Object value = array.get( i );
                    coll.add( deserialize( value, collectionType.collectedType() ));
                }
            } else
            {
                coll = new ArrayList<Object>();

                for( int i = 0; i < array.length(); i++ )
                {
                    Object value = array.get( i );
                    coll.add( deserialize( value, collectionType.collectedType() ));
                }
            }

            return coll;
        } else if( valueType instanceof EnumType )
        {
            try
            {
                Class enumType = valueType.type();

                // Get enum value
                return Enum.valueOf( enumType, (String) json );
            } catch( Exception e )
            {
                throw new IllegalArgumentException( e );
            }
        } else if( valueType instanceof MapType )
        {
            if( json instanceof String )
            {
                try
                {
                    // Legacy handling of serialized maps
                    String serializedString = (String) json;
                    byte[] bytes = serializedString.getBytes( "UTF-8" );
                    bytes = Base64Encoder.decode( bytes );
                    ByteArrayInputStream bin = new ByteArrayInputStream( bytes );
                    ObjectInputStream oin = new ObjectInputStream( bin );
                    Object result = oin.readObject();
                    oin.close();

                    return result;
                } catch( IOException e )
                {
                    throw new IllegalStateException( "Could not deserialize value", e );
                } catch( ClassNotFoundException e )
                {
                    throw new IllegalStateException( "Could not find class for serialized value", e );
                }
            } else
            {
                MapType mapType = (MapType) valueType;

                // New array-based handling
                JSONArray array = (JSONArray) json;

                Map<Object, Object> map = (Map<Object, Object>) DefaultValues.getDefaultValue( Map.class );

                for( int i = 0; i < array.length(); i++ )
                {
                    JSONObject entry = array.getJSONObject( i );
                    Object key = deserialize( entry.get( "key" ), mapType.getKeyType() );
                    Object value = deserialize(entry.get( "value" ), mapType.getValueType());
                    map.put( key, value );
                }

                return map;
            }
        } else if( valueType instanceof ValueCompositeType )
        {
            JSONObject jsonObject = (JSONObject) json;

            ValueCompositeType actualValueType = (ValueCompositeType) valueType;
            String actualType = jsonObject.optString( "_type" );
            if( !actualType.equals( "" ) )
            {
                ValueDescriptor descriptor = module.valueDescriptor( actualType );

                if( descriptor == null )
                {
                    throw new IllegalArgumentException( "Could not find any value of type '" + actualType + "' in module" + module );
                }

                actualValueType = descriptor.valueType();
            }

            final Map<QualifiedName, Object> values = new HashMap<QualifiedName, Object>();
            for( PropertyDescriptor persistentProperty : actualValueType.properties() )
            {
                Object valueJson = null;
                try
                {
                    valueJson = jsonObject.opt( persistentProperty.qualifiedName().name() );

                    Object value = null;
                    if( valueJson != null && !valueJson.equals( JSONObject.NULL ) )
                    {
                        value = deserialize( valueJson, persistentProperty.valueType() );

                        if (persistentProperty.isImmutable())
                        {
                            if (value instanceof Set)
                            {
                                value = Collections.unmodifiableSet( (Set<? extends Object>) value);
                            } else if (value instanceof List)
                            {
                                value = Collections.unmodifiableList( (List<? extends Object>) value);
                            } else if (value instanceof Map)
                            {
                                value = Collections.unmodifiableMap( (Map<? extends Object, ? extends Object>) value);
                            }
                        }
                    }

                    values.put( persistentProperty.qualifiedName(), value );
                } catch( JSONException e )
                {
                    // Not found in JSON or wrong format - try defaulting it
                    try
                    {
                        Object defaultValue = DefaultValues.getDefaultValue( persistentProperty.valueType().type() );
                        values.put( persistentProperty.qualifiedName(), defaultValue );
                    } catch( RuntimeException e1 )
                    {
                        // Didn't work, throw the exception
                        throw e;
                    }
                }
            }

            for( AssociationDescriptor associationDescriptor : actualValueType.associations() )
            {
                Object valueJson = jsonObject.optString( associationDescriptor.qualifiedName().name() );
                if (valueJson != null)
                    values.put( associationDescriptor.qualifiedName(), EntityReference.parseEntityReference( valueJson.toString() ) );
            }

            for( AssociationDescriptor associationDescriptor : actualValueType.manyAssociations() )
            {
                JSONArray jsonArray = jsonObject.optJSONArray( associationDescriptor.qualifiedName().name() );
                if (jsonArray != null)
                {
                    List<EntityReference> refs = new ArrayList<EntityReference>();
                    for (int i = 0; i < jsonArray.length(); i++)
                    {
                        refs.add( EntityReference.parseEntityReference( jsonArray.getString( i ) ) );
                    }
                }
            }

            ValueBuilder valueBuilder = module
                    .newValueBuilderWithState( actualValueType.type(), new Function<PropertyDescriptor, Object>()
                    {
                        @Override
                        public Object map( PropertyDescriptor descriptor )
                        {
                            return values.get( descriptor.qualifiedName() );
                        }
                    },new Function<AssociationDescriptor, EntityReference>()
                    {
                        @Override
                        public EntityReference map( AssociationDescriptor associationDescriptor )
                        {
                            Object ref = values.get( associationDescriptor.qualifiedName() );
                            if (ref == null)
                                return null;
                            else
                                return (EntityReference) ref;
                        }
                    },new Function<AssociationDescriptor, Iterable<EntityReference>>()
                    {
                        @Override
                        public Iterable<EntityReference> map( AssociationDescriptor associationDescriptor )
                        {
                            Object ref = values.get( associationDescriptor.qualifiedName() );
                            if (ref == null)
                                return Iterables.empty();
                            else
                                return (Iterable<EntityReference>) ref;
                        }
                    });

            return valueBuilder.newInstance();
        } else
        {
            try
            {
                if( json instanceof JSONObject )
                {
                    // ValueComposite deserialization
                    JSONObject jsonObject = (JSONObject) json;
                    String type = jsonObject.getString( "_type" );

                    ValueDescriptor valueDescriptor = module.valueDescriptor( type );
                    return deserialize( json, valueDescriptor.valueType() );
                } else
                {
                    String serializedString = (String) json;
                    byte[] bytes = serializedString.getBytes( "UTF-8" );
                    bytes = Base64Encoder.decode( bytes );
                    ByteArrayInputStream bin = new ByteArrayInputStream( bytes );
                    ObjectInputStream oin = new ObjectInputStream( bin );
                    Object result = oin.readObject();
                    oin.close();

                    if( result instanceof EntityReference )
                    {
                        EntityReference ref = (EntityReference) result;
                        if( !valueType.type().equals( EntityReference.class ) )
                        {
                            Class mixinType = valueType.type();
                            if (module.isUnitOfWorkActive())
                            {
                                UnitOfWork unitOfWork = module.currentUnitOfWork();
                                result = unitOfWork.get( mixinType, ref.identity() );
                            }
                        }
                    }

                    return result;
                }
            } catch( IOException e )
            {
                throw new IllegalStateException( "Could not deserialize value", e );
            } catch( ClassNotFoundException e )
            {
                throw new IllegalStateException( "Could not find class for serialized value", e );
            }
        }
    }
}
TOP

Related Classes of org.qi4j.api.json.JSONDeserializer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.