private Base Dict2Base(Dictionary <string, object> dictObj)
        {
            String typeName = dictObj[TypeDiscriminator] as String;
            Type   type     = SerializationUtilities.GetType(typeName);
            Base   baseObj  = Activator.CreateInstance(type) as Base;

            dictObj.Remove(TypeDiscriminator);
            dictObj.Remove("__closure");

            Dictionary <string, PropertyInfo> staticProperties = SerializationUtilities.GetTypePropeties(typeName);
            List <MethodInfo> onDeserializedCallbacks          = SerializationUtilities.GetOnDeserializedCallbacks(typeName);

            foreach (KeyValuePair <string, object> entry in dictObj)
            {
                string lowerPropertyName = entry.Key.ToLower();
                if (staticProperties.ContainsKey(lowerPropertyName) && staticProperties[lowerPropertyName].CanWrite)
                {
                    PropertyInfo property = staticProperties[lowerPropertyName];
                    if (entry.Value == null)
                    {
                        // Check for JsonProperty(NullValueHandling = NullValueHandling.Ignore) attribute
                        JsonPropertyAttribute attr = property.GetCustomAttribute <JsonPropertyAttribute>(true);
                        if (attr != null && attr.NullValueHandling == NullValueHandling.Ignore)
                        {
                            continue;
                        }
                    }

                    Type   targetValueType = property.PropertyType;
                    object convertedValue;
                    bool   conversionOk = ValueConverter.ConvertValue(targetValueType, entry.Value, out convertedValue);
                    if (conversionOk)
                    {
                        property.SetValue(baseObj, convertedValue);
                    }
                    else
                    {
                        // Cannot convert the value in the json to the static property type
                        throw new Exception(String.Format("Cannot deserialize {0} to {1}", entry.Value.GetType().FullName, targetValueType.FullName));
                    }
                }
                else
                {
                    // No writable property with this name
                    CallSiteCache.SetValue(entry.Key, baseObj, entry.Value);
                }
            }

            foreach (MethodInfo onDeserialized in onDeserializedCallbacks)
            {
                onDeserialized.Invoke(baseObj, new object[] { null });
            }

            return(baseObj);
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
        {
            if (CancellationToken.IsCancellationRequested)
            {
                return(null); // Check for cancellation
            }

            if (reader.TokenType == JsonToken.Null)
            {
                return(null);
            }

            // Check if we passed in an array, rather than an object.
            // TODO: Test the following branch. It's not used anywhere at the moment, and the default serializer prevents it from
            // ever being used (only allows single object serialization)
            if (reader.TokenType == JsonToken.StartArray)
            {
                var list = new List <Base>();
                var jarr = JArray.Load(reader);

                foreach (var val in jarr)
                {
                    if (CancellationToken.IsCancellationRequested)
                    {
                        return(null); // Check for cancellation
                    }

                    var whatever = SerializationUtilities.HandleValue(val, serializer, CancellationToken);
                    list.Add(whatever as Base);
                }
                return(list);
            }

            if (CancellationToken.IsCancellationRequested)
            {
                return(null); // Check for cancellation
            }

            var jObject = JObject.Load(reader);

            if (jObject == null)
            {
                return(null);
            }

            var objType = jObject.GetValue(TypeDiscriminator);

            // Assume dictionary!
            if (objType == null)
            {
                var dict = new Dictionary <string, object>();

                foreach (var val in jObject)
                {
                    if (CancellationToken.IsCancellationRequested)
                    {
                        return(null); // Check for cancellation
                    }

                    dict[val.Key] = SerializationUtilities.HandleValue(val.Value, serializer, CancellationToken);
                }
                return(dict);
            }

            if (CancellationToken.IsCancellationRequested)
            {
                return(null); // Check for cancellation
            }

            var discriminator = Extensions.Value <string>(objType);

            // Check for references.
            if (discriminator == "reference")
            {
                var    id  = Extensions.Value <string>(jObject.GetValue("referencedId"));
                string str = "";

                if (ReadTransport != null)
                {
                    str = ReadTransport.GetObject(id);
                }
                else
                {
                    Log.CaptureAndThrow(new SpeckleException("Cannot resolve reference, no transport is defined."), level: Sentry.Protocol.SentryLevel.Warning);
                }

                if (str != null && str != "")
                {
                    jObject       = JObject.Parse(str);
                    discriminator = Extensions.Value <string>(jObject.GetValue(TypeDiscriminator));
                }
                else
                {
                    Log.CaptureAndThrow(new SpeckleException("Cannot resolve reference. The provided transport could not find it."), level: Sentry.Protocol.SentryLevel.Warning);
                }
            }

            var type = SerializationUtilities.GetType(discriminator);
            var obj  = existingValue ?? Activator.CreateInstance(type);

            var contract = (JsonDynamicContract)serializer.ContractResolver.ResolveContract(type);
            var used     = new HashSet <string>();

            // remove unsettable properties
            jObject.Remove(TypeDiscriminator);
            jObject.Remove("__closure");

            if (CancellationToken.IsCancellationRequested)
            {
                return(null); // Check for cancellation
            }

            foreach (var jProperty in jObject.Properties())
            {
                if (CancellationToken.IsCancellationRequested)
                {
                    return(null); // Check for cancellation
                }

                if (used.Contains(jProperty.Name))
                {
                    continue;
                }

                used.Add(jProperty.Name);

                // first attempt to find a settable property, otherwise fall back to a dynamic set without type
                JsonProperty property = contract.Properties.GetClosestMatchProperty(jProperty.Name);

                if (property != null && property.Writable && !property.Ignored)
                {
                    if (type == typeof(Abstract) && property.PropertyName == "base")
                    {
                        var propertyValue = SerializationUtilities.HandleAbstractOriginalValue(jProperty.Value, ((JValue)jObject.GetValue("assemblyQualifiedName")).Value as string, serializer);
                        property.ValueProvider.SetValue(obj, propertyValue);
                    }
                    else
                    {
                        var val = SerializationUtilities.HandleValue(jProperty.Value, serializer, CancellationToken, property);
                        property.ValueProvider.SetValue(obj, val);
                    }
                }
                else
                {
                    // dynamic properties
                    CallSiteCache.SetValue(jProperty.Name, obj, SerializationUtilities.HandleValue(jProperty.Value, serializer, CancellationToken));
                }
            }

            if (CancellationToken.IsCancellationRequested)
            {
                return(null); // Check for cancellation
            }

            TotalProcessedCount++;
            OnProgressAction?.Invoke("DS", 1);

            foreach (var callback in contract.OnDeserializedCallbacks)
            {
                callback(obj, serializer.Context);
            }

            return(obj);
        }