private static Expression DeserializeExpression(Expression endpoint, Expression value, Type targetType)
            {
                if (targetType == typeof(void))
                {
                    return(Expression.Block(typeof(void), value));
                }

                if (targetType == typeof(string))
                {
                    return(value);
                }

                if (targetType.IsPrimitive)
                {
                    return(Expression.Call(
                               null,
                               typeof(Convert).GetMethod("To" + targetType.Name, BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string), typeof(IFormatProvider) }, null),
                               value,
                               Expression.Property(null, typeof(CultureInfo).GetProperty("InvariantCulture"))));
                }

                if (targetType.IsEnum)
                {
                    return(Expression.Convert(
                               Expression.Call(
                                   typeof(Enum).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(Type), typeof(string), typeof(bool) }, null),
                                   Expression.Constant(targetType),
                                   value,
                                   Expression.Constant(true)),
                               targetType));
                }

                var itemType = targetType.TryGetCollectionItemType();

                if (itemType != null)
                {
                    var enumerable = Expression.Call(typeof(Serializer <>).MakeGenericType(itemType).GetMethod("DeserializeCollection", BindingFlags.NonPublic | BindingFlags.Static), endpoint, value);
                    if (targetType.IsArray)
                    {
                        return(Expression.Call(typeof(Serializer <>).MakeGenericType(itemType).GetMethod("CollectionToArray", BindingFlags.NonPublic | BindingFlags.Static), enumerable));
                    }
                    return(enumerable);
                }

                var nullableType = Nullable.GetUnderlyingType(targetType);

                if (nullableType != null && Check(nullableType).CanSerialize())
                {
                    return(Expression.Condition(
                               Expression.Call(typeof(string).GetMethod("IsNullOrEmpty"), value),
                               Expression.New(targetType),
                               Expression.New(targetType.GetConstructor(new[] { nullableType }), DeserializeExpression(endpoint, value, nullableType))));
                }

                if (Check(targetType).CanBuildProxy())
                {
                    MethodInfo customFactory;
                    TryGetCustomProxyFactory(targetType, out customFactory);

                    Expression createExpression;
                    if (customFactory != null)
                    {
                        var realProxyType = ProxyBuilder.BuildProxyType(customFactory.GetParameters()[0].ParameterType);
                        createExpression = Expression.Call(
                            customFactory,
                            Expression.New(realProxyType.GetConstructor(new [] { typeof(IEndpoint), typeof(string) }), endpoint, value));
                    }
                    else
                    {
                        var type = ProxyBuilder.BuildProxyType(targetType);
                        createExpression = Expression.New(type.GetConstructor(new[] { typeof(IEndpoint), typeof(string) }), endpoint, value);
                    }

                    return(Expression.Condition(
                               Expression.Call(
                                   typeof(string).GetMethod("IsNullOrEmpty"),
                                   value),
                               Expression.Constant(null, targetType),
                               Expression.Convert(createExpression, targetType)));
                }

                if (targetType.IsClass)
                {
                    if (targetType.IsDefined(typeof(DataContractAttribute), true))
                    {
                        return(Expression.Call(typeof(Serializer <T>).GetMethod("DeserializeClass", BindingFlags.NonPublic | BindingFlags.Static), value));
                    }
                    return(Expression.Block(
                               Expression.Throw(Expression.New(typeof(InvalidOperationException).GetConstructor(new[] { typeof(string) }), Expression.Constant("cannot deserialize non-data class"))),
                               Expression.Default(targetType)));
                }

                throw new Exception("cannot deserialize expression to type " + targetType);
            }