Ejemplo n.º 1
0
 /// <summary>
 /// Gets the id (a unique hash) of this object. ⚠️ This method fully serializes the object, which in the case of large objects (with many sub-objects), has a tangible cost. Avoid using it!
 /// <para><b>Hint:</b> Objects that are retrieved/pulled from a server/local cache do have an id (hash) property pre-populated.</para>
 /// <para><b>Note:</b>The hash of a decomposed object differs from the hash of a non-decomposed object.</para>
 /// </summary>
 /// <param name="decompose">If true, will decompose the object in the process of hashing.</param>
 /// <returns></returns>
 public string GetId(bool decompose = false, SerializerVersion serializerVersion = SerializerVersion.V2)
 {
     if (serializerVersion == SerializerVersion.V1)
     {
         var(s, t) = Operations.GetSerializerInstance();
         if (decompose)
         {
             s.WriteTransports = new List <ITransport>()
             {
                 new MemoryTransport()
             };
         }
         var obj = JsonConvert.SerializeObject(this, t);
         return(JObject.Parse(obj).GetValue("id").ToString());
     }
     else
     {
         var s = new Serialisation.BaseObjectSerializerV2();
         if (decompose)
         {
             s.WriteTransports = new List <ITransport>()
             {
                 new MemoryTransport()
             };
         }
         var obj = s.Serialize(this);
         return(JObject.Parse(obj).GetValue("id").ToString());
     }
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Deserialize a DynamicSchema record stored as binary data.
        /// </summary>
        /// <param name="binary">raw binary record data</param>
        /// <returns>dictionary of field ID to field value</returns>
        public static Dictionary <int, object> DeserializeRecord(byte[] binary)
        {
            using (MemoryStream ms = new MemoryStream(binary))
            {
                using (BinaryReader br = getBinaryReader(ms))
                {
                    // Read version code
                    byte serializerVersionCode          = br.ReadByte();
                    SerializerVersion serializerVersion = (SerializerVersion)serializerVersionCode;

                    // Determine the correct deserializer
                    DynamicSchemaDeserializer deserializer = null;
                    switch (serializerVersion)
                    {
                    case SerializerVersion.VER_3_12:
                        deserializer = new Deserializers.Deserializer0312();
                        break;
                    }

                    if (deserializer == null)
                    {
                        throw new NotSupportedException(String.Format("Serializer version {0} not supported", serializerVersionCode));
                    }

                    // Perform deserialization
                    return(deserializer.DeserializeRecord(br));
                }
            }
        }
Ejemplo n.º 3
0
        protected override void BeginWriteRoot(string inRootName)
        {
            m_Stack.Clear();
            m_Current = m_Root = m_Document.CreateElement(SanitizeElementName(inRootName));
            m_Document.AppendChild(m_Root);

            XmlAttribute serializerVersionNode = m_Document.CreateAttribute(SERIALIZER_VERSION_KEY);

            serializerVersionNode.InnerText = SerializerVersion.ToString();
        }
 public static List <Base> DeserializeArray(string objectArr, SerializerVersion serializerVersion = SerializerVersion.V2)
 {
     if (serializerVersion == SerializerVersion.V1)
     {
         var(_, settings) = GetSerializerInstance();
         return(JsonConvert.DeserializeObject <List <Base> >(objectArr, settings));
     }
     else
     {
         var           deserializer = new BaseObjectDeserializerV2();
         List <object> deserialized = deserializer.DeserializeTransportObject(objectArr) as List <object>;
         List <Base>   ret          = new List <Base>();
         foreach (object obj in deserialized)
         {
             ret.Add((Base)obj);
         }
         return(ret);
     }
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Deserialize a DynamicSchema field stored as binary data.
        /// </summary>
        /// <param name="br">binary data reader</param>
        /// <returns>deserialized field data</returns>
        public static object DeserializeField(BinaryReader br)
        {
            // Read version code
            byte serializerVersionCode          = br.ReadByte();
            SerializerVersion serializerVersion = (SerializerVersion)serializerVersionCode;

            // Determine the correct deserializer
            DynamicSchemaDeserializer deserializer = null;

            switch (serializerVersion)
            {
            case SerializerVersion.VER_3_12:
                deserializer = new Deserializers.Deserializer0312();
                break;
            }

            if (deserializer == null)
            {
                throw new NotSupportedException(String.Format("Serializer version {0} not supported", serializerVersionCode));
            }

            // Perform deserialization
            return(deserializer.DeserializeField(br));
        }
 /// <summary>
 /// Deserializes a given object. Note: if you want to pull an object from a Speckle Transport or Server, please use any of the <see cref="Receive(string, Transports.ITransport, Transports.ITransport, Action{System.Collections.Concurrent.ConcurrentDictionary{string, int}})"/>.
 /// </summary>
 /// <param name="object">The json string representation of a speckle object that you want to deserialise.</param>
 /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
 /// <returns></returns>
 public static Base Deserialize(string @object, CancellationToken cancellationToken, SerializerVersion serializerVersion = SerializerVersion.V2)
 {
     if (serializerVersion == SerializerVersion.V1)
     {
         var(serializer, settings)    = GetSerializerInstance();
         serializer.CancellationToken = cancellationToken;
         return(JsonConvert.DeserializeObject <Base>(@object, settings));
     }
     else
     {
         var deserializer = new BaseObjectDeserializerV2();
         deserializer.CancellationToken = cancellationToken;
         return(deserializer.Deserialize(@object));
     }
 }
        /// <summary>
        /// Serializes a given object. Note: if you want to save and persist an object to Speckle Transport or Server, please use any of the "Send" methods. See <see cref="Send(Base, List{Transports.ITransport}, bool, Action{System.Collections.Concurrent.ConcurrentDictionary{string, int}}, Action{string, Exception})"/>.
        /// </summary>
        /// <param name="object"></param>
        /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
        /// <returns>A json string representation of the object.</returns>
        public static string Serialize(Base @object, CancellationToken cancellationToken, SerializerVersion serializerVersion = SerializerVersion.V2)
        {
            if (serializerVersion == SerializerVersion.V1)
            {
                var(serializer, settings)    = GetSerializerInstance();
                serializer.CancellationToken = cancellationToken;

                return(JsonConvert.SerializeObject(@object, settings));
            }
            else
            {
                var serializer = new BaseObjectSerializerV2();
                serializer.CancellationToken = cancellationToken;
                return(serializer.Serialize(@object));
            }
        }
        /// <summary>
        /// Receives an object from a transport.
        /// </summary>
        /// <param name="objectId"></param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to send notice of cancellation.</param>
        /// <param name="remoteTransport">The transport to receive from.</param>
        /// <param name="localTransport">Leave null to use the default cache.</param>
        /// <param name="onProgressAction">Action invoked on progress iterations.</param>
        /// <param name="onErrorAction">Action invoked on internal errors.</param>
        /// <param name="onTotalChildrenCountKnown">Action invoked once the total count of objects is known.</param>
        /// <returns></returns>
        public static async Task <Base> Receive(string objectId, CancellationToken cancellationToken, ITransport remoteTransport = null, ITransport localTransport = null, Action <ConcurrentDictionary <string, int> > onProgressAction = null, Action <string, Exception> onErrorAction = null, Action <int> onTotalChildrenCountKnown = null, bool disposeTransports = false, SerializerVersion serializerVersion = SerializerVersion.V2)
        {
            Log.AddBreadcrumb("Receive");

            BaseObjectSerializer     serializer   = null;
            JsonSerializerSettings   settings     = null;
            BaseObjectDeserializerV2 serializerV2 = null;

            if (serializerVersion == SerializerVersion.V1)
            {
                (serializer, settings) = GetSerializerInstance();
            }
            else
            {
                serializerV2 = new BaseObjectDeserializerV2();
            }

            var localProgressDict      = new ConcurrentDictionary <string, int>();
            var internalProgressAction = GetInternalProgressAction(localProgressDict, onProgressAction);

            var hasUserProvidedLocalTransport = localTransport != null;

            localTransport = localTransport != null ? localTransport : new SQLiteTransport();
            localTransport.OnErrorAction     = onErrorAction;
            localTransport.OnProgressAction  = internalProgressAction;
            localTransport.CancellationToken = cancellationToken;

            if (serializerVersion == SerializerVersion.V1)
            {
                serializer.ReadTransport     = localTransport;
                serializer.OnProgressAction  = internalProgressAction;
                serializer.OnErrorAction     = onErrorAction;
                serializer.CancellationToken = cancellationToken;
            }
            else
            {
                serializerV2.ReadTransport     = localTransport;
                serializerV2.OnProgressAction  = internalProgressAction;
                serializerV2.OnErrorAction     = onErrorAction;
                serializerV2.CancellationToken = cancellationToken;
            }

            // First we try and get the object from the local transport. If it's there, we assume all its children are there, and proceed with deserialisation.
            // This assumption is hard-wired into the SDK. Read below.
            var objString = localTransport.GetObject(objectId);

            if (objString != null)
            {
                // Shoot out the total children count
                var partial = JsonConvert.DeserializeObject <Placeholder>(objString);
                if (partial.__closure != null)
                {
                    onTotalChildrenCountKnown?.Invoke(partial.__closure.Count);
                }

                Base localRes;
                if (serializerVersion == SerializerVersion.V1)
                {
                    localRes = JsonConvert.DeserializeObject <Base>(objString, settings);
                }
                else
                {
                    localRes = serializerV2.Deserialize(objString);
                }

                if ((disposeTransports || !hasUserProvidedLocalTransport) && localTransport is IDisposable dispLocal)
                {
                    dispLocal.Dispose();
                }
                if (disposeTransports && remoteTransport != null && remoteTransport is IDisposable dispRempte)
                {
                    dispRempte.Dispose();
                }

                return(localRes);
            }
            else if (remoteTransport == null)
            {
                throw new SpeckleException($"Could not find specified object using the local transport, and you didn't provide a fallback remote from which to pull it.", level: SentryLevel.Error);
            }

            // If we've reached this stage, it means that we didn't get a local transport hit on our object, so we will proceed to get it from the provided remote transport.
            // This is done by copying itself and all its children from the remote transport into the local one.
            remoteTransport.OnErrorAction     = onErrorAction;
            remoteTransport.OnProgressAction  = internalProgressAction;
            remoteTransport.CancellationToken = cancellationToken;

            Log.AddBreadcrumb("RemoteHit");
            objString = await remoteTransport.CopyObjectAndChildren(objectId, localTransport, onTotalChildrenCountKnown);

            // Wait for the local transport to finish "writing" - in this case, it signifies that the remote transport has done pushing copying objects into it. (TODO: I can see some scenarios where latency can screw things up, and we should rather wait on the remote transport).
            await localTransport.WriteComplete();

            // Proceed to deserialise the object, now safely knowing that all its children are present in the local (fast) transport.

            Base res;

            if (serializerVersion == SerializerVersion.V1)
            {
                res = JsonConvert.DeserializeObject <Base>(objString, settings);
            }
            else
            {
                res = serializerV2.Deserialize(objString);
            }

            if ((disposeTransports || !hasUserProvidedLocalTransport) && localTransport is IDisposable dl)
            {
                dl.Dispose();
            }
            if (disposeTransports && remoteTransport is IDisposable dr)
            {
                dr.Dispose();
            }

            return(res);

            // Summary:
            // Basically, receiving an object (and all its subchildren) operates with two transports, one that is potentially slow, and one that is fast.
            // The fast transport ("localTransport") is used syncronously inside the deserialisation routine to get the value of nested references and set them. The slow transport ("remoteTransport") is used to get the raw data and populate the local transport with all necessary data for a successful deserialisation of the object.
            // Note: if properly implemented, there is no hard distinction between what is a local or remote transport; it's still just a transport. So, for example, if you want to receive an object without actually writing it first to a local transport, you can just pass a Server/S3 transport as a local transport.
            // This is not reccommended, but shows what you can do. Another tidbit: the local transport does not need to be disk-bound; it can easily be an in memory transport. In memory transports are the fastest ones, but they're of limited use for more
        }
 /// <summary>
 /// Receives an object from a transport.
 /// </summary>
 /// <param name="objectId"></param>
 /// <param name="remoteTransport">The transport to receive from.</param>
 /// <param name="localTransport">Leave null to use the default cache.</param>
 /// <param name="onProgressAction">Action invoked on progress iterations.</param>
 /// <param name="onErrorAction">Action invoked on internal errors.</param>
 /// <param name="onTotalChildrenCountKnown">Action invoked once the total count of objects is known.</param>
 /// <returns></returns>
 public static Task <Base> Receive(string objectId, ITransport remoteTransport = null, ITransport localTransport = null, Action <ConcurrentDictionary <string, int> > onProgressAction = null, Action <string, Exception> onErrorAction = null, Action <int> onTotalChildrenCountKnown = null, bool disposeTransports = false, SerializerVersion serializerVersion = SerializerVersion.V2)
 {
     return(Receive(
                objectId,
                CancellationToken.None,
                remoteTransport,
                localTransport,
                onProgressAction,
                onErrorAction,
                onTotalChildrenCountKnown,
                disposeTransports,
                serializerVersion
                ));
 }
Ejemplo n.º 10
0
        /// <summary>
        /// Sends an object via the provided transports. Defaults to the local cache.
        /// </summary>
        /// <param name="object">The object you want to send.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to send notice of cancellation.</param>
        /// <param name="transports">Where you want to send them.</param>
        /// <param name="useDefaultCache">Toggle for the default cache. If set to false, it will only send to the provided transports.</param>
        /// <param name="onProgressAction">Action that gets triggered on every progress tick (keeps track of all transports).</param>
        /// <param name="onErrorAction">Use this to capture and handle any errors from within the transports.</param>
        /// <returns>The id (hash) of the object.</returns>
        public static async Task <string> Send(Base @object, CancellationToken cancellationToken, List <ITransport> transports = null, bool useDefaultCache = true, Action <ConcurrentDictionary <string, int> > onProgressAction = null, Action <string, Exception> onErrorAction = null, bool disposeTransports = false, SerializerVersion serializerVersion = SerializerVersion.V2)
        {
            Log.AddBreadcrumb("Send");

            if (transports == null)
            {
                transports = new List <ITransport>();
            }

            if (transports.Count == 0 && useDefaultCache == false)
            {
                throw new SpeckleException($"You need to provide at least one transport: cannot send with an empty transport list and no default cache.", level: SentryLevel.Error);
            }

            if (useDefaultCache)
            {
                transports.Insert(0, new SQLiteTransport()
                {
                    TransportName = "LC"
                });
            }

            BaseObjectSerializer   serializer   = null;
            JsonSerializerSettings settings     = null;
            BaseObjectSerializerV2 serializerV2 = null;

            if (serializerVersion == SerializerVersion.V1)
            {
                (serializer, settings) = GetSerializerInstance();
            }
            else
            {
                serializerV2 = new BaseObjectSerializerV2();
            }

            var localProgressDict      = new ConcurrentDictionary <string, int>();
            var internalProgressAction = Operations.GetInternalProgressAction(localProgressDict, onProgressAction);

            if (serializerVersion == SerializerVersion.V1)
            {
                serializer.OnProgressAction  = internalProgressAction;
                serializer.CancellationToken = cancellationToken;
                serializer.OnErrorAction     = onErrorAction;
            }
            else
            {
                serializerV2.OnProgressAction  = internalProgressAction;
                serializerV2.CancellationToken = cancellationToken;
                serializerV2.OnErrorAction     = onErrorAction;
            }

            foreach (var t in transports)
            {
                t.OnProgressAction  = internalProgressAction;
                t.CancellationToken = cancellationToken;
                t.OnErrorAction     = onErrorAction;
                t.BeginWrite();

                if (serializerVersion == SerializerVersion.V1)
                {
                    serializer.WriteTransports.Add(t);
                }
                else
                {
                    serializerV2.WriteTransports.Add(t);
                }
            }

            string      obj;
            List <Task> transportAwaits;

            if (serializerVersion == SerializerVersion.V1)
            {
                obj             = JsonConvert.SerializeObject(@object, settings);
                transportAwaits = serializer.WriteTransports.Select(t => t.WriteComplete()).ToList();
            }
            else
            {
                obj             = serializerV2.Serialize(@object);
                transportAwaits = serializerV2.WriteTransports.Select(t => t.WriteComplete()).ToList();
            }

            if (cancellationToken.IsCancellationRequested)
            {
                return(null);
            }

            await Task.WhenAll(transportAwaits).ConfigureAwait(false);

            foreach (var t in transports)
            {
                t.EndWrite();
                if (useDefaultCache && t is SQLiteTransport lc && lc.TransportName == "LC")
                {
                    lc.Dispose(); continue;
                }
                if (disposeTransports && t is IDisposable disp)
                {
                    disp.Dispose();
                }
            }

            if (cancellationToken.IsCancellationRequested)
            {
                return(null);
            }

            var hash = JObject.Parse(obj).GetValue("id").ToString();

            return(hash);
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Sends an object via the provided transports. Defaults to the local cache.
 /// </summary>
 /// <param name="object">The object you want to send.</param>
 /// <param name="transports">Where you want to send them.</param>
 /// <param name="useDefaultCache">Toggle for the default cache. If set to false, it will only send to the provided transports.</param>
 /// <param name="onProgressAction">Action that gets triggered on every progress tick (keeps track of all transports).</param>
 /// <param name="onErrorAction">Use this to capture and handle any errors from within the transports.</param>
 /// <returns>The id (hash) of the object.</returns>
 public static Task <string> Send(Base @object, List <ITransport> transports = null, bool useDefaultCache = true, Action <ConcurrentDictionary <string, int> > onProgressAction = null, Action <string, Exception> onErrorAction = null, bool disposeTransports = false, SerializerVersion serializerVersion = SerializerVersion.V2)
 {
     return(Send(
                @object,
                CancellationToken.None,
                transports,
                useDefaultCache,
                onProgressAction,
                onErrorAction,
                disposeTransports,
                serializerVersion
                ));
 }