#region License // The MIT License (MIT) // // Copyright (c) 2020 Wanzyee Studio // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #endregion using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Newtonsoft.Json.Converters; using Newtonsoft.Json.UnityConverters.Configuration; using Newtonsoft.Json.UnityConverters.Helpers; using Newtonsoft.Json.UnityConverters.Utility; using UnityEngine; namespace Newtonsoft.Json.UnityConverters { public static class UnityConverterInitializer { private static bool _shouldAddConvertsToDefaultSettings = true; /// /// The default given by Newtonsoft.Json-for-Unity.Converters /// /// /// /// All its properties stay default, but the Converters includes below: /// 1. Any custom has constructor without parameters. /// 2. Any Newtonsoft.Json.JsonConverter from Newtonsoft.Json.UnityConverters. /// 3. . /// 4. . /// public static JsonSerializerSettings defaultUnityConvertersSettings { get; set; } = CreateJsonSettingsWithFreslyLoadedConfig(); /// /// If set to false then will not try to inject converters on init via /// the default settings property on JsonConvert /// . /// Default is true. /// public static bool shouldAddConvertsToDefaultSettings { get => _shouldAddConvertsToDefaultSettings; set { _shouldAddConvertsToDefaultSettings = value; UpdateDefaultSettings(); } } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] #pragma warning disable IDE0051 // Remove unused private members #if UNITY_EDITOR [UnityEditor.InitializeOnLoadMethod] #endif internal static void Init() #pragma warning restore IDE0051 // Remove unused private members { UpdateDefaultSettings(); } private static void UpdateDefaultSettings() { if (shouldAddConvertsToDefaultSettings) { if (JsonConvert.DefaultSettings == null) { JsonConvert.DefaultSettings = GetExistingDefaultUnitySettings; } } else { if (JsonConvert.DefaultSettings == GetExistingDefaultUnitySettings) { JsonConvert.DefaultSettings = null; } } } /// /// Refreshes the settings that are found in the Resources folder /// (specified in ); /// public static void RefreshSettingsFromConfig() { defaultUnityConvertersSettings = CreateJsonSettingsWithFreslyLoadedConfig(); } internal static JsonSerializerSettings GetExistingDefaultUnitySettings() { return defaultUnityConvertersSettings; } private static JsonSerializerSettings CreateJsonSettingsWithFreslyLoadedConfig() { var config = Resources.Load(UnityConvertersConfig.PATH_FOR_RESOURCES_LOAD); if (!config) { config = ScriptableObject.CreateInstance(); } var settings = new JsonSerializerSettings { Converters = CreateConverters(config), }; if (config.useUnityContractResolver) { settings.ContractResolver = new UnityTypeContractResolver(); } return settings; } /// /// Create the converter instances. /// /// The converters. internal static List CreateConverters(UnityConvertersConfig config) { var converterTypes = new List(); var grouping = FindGroupedConverters(config); converterTypes.AddRange(ApplyConfigFilter(grouping.outsideConverters, config.useAllOutsideConverters, config.outsideConverters)); converterTypes.AddRange(ApplyConfigFilter(grouping.unityConverters, config.useAllUnityConverters, config.unityConverters)); converterTypes.AddRange(ApplyConfigFilter(grouping.jsonNetConverters, config.useAllJsonNetConverters, config.jsonNetConverters)); var result = new List(); result.AddRange(converterTypes.Select(CreateConverter)); return result; } internal static ConverterGrouping FindGroupedConverters(UnityConvertersConfig config) { if (config.autoSyncConverters) { return ConverterGrouping.Create(FindConverters()); } return new ConverterGrouping { outsideConverters = config.outsideConverters.Select(x => GetTypeOrLog(x.converterName, x.converterAssembly)).WhereNotNullRef().ToList(), unityConverters = config.unityConverters.Select(x => GetTypeOrLog(x.converterName, x.converterAssembly)).WhereNotNullRef().ToList(), jsonNetConverters = config.jsonNetConverters.Select(x => GetTypeOrLog(x.converterName, x.converterAssembly)).WhereNotNullRef().ToList(), }; } private static Type GetTypeOrLog(string name, string assemblyName) { var type = TypeCache.FindType(name, assemblyName); if (type == null) { Debug.LogWarning($"Failed to lookup JsonConverter type. Ignoring it. Type name: \"{name}\""+ "\nTo fix this warning by going to \"Edit > Json.NET converter settings...\", and then the editor will update your config for you."); } return type; } /// /// Finds all the valid converter types inside the Newtonsoft.Json assembly. /// /// The types. internal static IEnumerable FindConverters() { #if UNITY_2019_2_OR_NEWER && UNITY_EDITOR var types = UnityEditor.TypeCache.GetTypesDerivedFrom(); #else var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(dll => dll.GetLoadableTypes()); #endif return FilterToJsonConvertersAndOrder(types); } private static IEnumerable FilterToJsonConvertersAndOrder(IEnumerable types) { return types .Where(type => typeof(JsonConverter).IsAssignableFrom(type) && !type.IsAbstract && !type.IsGenericTypeDefinition && type.GetConstructor(Array.Empty()) != null ) .OrderBy(type => type.FullName); } /// /// Meant to be coupled to the configs from . /// When the use all argument is false, the intersection between /// the types given from the first argument and the enumeration of /// converter configs are returned. /// private static IEnumerable ApplyConfigFilter(IEnumerable types, bool useAll, IEnumerable configs) { if (useAll) { return types; } var typesOfEnabledThroughConfig = configs .Where(o => o.enabled) .Select(o => Utility.TypeCache.FindType(o.converterName, o.converterAssembly)) .Where(o => o != null); var hashMap = new HashSet(typesOfEnabledThroughConfig); return types.Intersect(hashMap); } /// /// Try to create the converter of specified type. /// /// The converter. /// Type. [return: MaybeNull] private static JsonConverter CreateConverter(Type jsonConverterType) { try { return (JsonConverter)Activator.CreateInstance(jsonConverterType); } catch (Exception exception) { Debug.LogErrorFormat("Cannot create JsonConverter '{0}':\n{1}", jsonConverterType?.FullName, exception); } return null; } } }