public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializerReader internalReader)
        {
            if (reader.TokenType == JsonToken.Null)
            {
                if (!ReflectionUtils.IsNullable(objectType))
                {
                    throw JsonSerializationException.Create(reader, "Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType));
                }

                return(null);
            }
            if (objectType == null && existingValue != null)
            {
                objectType = existingValue.GetType();
            }

            JsonContract contract = internalReader.GetContractSafe(objectType);

            UnityEngine.Object unityObject = null;

            if (!reader.MoveToContent())
            {
                throw JsonSerializationException.Create(reader, "No JSON content found.");
            }
            if (reader.TokenType == JsonToken.Null)
            {
                return(null);
            }
            else if (reader.TokenType == JsonToken.StartObject)
            {
                string id;
                string unityGuid;
                Type   resolvedObjectType = objectType;

                if (internalReader.Serializer.MetadataPropertyHandling == MetadataPropertyHandling.Ignore)
                {
                    // don't look for metadata properties
                    reader.ReadAndAssert();
                    id        = null;
                    unityGuid = null;
                }
                else if (internalReader.Serializer.MetadataPropertyHandling == MetadataPropertyHandling.ReadAhead)
                {
                    JTokenReader tokenReader = reader as JTokenReader;
                    if (tokenReader == null)
                    {
                        JToken t = JToken.ReadFrom(reader);
                        tokenReader                        = (JTokenReader)t.CreateReader();
                        tokenReader.Culture                = reader.Culture;
                        tokenReader.DateFormatString       = reader.DateFormatString;
                        tokenReader.DateParseHandling      = reader.DateParseHandling;
                        tokenReader.DateTimeZoneHandling   = reader.DateTimeZoneHandling;
                        tokenReader.FloatParseHandling     = reader.FloatParseHandling;
                        tokenReader.SupportMultipleContent = reader.SupportMultipleContent;

                        // start
                        tokenReader.ReadAndAssert();

                        reader = tokenReader;
                    }

                    object newValue;
                    if (internalReader.ReadMetadataPropertiesToken(tokenReader, ref resolvedObjectType, ref contract, null, null, null, existingValue, out newValue, out id, out unityGuid, out unityObject))
                    {
                        if (SceneReferenceResolver.Current != null && !string.IsNullOrEmpty(unityGuid) && !AssetReferenceResolver.Current.Contains(unityGuid))
                        {
                            if (SceneReferenceResolver.Current.Contains(unityGuid))
                            {
                                SceneReferenceResolver.Current.Set(unityGuid, unityObject);
                            }
                            else
                            {
                                SceneReferenceResolver.Current.Add(unityGuid, unityObject);
                            }
                        }
                        if (unityObject != null)
                        {
                            return(unityObject);
                        }
                        return(newValue);
                    }
                }
                else
                {
                    reader.ReadAndAssert();
                    object newValue;
                    if (internalReader.ReadMetadataProperties(reader, ref resolvedObjectType, ref contract, null, null, null, existingValue, out newValue, out id, out unityGuid, out unityObject))
                    {
                        if (SceneReferenceResolver.Current != null && !string.IsNullOrEmpty(unityGuid) && !AssetReferenceResolver.Current.Contains(unityGuid))
                        {
                            if (SceneReferenceResolver.Current.Contains(unityGuid))
                            {
                                SceneReferenceResolver.Current.Set(unityGuid, unityObject);
                            }
                            else
                            {
                                SceneReferenceResolver.Current.Add(unityGuid, unityObject);
                            }
                        }
                        if (unityObject != null)
                        {
                            return(unityObject);
                        }
                        return(newValue);
                    }
                }

                if (internalReader.HasNoDefinedType(contract))
                {
                    return(internalReader.CreateJObject(reader));
                }

                bool createdFromNonDefaultCreator = false;
                JsonObjectContract objectContract = (JsonObjectContract)contract;
                object             targetObject;

                // check that if type name handling is being used that the existing value is compatible with the specified type
                if (existingValue != null && (resolvedObjectType == objectType || resolvedObjectType.IsAssignableFrom(existingValue.GetType())))
                {
                    targetObject = existingValue;
                }
                else if (unityObject != null)
                {
                    targetObject = unityObject;
                }
                else
                {
                    targetObject = Create(reader, internalReader, objectContract, id, unityGuid, objectType, out createdFromNonDefaultCreator);
                }

                if (SceneReferenceResolver.Current != null && !string.IsNullOrEmpty(unityGuid) && !AssetReferenceResolver.Current.Contains(unityGuid))
                {
                    if (SceneReferenceResolver.Current.Contains(unityGuid))
                    {
                        SceneReferenceResolver.Current.Set(unityGuid, (UnityEngine.Object)targetObject);
                    }
                    else
                    {
                        SceneReferenceResolver.Current.Add(unityGuid, (UnityEngine.Object)targetObject);
                    }
                }

                // don't populate if read from non-default creator because the object has already been read
                if (createdFromNonDefaultCreator)
                {
                    return(targetObject);
                }
                internalReader.OnDeserializing(reader, contract, targetObject);
                bool referenceAdded = false;
                if (id != null && targetObject != null)
                {
                    internalReader.AddReference(reader, id, targetObject);
                    referenceAdded = true;
                }
                targetObject = Populate(contract, reader, objectType, targetObject, internalReader);
                if (id != null && targetObject != null && !referenceAdded)
                {
                    internalReader.AddReference(reader, id, targetObject);
                }
                internalReader.OnDeserialized(reader, contract, targetObject);
                return(targetObject);
            }
            else
            {
                throw JsonSerializationException.Create(reader, "Unexpected initial token '{0}' when populating object. Expected JSON object.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
            }
        }