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);
            }
        }
Exemple #2
0
        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);
        }