public static object FullDeserialize(this FullSerializationData data, bool forceReflected = false)
        {
            object instance = null;

            FullDeserializeInto(data, ref instance, forceReflected);
            return(instance);
        }
        public static void FullDeserializeInto(this FullSerializationData data, ref object instance, bool forceReflected = false)
        {
            try
            {
                if (string.IsNullOrEmpty(data.json))
                {
                    instance = null;
                    return;
                }

                var operation = BeginFullOperation();
                operation.objectReferences.AddRange(data.objectReferences);
                FullDeserializeJson(operation.serializer, data.json, ref instance, forceReflected);
                EndFullOperation(operation);
            }
            catch (Exception ex)
            {
                try
                {
                    Debug.LogWarning(data.GetDebugString("Deserialization Failure Data"), instance as UnityObject);
                }
                catch (Exception ex2)
                {
                    Debug.LogWarning("Failed to log deserialization failure data:\n" + ex2, instance as UnityObject);
                }

                throw new SerializationException($"Deserialization into '{instance?.GetType().ToString() ?? "null"}' failed.", ex);
            }
        }
        public static void OnBeforeSerializeImplementation <T>(T uo, ref FullSerializationData fullData, ref OdinSerializationData odinData, bool deserializationFailed) where T : UnityObject, ILudiqRootObject
        {
            // To prevent data loss, we don't serialize something that failed to deserialize in the first place
            if (deserializationFailed)
            {
                Debug.LogWarning($"Skipping serialization of {uo.ToSafeString()} to prevent data loss because it failed to deserialize.\n", uo);
                return;
            }

            // Run the user handler
            try
            {
                uo.OnBeforeSerialize();
            }
            catch (Exception ex)
            {
                Debug.LogError($"Failed to run OnBeforeSerialize on {uo.ToSafeString()}.\n{ex}", uo);
                return;
            }

            // Check if we are migrating legacy FS data into OS to notify the user
            var migrating = fullData.ContainsRealData && !odinData.ContainsRealData();

            try
            {
                // Serialize the data using OS only from now on
                odinData = uo.OdinSerialize(ref odinData);

                if (migrating)
                {
                    // Clear the legacy FS data so that it isn't used as a fallback during deserialization from now on.
                    fullData = default;

                    Debug.Log($"Migrated legacy serialization data on {uo.ToSafeString()} from Full Serializer to Odin Serializer.\n", uo);
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"Failed to serialize {uo.ToSafeString()} using Odin Serializer.\n{ex}", uo);
                return;
            }

            // Run the user handler
            try
            {
                uo.OnAfterSerialize();
            }
            catch (Exception ex)
            {
                Debug.LogError($"Failed to run OnAfterSerialize on {uo.ToSafeString()}.\n{ex}", uo);
                return;
            }
        }
        public static void ShowData(string title, FullSerializationData fullData, OdinSerializationData odinData)
        {
            var guid     = Guid.NewGuid();
            var fileName = string.IsNullOrEmpty(title) ? guid.ToString() : title + "." + guid;

            var fullDataPath = Path.GetTempPath() + fileName + ".full.txt";
            var odinDataPath = Path.GetTempPath() + fileName + ".odin.txt";

            File.WriteAllText(fullDataPath, GetDebugString(fullData));
            File.WriteAllText(odinDataPath, GetDebugString(odinData));

            Process.Start(fullDataPath);
            Process.Start(odinDataPath);
        }
        public static FullSerializationData FullSerialize(this object value, bool forceReflected = false)
        {
            try
            {
                var operation        = BeginFullOperation();
                var json             = FullSerializeJson(operation.serializer, value, forceReflected);
                var objectReferences = operation.objectReferences.ToArray();
                var data             = new FullSerializationData(json, objectReferences);
                EndFullOperation(operation);

#if DEBUG_SERIALIZATION
                Debug.Log(data.ToString($"<color=#88FF00>Serialized: <b>{value?.GetType().Name ?? "null"} [{value?.GetHashCode().ToString() ?? "N/A"}]</b></color>"));
#endif

                return(data);
            }
            catch (Exception ex)
            {
                throw new SerializationException($"Serialization of '{value?.GetType().ToString() ?? "null"}' failed.", ex);
            }
        }
        public static string GetDebugString(this FullSerializationData data, string title = null)
        {
            using (var writer = new StringWriter())
            {
                writer.WriteLine("Full Serializer Data");

                if (!string.IsNullOrEmpty(title))
                {
                    writer.WriteLine(title);
                }

                writer.WriteLine();

                writer.WriteLine("Object References: ");

                WriteObjectReferenceDebug(writer, data.objectReferences);

                writer.WriteLine();

                writer.WriteLine("JSON: ");

                if (data.json == null)
                {
                    writer.WriteLine("(Null)");
                }
                else if (data.json == string.Empty)
                {
                    writer.WriteLine("(Empty)");
                }
                else
                {
                    writer.WriteLine(PrettyPrint(data.json));
                }

                return(writer.ToString());
            }
        }
        public static void OnAfterDeserializeImplementation <T>(T uo, FullSerializationData fullData, OdinSerializationData odinData, ref bool deserializationFailed) where T : UnityObject, ILudiqRootObject
        {
            // Set a flag to indicate to our API checker that Unity calls are forbidden
            isUnityDeserializing = true;

            try
            {
                object _this = uo;

                // If we don't reach the complete end of the process for any reason, the failure flag will be set.
                deserializationFailed = true;

                // Run the user callback
                try
                {
                    uo.OnBeforeDeserialize();
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Failed to run OnBeforeDeserialize on {uo.ToSafeString()}.\n{ex}", uo);
                    return;
                }

                // Deserialize with OS data if it is available
                if (odinData.ContainsRealData())
                {
                    try
                    {
                        odinData.OdinDeserializeInto(ref _this);
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError($"Failed to deserialize {uo.ToSafeString()} using Odin Serializer.\n{ex}", uo);
                        return;
                    }
                }
                // In theory, there shouldn't be a case where we have both OS and FS data because we clear the FS data on a successful OS deserialization.
                // Just to be absolutely safe we don't rollback to earlier FS data though, we're being mutually exclusive on deserialization too.
                else
                {
                    // Fallback to legacy FS data
                    try
                    {
                        fullData.FullDeserializeInto(ref _this, true);
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError($"Failed to deserialize {uo.ToSafeString()} using legacy Full Serializer data.\n{ex}", uo);
                        return;
                    }
                }

                // Run the user callback
                try
                {
                    uo.OnAfterDeserialize();
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Failed to run OnAfterDeserialize on {uo.ToSafeString()}.\n{ex}", uo);
                    return;
                }

                // We managed to execute everything successfully, clear the failure flag.
                deserializationFailed = false;
            }
            finally
            {
                // Clear our API lock regardless of what happened
                isUnityDeserializing = false;
            }
        }