/// <summary>
        /// Reads the JSON and converts to the real object.
        /// </summary>
        public static object ReadObject(JsonReader reader, Type expectedType = null, ISurrogateContext context = null)
        {
            object      newInstance              = null;
            Array       newInstanceAsArray       = null;
            IList       newInstanceAsIList       = null;
            IDictionary newInstanceAsIDictionary = null;
            Type        elementType              = null;
            JObject     jobj = JObject.Load(reader);

            var  assemblyQualifiedType = jobj["__type"].Value <string>();
            Type targetType            = SurrogatesDirectory.ContractedStringToType(assemblyQualifiedType);

            var isArray = jobj["__isArray"] != null ? jobj["__isArray"].Value <bool>() : false;

            if (isArray)
            {
                var length          = jobj["__arrayLength"].Value <int>();
                var elementTypeName = jobj["__elementType"].Value <string>();
                elementType = SurrogatesDirectory.ContractedStringToType(elementTypeName);
                newInstance = newInstanceAsArray = Array.CreateInstance(elementType, length);
            }
            else
            {
                newInstance = Activator.CreateInstance(targetType);
            }

            foreach (JProperty prop in jobj.Properties())
            {
                MemberInfo reflectedMember = null;
                object     adapterValue    = null;

                if (prop.Name == "__type")
                {
                    continue;
                }
                if (prop.Name == "__isArray")
                {
                    continue;
                }
                if (prop.Name == "__arrayLength")
                {
                    continue;
                }
                if (prop.Name == "__elementType")
                {
                    continue;
                }
                if (prop.Name == "__dict")
                {
                    newInstanceAsIDictionary = newInstance as IDictionary;
                    var keys   = prop.Value["_keys"].Value <JArray>();
                    var values = prop.Value["_values"].Value <JArray>();
                    var index  = 0;
                    foreach (var key in keys)
                    {
                        newInstanceAsIDictionary.Add(key, values[index++]);
                    }
                }
                else if (prop.Name == "__arr")
                {
                    var values = prop.Value as IEnumerable;
                    var index  = 0;
                    if (!isArray)
                    {
                        newInstanceAsIList = (IList)newInstance;
                    }
                    foreach (var item in values)
                    {
                        var itemAsJValue = item as JValue;
                        if (isArray)
                        {
                            if (itemAsJValue != null)
                            {
                                if (itemAsJValue.Type == JTokenType.Object)
                                {
                                    newInstanceAsArray.SetValue(ReadObject(itemAsJValue.CreateReader(), null, context), index);
                                }
                                else
                                {
                                    newInstanceAsArray.SetValue(Convert.ChangeType(itemAsJValue.Value, elementType), index);
                                }
                            }
                            else
                            {
                                newInstanceAsArray.SetValue(ReadObject((item as JObject).CreateReader(), null, context), index);
                            }
                        }
                        else
                        {
                            if (itemAsJValue != null)
                            {
                                if (itemAsJValue.Type == JTokenType.Object)
                                {
                                    newInstanceAsIList.Add(ReadObject(itemAsJValue.CreateReader(), null, context));
                                }
                                else
                                {
                                    newInstanceAsIList.Add(itemAsJValue.Value);
                                }
                            }
                            else
                            {
                                newInstanceAsIList.Add(ReadObject((item as JObject).CreateReader(), null, context));
                            }
                        }
                        index++;
                    }
                }
                else
                {
                    //Check that is always ONE member.

                    reflectedMember = GetFieldInfoIncludingBaseClasses(targetType, prop.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    var reflectedField = reflectedMember as FieldInfo;
                    try
                    {
                        if (reflectedField != null)
                        {
                            if (reflectedField.FieldType.IsValueType || reflectedField.FieldType.Name.Equals("String"))
                            {
                                adapterValue = Convert.ChangeType(prop.Value, reflectedField.FieldType);
                                reflectedField.SetValue(newInstance, adapterValue);
                            }
                            else
                            {
                                var currentField = reflectedField;
                                if (typeof(IStateObject).IsAssignableFrom(currentField.FieldType))
                                {
                                    var uniqueID = prop.Value.Value <string>();
                                    if (!String.IsNullOrWhiteSpace(uniqueID))
                                    {
                                        reflectedField.SetValue(newInstance, context.RestoreStateObject(uniqueID));
                                    }
                                }
                                else if (currentField.FieldType.IsSerializable && typeof(MulticastDelegate).IsAssignableFrom(currentField.FieldType))
                                {
                                    //Skip do no support delegate fields
                                }
                                else if (SurrogatesDirectory.IsSurrogateRegistered(currentField.FieldType))
                                {
                                    var uniqueID = prop.Value.Value <string>();
                                    if (!String.IsNullOrWhiteSpace(uniqueID))
                                    {
                                        reflectedField.SetValue(newInstance, context.RestoreSurrogateValue(uniqueID));
                                    }
                                }
                                else
                                {
                                    adapterValue = (prop.Value.ToString() == String.Empty) ? null : ReadObject(prop.Value.CreateReader(), reflectedField.FieldType, context);
                                    reflectedField.SetValue(newInstance, adapterValue);
                                }
                            }
                        }
                    }
                    catch
                    {
                        throw new InvalidOperationException("Error While deserialing object.");
                    }
                }
            }
            return(newInstance);
        }