using System; using System.Collections.Generic; using UnityEngine; namespace PrefsGUI.Utility { /// /// JsonUtility extension to convert any type. /// The original JsonUtility converts public fields of objects to Json, /// so primitive types, etc. are not supported. /// public static class JsonUtilityEx { public static T FromJson(string json) => (T)FromJson(json, typeof(T)); public static object FromJson(string json, Type type) { object ret; if (ValueWrapper.NeedWrap(type)) { var wrapperType = ValueWrapper.GetWrapperType(type); var wrapper = JsonUtility.FromJson(json, wrapperType); ret = (wrapper as ValueWrapper)?.obj; } else { ret = JsonUtility.FromJson(json, type); } return ret; } public static string ToJson(object obj, bool prettyPrint = false) { return JsonUtility.ToJson(WrapObject(obj), prettyPrint); } private static object WrapObject(object obj) { if ( obj != null) { var type = obj.GetType(); if (ValueWrapper.NeedWrap(type)) { obj = Activator.CreateInstance(ValueWrapper.GetWrapperType(type), obj); } } return obj; } internal abstract class ValueWrapper { private static readonly HashSet needWrapTypes = new() { typeof(string), typeof(Vector2Int), typeof(Vector3Int), typeof(Rect), typeof(RectOffset), typeof(Bounds), typeof(BoundsInt), typeof(Gradient) }; private static readonly Dictionary needWrapTable = new(); public static Type GetWrapperType(Type type) => typeof(ValueWrapper<>).MakeGenericType(type); public static bool NeedWrap(Type type) { if (!needWrapTable.TryGetValue(type, out var ret)) { ret = type.IsPrimitive || needWrapTypes.Contains(type) || type.IsArray || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)); needWrapTable[type] = ret; } return ret; } public abstract object obj { get; } } private class ValueWrapper : ValueWrapper { // [public/not readonly] to be target to JsonUtility serialization // ReSharper disable once MemberCanBePrivate.Local // ReSharper disable once FieldCanBeMadeReadOnly.Local public T value; public override object obj => value; public ValueWrapper(T value) { this.value = value; } } } }