public Base Deserialize(String rootObjectJson)
        {
            if (Busy)
            {
                throw new Exception("A deserializer instance can deserialize only 1 object at a time. Consider creating multiple deserializer instances");
            }
            try
            {
                Busy = true;
                DeserializedObjects = new Dictionary <string, object>();
                WorkerThreads       = new DeserializationWorkerThreads(this);
                WorkerThreads.Start();

                List <(string, int)> closures = GetClosures(rootObjectJson);
                closures.Sort((a, b) => b.Item2.CompareTo(a.Item2));
                foreach (var closure in closures)
                {
                    string objId   = closure.Item1;
                    string objJson = ReadTransport.GetObject(objId);
                    object deserializedOrPromise = DeserializeTransportObjectProxy(objJson);
                    lock (DeserializedObjects)
                    {
                        DeserializedObjects[objId] = deserializedOrPromise;
                    }
                }

                object ret = DeserializeTransportObject(rootObjectJson);
                return(ret as Base);
            }
            finally
            {
                DeserializedObjects = null;
                WorkerThreads.Dispose();
                WorkerThreads = null;
                Busy          = false;
            }
        }
        public object ConvertJsonElement(JToken doc)
        {
            if (CancellationToken.IsCancellationRequested)
            {
                return(null); // Check for cancellation
            }

            switch (doc.Type)
            {
            case JTokenType.Undefined:
            case JTokenType.Null:
            case JTokenType.None:
                return(null);

            case JTokenType.Boolean:
                return((bool)doc);

            case JTokenType.Integer:
                return((long)doc);

            case JTokenType.Float:
                return((double)doc);

            case JTokenType.String:
                return((string)doc);

            case JTokenType.Date:
                return((DateTime)doc);

            case JTokenType.Array:
                JArray        docAsArray   = (JArray)doc;
                List <object> jsonList     = new List <object>(docAsArray.Count);
                int           retListCount = 0;
                foreach (JToken value in docAsArray)
                {
                    object convertedValue = ConvertJsonElement(value);
                    retListCount += (convertedValue is DataChunk) ? ((DataChunk)convertedValue).data.Count : 1;
                    jsonList.Add(convertedValue);
                }

                List <object> retList = new List <object>(retListCount);
                foreach (object jsonObj in jsonList)
                {
                    if (jsonObj is DataChunk)
                    {
                        retList.AddRange(((DataChunk)jsonObj).data);
                    }
                    else
                    {
                        retList.Add(jsonObj);
                    }
                }

                return(retList);

            case JTokenType.Object:
                Dictionary <string, object> dict = new Dictionary <string, object>();

                foreach (JToken propJToken in doc)
                {
                    JProperty prop = (JProperty)propJToken;
                    if (prop.Name == "__closure")
                    {
                        continue;
                    }
                    dict[prop.Name] = ConvertJsonElement(prop.Value);
                }

                if (!dict.ContainsKey(TypeDiscriminator))
                {
                    return(dict);
                }

                if ((dict[TypeDiscriminator] as String) == "reference" && dict.ContainsKey("referencedId"))
                {
                    string objId        = dict["referencedId"] as String;
                    object deserialized = null;
                    lock (DeserializedObjects)
                    {
                        if (DeserializedObjects.ContainsKey(objId))
                        {
                            deserialized = DeserializedObjects[objId];
                        }
                    }
                    if (deserialized != null && deserialized is Task <object> )
                    {
                        deserialized = ((Task <object>)deserialized).Result;
                        lock (DeserializedObjects)
                        {
                            DeserializedObjects[objId] = deserialized;
                        }
                    }

                    if (deserialized != null)
                    {
                        return(deserialized);
                    }

                    // This reference was not already deserialized. Do it now in sync mode
                    string objectJson = ReadTransport.GetObject(objId);
                    deserialized = DeserializeTransportObject(objectJson);
                    lock (DeserializedObjects)
                    {
                        DeserializedObjects[objId] = deserialized;
                    }
                    return(deserialized);
                }

                return(Dict2Base(dict));

            default:
                throw new Exception("Json value not supported: " + doc.Type.ToString());
            }
        }
        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);
        }