private void ApplyObjectMembers([NotNull] object obj, [NotNull] Type containerType, [NotNull] IReadOnlyDictionary <string, object> container, [NotNull] ScriptableObjectAttribute options, [NotNull, ItemNotNull] PropertyInfo[] properties, [NotNull, ItemNotNull] FieldInfo[] fields, [CanBeNull] INamingConvention naming, int level) { foreach (var kv in container) { if (level == 0 && FilteredNames.Contains(kv.Key)) { continue; } var setter = FindSetterByName(containerType, properties, fields, kv.Key, naming); if (!setter.IsValid) { if (options.ThrowOnUnmatched) { throw new SerializationException(); } else { continue; } } var acceptedType = setter.GetValueType(); var rawValue = DeserializeValue(kv.Value, acceptedType, level); var rawValueType = NotNullTypeOf(rawValue); SetValue(setter, obj, rawValue, rawValueType); } }
private object ApplyObjectMembers([CanBeNull] object obj, [NotNull] Dictionary <string, object> container, [NotNull] ScriptableObjectAttribute options, [CanBeNull] INamingConvention naming, int level) { foreach (var kv in container) { if (level == 0 && FilteredNames.Contains(kv.Key)) { continue; } var setter = FindSetterByName(kv.Key, naming); if (!setter.IsValid) { if (options.ThrowOnUnmatched) { throw new SerializationException(); } else { continue; } } var acceptedType = setter.GetValueType(); var rawValue = DeserializeValue(kv.Value, acceptedType, level); var rawValueType = SerializingHelper.NonNullTypeOf(rawValue); obj = SetValue(setter, obj, rawValue, rawValueType); } return(obj); }
private object Deserialize([NotNull] IReadOnlyDictionary <string, object> container, [NotNull] Type type) { if (container == null) { throw new ArgumentNullException(nameof(container)); } var monoBehaviorAttr = type.GetCustomAttribute <MonoBehaviourAttribute>() ?? DefaultClassOptions; var allProperties = type.GetProperties(InternalBindings); var allFields = type.GetFields(InternalBindings).Where(field => { var compilerGenerated = field.GetCustomAttribute <CompilerGeneratedAttribute>(); // Filter out compiler generated fields. return(compilerGenerated == null); }); IReadOnlyList <PropertyInfo> properties; IReadOnlyList <FieldInfo> fields; switch (monoBehaviorAttr.PopulationStrategy) { case PopulationStrategy.OptIn: properties = allProperties.Where(prop => prop.GetCustomAttribute <MonoBehaviourPropertyAttribute>() != null).ToArray(); fields = allFields.Where(field => field.GetCustomAttribute <MonoBehaviourPropertyAttribute>() != null).ToArray(); break; case PopulationStrategy.OptOut: properties = allProperties.Where(prop => prop.GetCustomAttribute <MonoBehaviourIgnoreAttribute>() == null).ToArray(); fields = allFields.Where(field => field.GetCustomAttribute <MonoBehaviourIgnoreAttribute>() == null).ToArray(); break; default: throw new ArgumentOutOfRangeException(); } var naming = monoBehaviorAttr.NamingConventionType != null ? (INamingConvention)Activator.CreateInstance(monoBehaviorAttr.NamingConventionType, true) : null; var obj = Activator.CreateInstance(type, true); foreach (var kv in container) { if (FilteredNames.Contains(kv.Key)) { continue; } var propOrField = FindByName(properties, fields, kv.Key, naming); if (!propOrField.IsValid) { if (monoBehaviorAttr.ThrowOnUnmatched) { throw new SerializationException(); } else { continue; } } if (kv.Value is CustomType ct) { if (ct.Variables.Count == 1 && ct.Variables.ContainsKey("Array") && ct.Variables["Array"] is IReadOnlyList <object> varArray) { // It is an array. var arrayType = propOrField.GetValueType(); var targetIsArray = arrayType.IsArray; var targetIsCollection = arrayType.ImplementsGenericInterface(typeof(ICollection <>)); if (!targetIsArray && !targetIsCollection) { throw new SerializationException(); } Type elementType; if (targetIsArray) { elementType = arrayType.GetElementType(); } else { elementType = arrayType.GetGenericArguments()[0]; } if (elementType == null) { throw new SerializationException(); } if (targetIsArray) { // T[] var array = Array.CreateInstance(elementType, varArray.Count); if (varArray.Count == 0) { propOrField.SetValue(obj, array); } else { var sourceIsCustomObjectArray = varArray[0].GetType().ImplementsGenericInterface(typeof(IReadOnlyDictionary <,>)); if (sourceIsCustomObjectArray) { for (var i = 0; i < varArray.Count; ++i) { var d2 = (IReadOnlyDictionary <string, object>)varArray[i]; var innerObject = d2.First().Value; if (innerObject.GetType().ImplementsGenericInterface(typeof(IReadOnlyDictionary <,>))) { var obj1 = Deserialize((IReadOnlyDictionary <string, object>)innerObject, elementType); array.SetValue(obj1, i); } else { if (elementType == typeof(bool) && innerObject is byte byt) { array.SetValue(byt != 0, i); } else { array.SetValue(innerObject, i); } } } } else { for (var i = 0; i < varArray.Count; ++i) { array.SetValue(varArray[i], i); } } } propOrField.SetValue(obj, array); } else { // ICollection<T> var ctor = arrayType.GetConstructor(InternalBindings, null, Type.EmptyTypes, null); if (ctor == null) { throw new SerializationException(); } var collection = ctor.Invoke(null); var addMethod = arrayType.GetMethod("Add", InternalBindings); if (addMethod == null) { throw new InvalidCastException("The type is not a collection."); } if (varArray.Count == 0) { propOrField.SetValue(obj, collection); } else { var sourceIsCustomObjectArray = varArray[0].GetType().ImplementsGenericInterface(typeof(IReadOnlyDictionary <,>)); if (sourceIsCustomObjectArray) { foreach (var arrElem in varArray) { var d2 = (IReadOnlyDictionary <string, object>)arrElem; var innerObject = d2.First().Value; if (innerObject.GetType().ImplementsGenericInterface(typeof(IReadOnlyDictionary <,>))) { var obj1 = Deserialize((IReadOnlyDictionary <string, object>)innerObject, elementType); addMethod.Invoke(collection, new[] { obj1 }); } else { addMethod.Invoke(collection, new[] { innerObject }); } } } else { foreach (var arrElem in varArray) { addMethod.Invoke(collection, new[] { arrElem }); } } } propOrField.SetValue(obj, collection); } } else { // It is an object. var dictType = propOrField.GetValueType(); var obj1 = Deserialize(ct, dictType); propOrField.SetValue(obj, obj1); } } else { // It is a primitive value. var acceptedType = propOrField.GetValueType(); var serializedValueType = kv.Value.GetType(); if (serializedValueType == acceptedType) { propOrField.SetValue(obj, kv.Value); } else { // A little convertion is needed here... var converted = false; // Is the situation: target type is an enum, recorded type is a integer type? if (acceptedType.IsEnum) { if (serializedValueType == typeof(byte) || serializedValueType == typeof(sbyte) || serializedValueType == typeof(ushort) || serializedValueType == typeof(short) || serializedValueType == typeof(uint) || serializedValueType == typeof(int) || serializedValueType == typeof(ulong) || serializedValueType == typeof(long)) { var enumValue = Enum.ToObject(acceptedType, kv.Value); propOrField.SetValue(obj, enumValue); converted = true; } } if (!converted) { do { if (kv.Value is byte b && acceptedType == typeof(bool)) { // A special case: Unity uses UInt8 to store booleans. propOrField.SetValue(obj, b != 0); converted = true; break; } var converterType = propOrField.Attribute?.ConverterType; if (converterType == null) { // No specified converter found, then fail. break; } if (!converterType.ImplementsInterface(typeof(ISimpleTypeConverter))) { throw new ArgumentException("Converter does not implement " + nameof(ISimpleTypeConverter) + "."); } ISimpleTypeConverter converter; // Retrieve or create specified converter. if (_createdTypeConverters.ContainsKey(converterType)) { converter = _createdTypeConverters[converterType]; } else { converter = (ISimpleTypeConverter)Activator.CreateInstance(converterType); _createdTypeConverters[converterType] = converter; } if (!converter.CanConvertFrom(serializedValueType) || !converter.CanConvertTo(acceptedType)) { // If the converter cannot handle desired conversion, fail. break; } var convertedValue = converter.ConvertTo(kv.Value, acceptedType); propOrField.SetValue(obj, convertedValue); converted = true; } while (false); } if (!converted) { throw new InvalidCastException($"Serialized type {serializedValueType} cannot be converted to {acceptedType}."); } } } } return(obj); }