Beispiel #1
0
            private object CreateCollection(DataTypeDescriptor descriptor, int capacity, bool isCaseInsensitive, object comparer)
            {
                CreateInstanceAccessor ctor = GetInitializer(descriptor);

                if (CtorArguments == null)
                {
                    return(ctor.CreateInstance());
                }

                object[] parameters = new object[CtorArguments.Length];
                for (int i = 0; i < CtorArguments.Length; i++)
                {
                    switch (CtorArguments[i])
                    {
                    case CollectionCtorArguments.Capacity:
                        parameters[i] = capacity;
                        break;

                    case CollectionCtorArguments.Comparer:
                        parameters[i] = comparer;
                        break;

                    case CollectionCtorArguments.CaseInsensitivity:
                        parameters[i] = isCaseInsensitive;
                        break;

                    default:
                        return(Throw.InternalError <object>($"Unsupported {nameof(CollectionCtorArguments)}"));
                    }
                }

                return(ctor.CreateInstance(parameters));
            }
        private protected static bool TryDeserializeByConverter(MemberInfo member, Type memberType, Func <string> readStringValue, out object result)
        {
            TypeConverter converter = null;

            // Explicitly defined type converter if can convert from string
            Attribute[] attrs = Attribute.GetCustomAttributes(member, typeof(TypeConverterAttribute), true);
            if (attrs.Length > 0 && attrs[0] is TypeConverterAttribute convAttr &&
                Reflector.ResolveType(convAttr.ConverterTypeName) is Type convType)
            {
                ConstructorInfo ctor       = convType.GetConstructor(new Type[] { Reflector.Type });
                object[]        ctorParams = { memberType };
                if (ctor == null)
                {
                    ctor       = convType.GetDefaultConstructor();
                    ctorParams = Reflector.EmptyObjects;
                }

                if (ctor != null)
                {
                    converter = CreateInstanceAccessor.GetAccessor(ctor).CreateInstance(ctorParams) as TypeConverter;
                }
            }

            if (converter?.CanConvertFrom(Reflector.StringType) != true)
            {
                result = null;
                return(false);
            }

            result = converter.ConvertFromInvariantString(readStringValue.Invoke());
            return(true);
        }
        private protected static object CreateCollectionByInitializerCollection(ConstructorInfo collectionCtor, IEnumerable initializerCollection, Dictionary <MemberInfo, object> members)
        {
            initializerCollection = initializerCollection.AdjustInitializerCollection(collectionCtor);
            object result = CreateInstanceAccessor.GetAccessor(collectionCtor).CreateInstance(initializerCollection);

            // restoring fields and properties of the final collection
            foreach (KeyValuePair <MemberInfo, object> member in members)
            {
                var property = member.Key as PropertyInfo;
                var field    = property != null ? null : member.Key as FieldInfo;


                // read-only property
                if (property?.CanWrite == false)
                {
                    object existingValue = property.Get(result);
                    if (property.PropertyType.IsValueType)
                    {
                        if (Equals(existingValue, member.Value))
                        {
                            continue;
                        }
                        Throw.SerializationException(Res.XmlSerializationPropertyHasNoSetter(property.Name, collectionCtor.DeclaringType));
                    }

                    if (existingValue == null && member.Value == null)
                    {
                        continue;
                    }
                    if (member.Value == null)
                    {
                        Throw.ReflectionException(Res.XmlSerializationPropertyHasNoSetterCantSetNull(property.Name, collectionCtor.DeclaringType));
                    }
                    if (existingValue == null)
                    {
                        Throw.ReflectionException(Res.XmlSerializationPropertyHasNoSetterGetsNull(property.Name, collectionCtor.DeclaringType));
                    }
                    if (existingValue.GetType() != member.Value.GetType())
                    {
                        Throw.ArgumentException(Res.XmlSerializationPropertyTypeMismatch(collectionCtor.DeclaringType, property.Name, member.Value.GetType(), existingValue.GetType()));
                    }

                    CopyContent(member.Value, existingValue);
                    continue;
                }

                // read-write property
                if (property != null)
                {
                    property.Set(result, member.Value);
                    continue;
                }

                // field
                field.Set(result, member.Value);
            }

            return(result);
        }
Beispiel #4
0
            private CreateInstanceAccessor GetInitializer(DataTypeDescriptor descriptor)
            {
                CreateInstanceAccessor GetCtorAccessor(Type type)
                {
                    if (CtorArguments == null)
                    {
                        return(CreateInstanceAccessor.GetAccessor(type));
                    }
                    Type[] args = new Type[CtorArguments.Length];
                    for (int i = 0; i < CtorArguments.Length; i++)
                    {
                        switch (CtorArguments[i])
                        {
                        case CollectionCtorArguments.Capacity:
                            args[i] = Reflector.IntType;
                            break;

                        case CollectionCtorArguments.CaseInsensitivity:
                            args[i] = Reflector.BoolType;
                            break;

                        case CollectionCtorArguments.Comparer:
                            args[i] = IsGeneric
                                    ? HasEqualityComparer
                                        ? typeof(IEqualityComparer <>).GetGenericType(type.GetGenericArguments()[0])
                                        : typeof(IComparer <>).GetGenericType(type.GetGenericArguments()[0])
                                    : HasEqualityComparer
                                        ? typeof(IEqualityComparer)
                                        : typeof(IComparer);
                            break;

                        default:
                            return(Throw.InternalError <CreateInstanceAccessor>($"Unsupported {nameof(CollectionCtorArguments)}"));
                        }
                    }

                    ConstructorInfo ctor = type.GetConstructor(args);

                    if (ctor == null)
                    {
                        Throw.InvalidOperationException(Res.ReflectionCtorNotFound(type));
                    }
                    return(CreateInstanceAccessor.GetAccessor(ctor));
                }

                if (ctorCache == null)
                {
                    Interlocked.CompareExchange(ref ctorCache, new Cache <Type, CreateInstanceAccessor>(GetCtorAccessor).GetThreadSafeAccessor(), null);
                }
                return(ctorCache[descriptor.Type]);
            }
        private static bool TryDeserializeObject(Type type, XmlReader reader, object existingInstance, out object result)
        {
            #region Local Methods to reduce complexity

            bool TryDeserializeKeyValue(ref TryDeserializeObjectContext ctx)
            {
                if (ctx.Type?.IsGenericTypeOf(Reflector.KeyValuePairType) != true)
                {
                    return(false);
                }

                bool   keyRead   = false;
                bool   valueRead = false;
                object key       = null;
                object value     = null;

                while (true)
                {
                    ReadToNodeType(ctx.Reader, XmlNodeType.Element, XmlNodeType.EndElement);
                    switch (ctx.Reader.NodeType)
                    {
                    case XmlNodeType.Element:
                        switch (ctx.Reader.Name)
                        {
                        case nameof(KeyValuePair <_, _> .Key):
                            if (keyRead)
                            {
                                Throw.ArgumentException(Res.XmlSerializationMultipleKeys);
                            }

                            keyRead = true;
                            string attrType = ctx.Reader[XmlSerializer.AttributeType];
                            Type   keyType  = attrType != null?Reflector.ResolveType(attrType) : ctx.Type.GetGenericArguments()[0];

                            if (!TryDeserializeObject(keyType, ctx.Reader, null, out key))
                            {
                                if (attrType != null && keyType == null)
                                {
                                    Throw.ReflectionException(Res.XmlSerializationCannotResolveType(attrType));
                                }
                                Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(keyType));
                            }
                            break;

                        case nameof(KeyValuePair <_, _> .Value):
                            if (valueRead)
                            {
                                Throw.ArgumentException(Res.XmlSerializationMultipleValues);
                            }

                            valueRead = true;
                            attrType  = ctx.Reader[XmlSerializer.AttributeType];
                            Type valueType = attrType != null?Reflector.ResolveType(attrType) : ctx.Type.GetGenericArguments()[1];

                            if (!TryDeserializeObject(valueType, ctx.Reader, null, out value))
                            {
                                if (attrType != null && valueType == null)
                                {
                                    Throw.ReflectionException(Res.XmlSerializationCannotResolveType(attrType));
                                }
                                Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(valueType));
                            }
                            break;

                        default:
                            Throw.ArgumentException(Res.XmlSerializationUnexpectedElement(ctx.Reader.Name));
                            break;
                        }
                        break;

                    case XmlNodeType.EndElement:
                        // end of KeyValue: checking whether both key and value have been read
                        if (!keyRead)
                        {
                            Throw.ArgumentException(Res.XmlSerializationKeyValueMissingKey);
                        }
                        if (!valueRead)
                        {
                            Throw.ArgumentException(Res.XmlSerializationKeyValueMissingValue);
                        }

                        ctx.Result = Activator.CreateInstance(ctx.Type);
                        Accessors.SetKeyValue(ctx.Result, key, value);
                        return(true);
                    }
                }
            }

            void DeserializeBinary(ref TryDeserializeObjectContext ctx)
            {
                if (ctx.Reader.IsEmptyElement)
                {
                    return;
                }

                string attrCrc = ctx.Reader[XmlSerializer.AttributeCrc];

                ReadToNodeType(ctx.Reader, XmlNodeType.Text);
                byte[] data = Convert.FromBase64String(ctx.Reader.Value);
                if (attrCrc != null)
                {
                    if (Crc32.CalculateHash(data).ToString("X8", CultureInfo.InvariantCulture) != attrCrc)
                    {
                        Throw.ArgumentException(Res.XmlSerializationCrcError);
                    }
                }

                ctx.Result = BinarySerializer.Deserialize(data);
                ReadToNodeType(ctx.Reader, XmlNodeType.EndElement);
            }

            bool TryDeserializeComplexObject(ref TryDeserializeObjectContext ctx)
            {
                if (ctx.Type == null || ctx.Reader.IsEmptyElement)
                {
                    return(false);
                }

                // 1.) array (both existing and new)
                if (ctx.Type.IsArray)
                {
                    ctx.Result = DeserializeArray(ctx.ExistingInstance as Array, ctx.Type.GetElementType(), ctx.Reader, true);
                    return(true);
                }

                // 2.) existing read-write collection
                if (ctx.Type.IsReadWriteCollection(ctx.ExistingInstance))
                {
                    DeserializeContent(ctx.Reader, ctx.ExistingInstance);
                    ctx.Result = ctx.ExistingInstance;
                    return(true);
                }

                bool isCollection = ctx.Type.IsSupportedCollectionForReflection(out var defaultCtor, out var collectionCtor, out var elementType, out bool isDictionary);

                // 3.) New collection by collectionCtor (only if there is no defaultCtor)
                if (isCollection && defaultCtor == null && !ctx.Type.IsValueType)
                {
                    ctx.Result = DeserializeContentByInitializerCollection(ctx.Reader, collectionCtor, elementType, isDictionary);
                    return(true);
                }

                ctx.Result = ctx.ExistingInstance ?? (ctx.Type.CanBeCreatedWithoutParameters()
                    ? ctx.Type.IsValueType ? Activator.CreateInstance(ctx.Type) : CreateInstanceAccessor.GetAccessor(ctx.Type).CreateInstance()
                    : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(ctx.Type)));

                // 4.) New collection by collectionCtor again (there IS defaultCtor but the new instance is read-only so falling back to collectionCtor)
                if (isCollection && !ctx.Type.IsReadWriteCollection(ctx.Result))
                {
                    if (collectionCtor != null)
                    {
                        ctx.Result = DeserializeContentByInitializerCollection(ctx.Reader, collectionCtor, elementType, isDictionary);
                        return(true);
                    }

                    Throw.SerializationException(Res.XmlSerializationCannotDeserializeReadOnlyCollection(ctx.Type));
                }

                // 5.) Newly created collection or any other object (both existing and new)
                DeserializeContent(ctx.Reader, ctx.Result);
                return(true);
            }

            #endregion

            // null value
            if (reader.IsEmptyElement && (type == null || !type.IsValueType || type.IsNullable()))
            {
                result = null;
                return(true);
            }

            if (type != null && type.IsNullable())
            {
                type = Nullable.GetUnderlyingType(type);
            }

            // a.) If type can be natively parsed, parsing from string
            if (type != null && type.CanBeParsedNatively())
            {
                string value = ReadStringValue(reader);
                result = value.Parse(type);
                return(true);
            }

            // b.) Deserialize IXmlSerializable
            string format = reader[XmlSerializer.AttributeFormat];
            if (type != null && format == XmlSerializer.AttributeValueCustom)
            {
                object instance = existingInstance ?? (type.CanBeCreatedWithoutParameters()
                    ? type.IsValueType ? Activator.CreateInstance(type) : CreateInstanceAccessor.GetAccessor(type).CreateInstance()
                    : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(type)));
                if (!(instance is IXmlSerializable xmlSerializable))
                {
                    result = default;
                    Throw.ArgumentException(Res.XmlSerializationNotAnIXmlSerializable(type));
                    return(default);
        private static bool TryDeserializeObject(Type type, XElement element, object existingInstance, out object result)
        {
            #region Local Methods to reduce complexity

            bool TryDeserializeKeyValue(ref TryDeserializeObjectContext ctx)
            {
                if (ctx.Type?.IsGenericTypeOf(Reflector.KeyValuePairType) == true)
                {
                    // key
                    XElement xItem = ctx.Element.Element(nameof(KeyValuePair <_, _> .Key));
                    if (xItem == null)
                    {
                        Throw.ArgumentException(Res.XmlSerializationKeyValueMissingKey);
                    }
                    XAttribute xType   = xItem.Attribute(XmlSerializer.AttributeType);
                    Type       keyType = xType != null?Reflector.ResolveType(xType.Value) : ctx.Type.GetGenericArguments()[0];

                    if (!TryDeserializeObject(keyType, xItem, null, out object key))
                    {
                        if (xType != null && keyType == null)
                        {
                            Throw.ReflectionException(Res.XmlSerializationCannotResolveType(xType.Value));
                        }
                        Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(keyType));
                    }

                    // value
                    xItem = ctx.Element.Element(nameof(KeyValuePair <_, _> .Value));
                    if (xItem == null)
                    {
                        Throw.ArgumentException(Res.XmlSerializationKeyValueMissingValue);
                    }
                    xType = xItem.Attribute(XmlSerializer.AttributeType);
                    Type valueType = xType != null?Reflector.ResolveType(xType.Value) : ctx.Type.GetGenericArguments()[1];

                    if (!TryDeserializeObject(valueType, xItem, null, out object value))
                    {
                        if (xType != null && valueType == null)
                        {
                            Throw.ReflectionException(Res.XmlSerializationCannotResolveType(xType.Value));
                        }
                        Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(valueType));
                    }

                    ctx.Result = Activator.CreateInstance(ctx.Type);
                    Accessors.SetKeyValue(ctx.Result, key, value);
                    return(true);
                }

                return(false);
            }

            void DeserializeBinary(ref TryDeserializeObjectContext ctx)
            {
                if (ctx.Element.IsEmpty)
                {
                    return;
                }
                byte[]     data    = Convert.FromBase64String(ctx.Element.Value);
                XAttribute attrCrc = ctx.Element.Attribute(XmlSerializer.AttributeCrc);

                if (attrCrc != null)
                {
                    if (Crc32.CalculateHash(data).ToString("X8", CultureInfo.InvariantCulture) != attrCrc.Value)
                    {
                        Throw.ArgumentException(Res.XmlSerializationCrcError);
                    }
                }

                ctx.Result = BinarySerializer.Deserialize(data);
            }

            bool TryDeserializeComplexObject(ref TryDeserializeObjectContext ctx)
            {
                if (ctx.Type != null && !ctx.Element.IsEmpty)
                {
                    // 1.) array (both existing and new)
                    if (ctx.Type.IsArray)
                    {
                        ctx.Result = DeserializeArray(ctx.ExistingInstance as Array, ctx.Type.GetElementType(), ctx.Element, true);
                        return(true);
                    }

                    // 2.) existing read-write collection
                    if (ctx.Type.IsReadWriteCollection(ctx.ExistingInstance))
                    {
                        DeserializeContent(ctx.Element, ctx.ExistingInstance);
                        ctx.Result = ctx.ExistingInstance;
                        return(true);
                    }

                    bool isCollection = ctx.Type.IsSupportedCollectionForReflection(out var defaultCtor, out var collectionCtor, out var elementType, out bool isDictionary);

                    // 3.) New collection by collectionCtor (only if there is no defaultCtor)
                    if (isCollection && defaultCtor == null && !ctx.Type.IsValueType)
                    {
                        ctx.Result = DeserializeContentByInitializerCollection(ctx.Element, collectionCtor, elementType, isDictionary);
                        return(true);
                    }

                    ctx.Result = ctx.ExistingInstance ?? (ctx.Type.CanBeCreatedWithoutParameters()
                            ? ctx.Type.IsValueType ? Activator.CreateInstance(ctx.Type) : CreateInstanceAccessor.GetAccessor(ctx.Type).CreateInstance()
                            : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(ctx.Type)));

                    // 4.) New collection by collectionCtor again (there IS defaultCtor but the new instance is read-only so falling back to collectionCtor)
                    if (isCollection && !ctx.Type.IsReadWriteCollection(ctx.Result))
                    {
                        if (collectionCtor != null)
                        {
                            ctx.Result = DeserializeContentByInitializerCollection(ctx.Element, collectionCtor, elementType, isDictionary);
                            return(true);
                        }

                        Throw.SerializationException(Res.XmlSerializationCannotDeserializeReadOnlyCollection(ctx.Type));
                    }

                    // 5.) Newly created collection or any other object (both existing and new)
                    DeserializeContent(ctx.Element, ctx.Result);
                    return(true);
                }

                return(false);
            }

            #endregion

            // null value
            if (element.IsEmpty && (type == null || !type.IsValueType || type.IsNullable()))
            {
                result = null;
                return(true);
            }

            if (type != null && type.IsNullable())
            {
                type = Nullable.GetUnderlyingType(type);
            }

            // a.) If type can be natively parsed, parsing from string
            if (type != null && type.CanBeParsedNatively())
            {
                string value = ReadStringValue(element);
                result = value.Parse(type);
                return(true);
            }

            // b.) Deserialize IXmlSerializable
            string format = element.Attribute(XmlSerializer.AttributeFormat)?.Value;
            if (type != null && format == XmlSerializer.AttributeValueCustom)
            {
                object instance = existingInstance ?? (type.CanBeCreatedWithoutParameters()
                    ? type.IsValueType ? Activator.CreateInstance(type) : CreateInstanceAccessor.GetAccessor(type).CreateInstance()
                    : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(type)));
                if (!(instance is IXmlSerializable xmlSerializable))
                {
                    result = default;
                    Throw.ArgumentException(Res.XmlSerializationNotAnIXmlSerializable(type));
                    return(default);