File: System\Xml\Serialization\ReflectionXmlSerializationReader.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Xml.Schema;
 
namespace System.Xml.Serialization
{
    internal delegate void UnknownNodeAction(object? o);
 
    [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
    [RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
    internal sealed class ReflectionXmlSerializationReader : XmlSerializationReader
    {
        private readonly XmlMapping _mapping;
 
        internal static TypeDesc StringTypeDesc { get; set; } = (new TypeScope()).GetTypeDesc(typeof(string));
        internal static TypeDesc QnameTypeDesc { get; set; } = (new TypeScope()).GetTypeDesc(typeof(XmlQualifiedName));
 
        public ReflectionXmlSerializationReader(XmlMapping mapping, XmlReader xmlReader, XmlDeserializationEvents events, string? encodingStyle)
        {
            Init(xmlReader, events, encodingStyle);
            _mapping = mapping;
        }
 
        protected override void InitCallbacks()
        {
            TypeScope scope = _mapping.Scope!;
            foreach (TypeMapping mapping in scope.TypeMappings)
            {
                if (mapping.IsSoap &&
                        (mapping is StructMapping || mapping is EnumMapping || mapping is ArrayMapping || mapping is NullableMapping) &&
                        !mapping.TypeDesc!.IsRoot)
                {
                    AddReadCallback(
                        mapping.TypeName!,
                        mapping.Namespace!,
                        mapping.TypeDesc.Type!,
                        CreateXmlSerializationReadCallback(mapping));
                }
            }
        }
 
        protected override void InitIDs()
        {
        }
 
        public object? ReadObject()
        {
            XmlMapping xmlMapping = _mapping;
            if (!xmlMapping.IsReadable)
                return null;
 
            if (!xmlMapping.GenerateSerializer)
                throw new ArgumentException(SR.Format(SR.XmlInternalError, "xmlMapping"));
 
            if (xmlMapping is XmlTypeMapping xmlTypeMapping)
            {
                return GenerateTypeElement(xmlTypeMapping);
            }
            else if (xmlMapping is XmlMembersMapping xmlMembersMapping)
            {
                return GenerateMembersElement(xmlMembersMapping);
            }
            else
            {
                throw new ArgumentException(SR.Format(SR.XmlInternalError, "xmlMapping"));
            }
        }
 
        private object?[] GenerateMembersElement(XmlMembersMapping xmlMembersMapping)
        {
            if (xmlMembersMapping.Accessor.IsSoap)
            {
                return GenerateEncodedMembersElement(xmlMembersMapping);
            }
            else
            {
                return GenerateLiteralMembersElement(xmlMembersMapping);
            }
        }
 
        private object[] GenerateLiteralMembersElement(XmlMembersMapping xmlMembersMapping)
        {
            ElementAccessor element = xmlMembersMapping.Accessor;
            MemberMapping[] mappings = ((MembersMapping)element.Mapping!).Members!;
            bool hasWrapperElement = ((MembersMapping)element.Mapping).HasWrapperElement;
            Reader.MoveToContent();
 
            object[] p = new object[mappings.Length];
            InitializeValueTypes(p, mappings);
 
            if (hasWrapperElement)
            {
                string elementName = element.Name;
                string elementNs = element.Form == XmlSchemaForm.Qualified ? element.Namespace! : string.Empty;
                Reader.MoveToContent();
                while (Reader.NodeType != XmlNodeType.EndElement && Reader.NodeType != XmlNodeType.None)
                {
                    if (Reader.IsStartElement(element.Name, elementNs))
                    {
                        if (!GenerateLiteralMembersElementInternal(mappings, hasWrapperElement, p))
                        {
                            continue;
                        }
 
                        ReadEndElement();
                    }
                    else
                    {
 
                        UnknownNode(null, $"{elementNs}:{elementName}");
                    }
 
                    Reader.MoveToContent();
                }
            }
            else
            {
                GenerateLiteralMembersElementInternal(mappings, hasWrapperElement, p);
            }
 
            return p;
        }
 
        private bool GenerateLiteralMembersElementInternal(MemberMapping[] mappings, bool hasWrapperElement, object?[] p)
        {
            Member? anyText = null;
            Member? anyElement = null;
            Member? anyAttribute = null;
 
            var membersList = new List<Member>();
            var textOrArrayMembersList = new List<Member>();
            var attributeMembersList = new List<Member>();
 
            for (int i = 0; i < mappings.Length; i++)
            {
                int index = i;
                MemberMapping mapping = mappings[index];
                Action<object?> source = (o) => p[index] = o;
 
                Member member = new Member(mapping);
                Member anyMember = new Member(mapping);
 
                if (mapping.Xmlns != null)
                {
                    var xmlns = new XmlSerializerNamespaces();
                    p[index] = xmlns;
                    member.XmlnsSource = xmlns.Add;
                }
 
                member.Source = source;
                anyMember.Source = source;
 
                if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite)
                {
                    string nameSpecified = $"{mapping.Name}Specified";
                    for (int j = 0; j < mappings.Length; j++)
                    {
                        if (mappings[j].Name == nameSpecified)
                        {
                            int indexJ = j;
                            member.CheckSpecifiedSource = (o) => p[indexJ] = o;
                        }
                    }
                }
 
                bool foundAnyElement = false;
                if (mapping.Text != null)
                {
                    anyText = anyMember;
                }
 
                if (mapping.Attribute != null && mapping.Attribute.Any)
                {
                    anyMember.Collection = new CollectionMember();
                    anyMember.ArraySource = anyMember.Source;
                    anyMember.Source = anyMember.Collection.Add;
 
                    anyAttribute = anyMember;
                }
 
                if (mapping.Attribute != null || mapping.Xmlns != null)
                {
                    attributeMembersList.Add(member);
                }
                else if (mapping.Text != null)
                {
                    textOrArrayMembersList.Add(member);
                }
 
                if (!mapping.IsSequence)
                {
                    for (int j = 0; j < mapping.Elements!.Length; j++)
                    {
                        if (mapping.Elements[j].Any && mapping.Elements[j].Name.Length == 0)
                        {
                            anyElement = anyMember;
                            if (mapping.Attribute == null && mapping.Text == null)
                            {
                                anyMember.Collection = new CollectionMember();
                                anyMember.ArraySource = anyMember.Collection.Add;
 
                                textOrArrayMembersList.Add(anyMember);
                            }
 
                            foundAnyElement = true;
                            break;
                        }
                    }
                }
 
                if (mapping.Attribute != null || mapping.Text != null || foundAnyElement)
                {
                    membersList.Add(anyMember);
                }
                else if (mapping.TypeDesc!.IsArrayLike
                    && !(mapping.Elements!.Length == 1 && mapping.Elements[0].Mapping is ArrayMapping))
                {
                    anyMember.Collection = new CollectionMember();
                    anyMember.ArraySource = anyMember.Collection.Add;
 
                    membersList.Add(anyMember);
                    textOrArrayMembersList.Add(anyMember);
                }
                else
                {
                    membersList.Add(member);
                }
            }
 
            Member[] members = membersList.ToArray();
            Member[] textOrArrayMembers = textOrArrayMembersList.ToArray();
 
            if (members.Length > 0 && members[0].Mapping.IsReturnValue)
                IsReturnValue = true;
 
            if (attributeMembersList.Count > 0)
            {
                Member[] attributeMembers = attributeMembersList.ToArray();
                object? tempObject = null;
                WriteAttributes(attributeMembers, anyAttribute, UnknownNode, ref tempObject);
                Reader.MoveToElement();
            }
 
            if (hasWrapperElement)
            {
                if (Reader.IsEmptyElement)
                {
                    Reader.Skip();
                    Reader.MoveToContent();
                    return false;
                }
 
                Reader.ReadStartElement();
            }
 
            Reader.MoveToContent();
            while (Reader.NodeType != XmlNodeType.EndElement && Reader.NodeType != XmlNodeType.None)
            {
                WriteMemberElements(members, UnknownNode, UnknownNode, anyElement, anyText, null);
                Reader.MoveToContent();
            }
 
            foreach (Member member in textOrArrayMembers)
            {
                object? value = null;
                SetCollectionObjectWithCollectionMember(ref value, member.Collection!, member.Mapping.TypeDesc!.Type!);
                member.Source!(value);
            }
 
            if (anyAttribute != null)
            {
                object? value = null;
                SetCollectionObjectWithCollectionMember(ref value, anyAttribute.Collection!, anyAttribute.Mapping.TypeDesc!.Type!);
                anyAttribute.ArraySource!(value);
            }
 
            return true;
        }
 
        private static void InitializeValueTypes(object?[] p, MemberMapping[] mappings)
        {
            for (int i = 0; i < mappings.Length; i++)
            {
                if (!mappings[i].TypeDesc!.IsValueType)
                    continue;
 
 
                if (mappings[i].TypeDesc!.IsOptionalValue && mappings[i].TypeDesc!.BaseTypeDesc!.UseReflection)
                {
                    p[i] = null;
                }
                else
                {
                    p[i] = ReflectionCreateObject(mappings[i].TypeDesc!.Type!);
                }
            }
        }
 
        private object?[] GenerateEncodedMembersElement(XmlMembersMapping xmlMembersMapping)
        {
            ElementAccessor element = xmlMembersMapping.Accessor;
            var membersMapping = (MembersMapping)element.Mapping!;
            MemberMapping[] mappings = membersMapping.Members!;
            bool hasWrapperElement = membersMapping.HasWrapperElement;
            bool writeAccessors = membersMapping.WriteAccessors;
 
            Reader.MoveToContent();
 
            object?[] p = new object[mappings.Length];
            InitializeValueTypes(p, mappings);
 
            bool isEmptyWrapper = true;
            if (hasWrapperElement)
            {
                Reader.MoveToContent();
                while (Reader.NodeType == XmlNodeType.Element)
                {
                    string? root = Reader.GetAttribute("root", Soap.Encoding);
                    if (root == null || XmlConvert.ToBoolean(root))
                        break;
 
                    ReadReferencedElement();
                    Reader.MoveToContent();
                }
 
                if (membersMapping.ValidateRpcWrapperElement)
                {
                    string name = element.Name;
                    string? ns = element.Form == XmlSchemaForm.Qualified ? element.Namespace : string.Empty;
                    if (!XmlNodeEqual(Reader, name, ns))
                    {
                        throw CreateUnknownNodeException();
                    }
                }
 
                isEmptyWrapper = Reader.IsEmptyElement;
                Reader.ReadStartElement();
            }
 
            Member[] members = new Member[mappings.Length];
            for (int i = 0; i < mappings.Length; i++)
            {
                int index = i;
                MemberMapping mapping = mappings[index];
                var member = new Member(mapping);
                member.Source = (value) => p[index] = value;
                members[index] = member;
                if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite)
                {
                    string nameSpecified = $"{mapping.Name}Specified";
                    for (int j = 0; j < mappings.Length; j++)
                    {
                        if (mappings[j].Name == nameSpecified)
                        {
                            int indexOfSpecifiedMember = j;
                            member.CheckSpecifiedSource = (value) => p[indexOfSpecifiedMember] = value;
                            break;
                        }
                    }
                }
 
            }
 
            Fixup? fixup = WriteMemberFixupBegin(members, p);
            if (members.Length > 0 && members[0].Mapping.IsReturnValue)
            {
                IsReturnValue = true;
            }
 
            List<CheckTypeSource>? checkTypeHrefSource = null;
            if (!hasWrapperElement && !writeAccessors)
            {
                checkTypeHrefSource = new List<CheckTypeSource>();
            }
 
            Reader.MoveToContent();
            while (Reader.NodeType != XmlNodeType.EndElement && Reader.NodeType != XmlNodeType.None)
            {
                UnknownNodeAction unrecognizedElementSource;
                if (checkTypeHrefSource == null)
                {
                    unrecognizedElementSource = (_) => UnknownNode(p);
                }
                else
                {
                    unrecognizedElementSource = (_) =>
                    {
                        if (Reader.GetAttribute("id", null) != null)
                        {
                            ReadReferencedElement();
                        }
                        else
                        {
                            UnknownNode(p);
                        }
                    };
                }
 
                WriteMemberElements(members, unrecognizedElementSource, (_) => UnknownNode(p), null, null, fixup: fixup, checkTypeHrefsSource: checkTypeHrefSource);
                Reader.MoveToContent();
            }
 
            if (!isEmptyWrapper)
            {
                ReadEndElement();
            }
 
            if (checkTypeHrefSource != null)
            {
                foreach (CheckTypeSource currentySource in checkTypeHrefSource)
                {
                    bool isReferenced = true;
                    bool isObject = currentySource.IsObject;
                    object? refObj = isObject ? currentySource.RefObject : GetTarget((string)currentySource.RefObject!);
                    if (refObj == null)
                    {
                        continue;
                    }
 
                    var checkTypeSource = new CheckTypeSource()
                    {
                        RefObject = refObj,
                        Type = refObj.GetType(),
                        Id = null
                    };
                    WriteMemberElementsIf(members, null, (_) => isReferenced = false, fixup, checkTypeSource);
 
                    if (isObject && isReferenced)
                    {
                        Referenced(refObj);
                    }
                }
            }
 
            ReadReferencedElements();
            return p;
        }
 
        private object? GenerateTypeElement(XmlTypeMapping xmlTypeMapping)
        {
            ElementAccessor element = xmlTypeMapping.Accessor;
            TypeMapping mapping = element.Mapping!;
 
            Reader.MoveToContent();
            var memberMapping = new MemberMapping();
            memberMapping.TypeDesc = mapping.TypeDesc;
            memberMapping.Elements = new ElementAccessor[] { element };
 
            object? o = null;
            var holder = new ObjectHolder();
            var member = new Member(memberMapping);
            member.Source = (value) => holder.Object = value;
            member.GetSource = () => holder.Object;
            UnknownNodeAction elementElseAction = CreateUnknownNodeException;
            UnknownNodeAction elseAction = UnknownNode;
            WriteMemberElements(new Member[] { member }, elementElseAction, elseAction, element.Any ? member : null, null);
            o = holder.Object;
 
            if (element.IsSoap)
            {
                Referenced(o);
                ReadReferencedElements();
            }
 
            return o;
        }
 
        private void WriteMemberElements(Member[] expectedMembers, UnknownNodeAction elementElseAction, UnknownNodeAction elseAction, Member? anyElement, Member? anyText, Fixup? fixup = null, List<CheckTypeSource>? checkTypeHrefsSource = null)
        {
            bool checkType = checkTypeHrefsSource != null;
            if (Reader.NodeType == XmlNodeType.Element)
            {
                if (checkType)
                {
                    if (Reader.GetAttribute("root", Soap.Encoding) == "0")
                    {
                        elementElseAction(null);
                        return;
                    }
 
                    WriteMemberElementsCheckType(checkTypeHrefsSource!);
                }
                else
                {
                    WriteMemberElementsIf(expectedMembers, anyElement, elementElseAction, fixup: fixup);
                }
            }
            else if (anyText != null && anyText.Mapping != null && WriteMemberText(anyText))
            {
            }
            else
            {
                ProcessUnknownNode(elseAction);
            }
        }
 
        private void WriteMemberElementsCheckType(List<CheckTypeSource> checkTypeHrefsSource)
        {
            object? RefElememnt = ReadReferencingElement(null, null, true, out string? refElemId);
            var source = new CheckTypeSource();
            if (refElemId != null)
            {
                source.RefObject = refElemId;
                source.IsObject = false;
                checkTypeHrefsSource.Add(source);
            }
            else if (RefElememnt != null)
            {
                source.RefObject = RefElememnt;
                source.IsObject = true;
                checkTypeHrefsSource.Add(source);
            }
        }
 
        private static void ProcessUnknownNode(UnknownNodeAction action)
        {
            action?.Invoke(null);
        }
 
        private void WriteMembers(Member[] members, UnknownNodeAction elementElseAction, UnknownNodeAction elseAction, Member? anyElement, Member? anyText)
        {
            Reader.MoveToContent();
 
            while (Reader.NodeType != XmlNodeType.EndElement && Reader.NodeType != XmlNodeType.None)
            {
                WriteMemberElements(members, elementElseAction, elseAction, anyElement, anyText);
                Reader.MoveToContent();
            }
        }
 
        private static void SetCollectionObjectWithCollectionMember([NotNull] ref object? collection, CollectionMember collectionMember,
            [DynamicallyAccessedMembers(TrimmerConstants.AllMethods)] Type collectionType)
        {
            if (collectionType.IsArray)
            {
                Array a;
                if (collection is Array currentArray && currentArray.Length == collectionMember.Count)
                {
                    a = currentArray;
                }
                else
                {
                    a = Array.CreateInstanceFromArrayType(collectionType, collectionMember.Count);
                }
 
                for (int i = 0; i < collectionMember.Count; i++)
                {
                    a.SetValue(collectionMember[i], i);
                }
 
                collection = a;
            }
            else
            {
                collection ??= ReflectionCreateObject(collectionType)!;
 
                AddObjectsIntoTargetCollection(collection, collectionMember, collectionType);
            }
        }
 
        private static void AddObjectsIntoTargetCollection(object targetCollection, List<object?> sourceCollection,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type targetCollectionType)
        {
            if (targetCollection is IList targetList)
            {
                foreach (object? item in sourceCollection)
                {
                    targetList.Add(item);
                }
            }
            else
            {
                MethodInfo? addMethod = targetCollectionType.GetMethod("Add");
                if (addMethod == null)
                {
                    throw new InvalidOperationException(SR.XmlInternalError);
                }
 
                object?[] arguments = new object?[1];
                foreach (object? item in sourceCollection)
                {
                    arguments[0] = item;
                    addMethod.Invoke(targetCollection, arguments);
                }
            }
        }
 
        private static readonly ContextAwareTables<Hashtable> s_setMemberValueDelegateCache = new ContextAwareTables<Hashtable>();
 
        private static ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate GetSetMemberValueDelegate(object o, string memberName)
        {
            Debug.Assert(o != null, "Object o should not be null");
            Debug.Assert(!string.IsNullOrEmpty(memberName), "memberName must have a value");
            Type type = o.GetType();
            var delegateCacheForType = s_setMemberValueDelegateCache.GetOrCreateValue(type, _ => new Hashtable());
            var result = delegateCacheForType[memberName];
            if (result == null)
            {
                lock (delegateCacheForType)
                {
                    if ((result = delegateCacheForType[memberName]) == null)
                    {
                        MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetEffectiveSetInfo(o.GetType(), memberName);
                        Debug.Assert(memberInfo != null, "memberInfo could not be retrieved");
 
                        if (type.IsValueType || !RuntimeFeature.IsDynamicCodeSupported)
                        {
                            if (memberInfo is PropertyInfo propInfo)
                            {
                                result = new ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate(propInfo.SetValue);
                            }
                            else if (memberInfo is FieldInfo fieldInfo)
                            {
                                result = new ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate(fieldInfo.SetValue);
                            }
                            else
                            {
                                throw new InvalidOperationException(SR.XmlInternalError);
                            }
                        }
                        else
                        {
                            Type memberType;
                            if (memberInfo is PropertyInfo propInfo)
                            {
                                memberType = propInfo.PropertyType;
                            }
                            else if (memberInfo is FieldInfo fieldInfo)
                            {
                                memberType = fieldInfo.FieldType;
                            }
                            else
                            {
                                throw new InvalidOperationException(SR.XmlInternalError);
                            }
 
                            MethodInfo getSetMemberValueDelegateWithTypeGenericMi = typeof(ReflectionXmlSerializationReaderHelper).GetMethod("GetSetMemberValueDelegateWithType", BindingFlags.Static | BindingFlags.Public)!;
                            MethodInfo getSetMemberValueDelegateWithTypeMi = getSetMemberValueDelegateWithTypeGenericMi.MakeGenericMethod(o.GetType(), memberType);
                            var getSetMemberValueDelegateWithType = getSetMemberValueDelegateWithTypeMi.CreateDelegate<Func<MemberInfo, ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>>();
                            result = getSetMemberValueDelegateWithType(memberInfo);
                        }
 
                        delegateCacheForType[memberName] = result;
                    }
                }
            }
 
            return (ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate)result;
        }
 
        private static object? GetMemberValue(object o, MemberInfo memberInfo)
        {
            if (memberInfo is PropertyInfo propertyInfo)
            {
                return propertyInfo.GetValue(o);
            }
            else if (memberInfo is FieldInfo fieldInfo)
            {
                return fieldInfo.GetValue(o);
            }
 
            throw new InvalidOperationException(SR.XmlInternalError);
        }
 
        private bool WriteMemberText(Member anyText)
        {
            object? value;
            MemberMapping anyTextMapping = anyText.Mapping;
            if ((Reader.NodeType == XmlNodeType.Text ||
                        Reader.NodeType == XmlNodeType.CDATA ||
                        Reader.NodeType == XmlNodeType.Whitespace ||
                        Reader.NodeType == XmlNodeType.SignificantWhitespace))
            {
                TextAccessor text = anyTextMapping.Text!;
                if (text.Mapping is SpecialMapping special)
                {
                    if (special.TypeDesc!.Kind == TypeKind.Node)
                    {
                        value = Document.CreateTextNode(Reader.ReadString());
                    }
                    else
                    {
                        throw new InvalidOperationException(SR.XmlInternalError);
                    }
                }
                else
                {
                    if (anyTextMapping.TypeDesc!.IsArrayLike)
                    {
                        if (text.Mapping!.TypeDesc!.CollapseWhitespace)
                        {
                            value = CollapseWhitespace(Reader.ReadString());
                        }
                        else
                        {
                            value = Reader.ReadString();
                        }
                    }
                    else
                    {
                        if (text.Mapping!.TypeDesc == StringTypeDesc || text.Mapping.TypeDesc!.FormatterName == "String")
                        {
                            value = ReadString(null, text.Mapping.TypeDesc.CollapseWhitespace);
                        }
                        else
                        {
                            value = WritePrimitive(text.Mapping, (state) => ((ReflectionXmlSerializationReader)state).Reader.ReadString(), this);
                        }
                    }
                }
 
                anyText.Source!(value);
                return true;
            }
 
            return false;
        }
 
        private static bool IsSequence()
        {
            // https://github.com/dotnet/runtime/issues/1402:
            // Currently the reflection based method treat this kind of type as normal types.
            // But potentially we can do some optimization for types that have ordered properties.
            return false;
        }
 
        private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementMember, UnknownNodeAction elementElseAction, Fixup? fixup = null, CheckTypeSource? checkTypeSource = null)
        {
            bool checkType = checkTypeSource != null;
            bool isSequence = IsSequence();
            if (isSequence)
            {
                // https://github.com/dotnet/runtime/issues/1402:
                // Currently the reflection based method treat this kind of type as normal types.
                // But potentially we can do some optimization for types that have ordered properties.
            }
 
            ElementAccessor? e = null;
            Member? member = null;
            bool foundElement = false;
            int elementIndex = -1;
            foreach (Member m in expectedMembers)
            {
                if (m.Mapping.Xmlns != null)
                    continue;
                if (m.Mapping.Ignore)
                    continue;
                if (isSequence && (m.Mapping.IsText || m.Mapping.IsAttribute))
                    continue;
 
                for (int i = 0; i < m.Mapping.Elements!.Length; i++)
                {
                    ElementAccessor ele = m.Mapping.Elements[i];
                    string? ns = ele.Form == XmlSchemaForm.Qualified ? ele.Namespace : string.Empty;
                    if (checkType)
                    {
                        Type elementType;
                        if (ele.Mapping is NullableMapping nullableMapping)
                        {
                            TypeDesc td = nullableMapping.BaseMapping!.TypeDesc!;
                            elementType = td.Type!;
                        }
                        else
                        {
                            elementType = ele.Mapping!.TypeDesc!.Type!;
                        }
 
                        if (elementType.IsAssignableFrom(checkTypeSource!.Type))
                        {
                            foundElement = true;
                        }
                    }
                    else if (ele.Name == Reader.LocalName && ns == Reader.NamespaceURI)
                    {
                        foundElement = true;
                    }
 
                    if (foundElement)
                    {
                        e = ele;
                        member = m;
                        elementIndex = i;
                        break;
                    }
                }
 
                if (foundElement)
                    break;
            }
 
            if (foundElement)
            {
                if (checkType)
                {
                    member!.Source!(checkTypeSource!.RefObject!);
 
                    if (member.FixupIndex >= 0)
                    {
                        fixup!.Ids![member.FixupIndex] = checkTypeSource.Id;
                    }
                }
                else
                {
                    string? ns = e!.Form == XmlSchemaForm.Qualified ? e.Namespace : string.Empty;
                    bool isList = member!.Mapping.TypeDesc!.IsArrayLike && !member.Mapping.TypeDesc.IsArray;
                    WriteElement(e, isList && member.Mapping.TypeDesc.IsNullable, member.Mapping.ReadOnly, ns, member.FixupIndex, fixup, member);
                }
            }
            else
            {
                if (anyElementMember != null && anyElementMember.Mapping != null)
                {
                    MemberMapping anyElement = anyElementMember.Mapping;
                    member = anyElementMember;
                    ElementAccessor[] elements = anyElement.Elements!;
                    for (int i = 0; i < elements.Length; i++)
                    {
                        ElementAccessor element = elements[i];
                        if (element.Any && element.Name.Length == 0)
                        {
                            string? ns = element.Form == XmlSchemaForm.Qualified ? element.Namespace : string.Empty;
                            WriteElement(element, false, false, ns, fixup: fixup, member: member);
                            break;
                        }
                    }
                }
                else
                {
                    member = null;
                    ProcessUnknownNode(elementElseAction);
                }
            }
        }
 
        private object? WriteElement(ElementAccessor element, bool checkForNull, bool readOnly, string? defaultNamespace, int fixupIndex = -1, Fixup? fixup = null, Member? member = null)
        {
            object? value = null;
            if (element.Mapping is ArrayMapping arrayMapping)
            {
                value = WriteArray(arrayMapping, readOnly, fixupIndex, fixup, member);
            }
            else if (element.Mapping is NullableMapping nullableMapping)
            {
                value = WriteNullableMethod(nullableMapping, defaultNamespace);
            }
            else if (!element.Mapping!.IsSoap && (element.Mapping is PrimitiveMapping))
            {
                if (element.IsNullable && ReadNull())
                {
                    if (element.Mapping.TypeDesc!.IsValueType)
                    {
                        value = ReflectionCreateObject(element.Mapping.TypeDesc.Type!);
                    }
                    else
                    {
                        value = null;
                    }
                }
                else if ((element.Default != null && element.Default != DBNull.Value && element.Mapping.TypeDesc!.IsValueType)
                         && (Reader.IsEmptyElement))
                {
                    Reader.Skip();
                }
                else if (element.Mapping.TypeDesc!.Type == typeof(TimeSpan) && Reader.IsEmptyElement)
                {
                    Reader.Skip();
                    value = default(TimeSpan);
                }
                else if (element.Mapping.TypeDesc!.Type == typeof(DateTimeOffset) && Reader.IsEmptyElement)
                {
                    Reader.Skip();
                    value = default(DateTimeOffset);
                }
                else
                {
                    if (element.Mapping.TypeDesc == QnameTypeDesc)
                    {
                        value = ReadElementQualifiedName();
                    }
                    else
                    {
                        if (element.Mapping.TypeDesc.FormatterName == "ByteArrayBase64")
                        {
                            value = ToByteArrayBase64(false);
                        }
                        else if (element.Mapping.TypeDesc.FormatterName == "ByteArrayHex")
                        {
                            value = ToByteArrayHex(false);
                        }
                        else
                        {
                            Func<object, string> readFunc = (state) => ((XmlReader)state).ReadElementContentAsString();
                            value = WritePrimitive(element.Mapping, readFunc, Reader);
                        }
                    }
                }
            }
            else if (element.Mapping is StructMapping || (element.Mapping.IsSoap && element.Mapping is PrimitiveMapping))
            {
                TypeMapping mapping = element.Mapping;
                if (mapping.IsSoap)
                {
                    object? rre = fixupIndex >= 0 ?
                          ReadReferencingElement(mapping.TypeName, mapping.Namespace, out fixup!.Ids![fixupIndex])
                        : ReadReferencedElement(mapping.TypeName, mapping.Namespace);
 
                    if (!mapping.TypeDesc!.IsValueType || rre != null)
                    {
                        value = rre;
                        Referenced(value);
                    }
 
                    if (fixupIndex >= 0)
                    {
                        if (member == null)
                        {
                            throw new InvalidOperationException(SR.XmlInternalError);
                        }
 
                        member.Source!(value!);
                        return value;
                    }
                }
                else
                {
                    if (checkForNull && (member!.Source == null && member.ArraySource == null))
                    {
                        Reader.Skip();
                    }
                    else
                    {
                        value = WriteStructMethod(
                                mapping: (StructMapping)mapping,
                                isNullable: mapping.TypeDesc!.IsNullable && element.IsNullable,
                                checkType: true,
                                defaultNamespace: defaultNamespace
                                );
                    }
                }
            }
            else if (element.Mapping is SpecialMapping specialMapping)
            {
                switch (specialMapping.TypeDesc!.Kind)
                {
                    case TypeKind.Node:
                        bool isDoc = specialMapping.TypeDesc.FullName == typeof(XmlDocument).FullName;
                        if (isDoc)
                        {
                            value = ReadXmlDocument(!element.Any);
                        }
                        else
                        {
                            value = ReadXmlNode(!element.Any);
                        }
 
                        break;
                    case TypeKind.Serializable:
                        SerializableMapping sm = (SerializableMapping)element.Mapping;
                        // check to see if we need to do the derivation
                        bool flag = true;
                        if (sm.DerivedMappings != null)
                        {
                            XmlQualifiedName? tser = GetXsiType();
                            if (tser == null || QNameEqual(tser, sm.XsiType!.Name, defaultNamespace))
                            {
                            }
                            else
                            {
                                flag = false;
                            }
                        }
 
                        if (flag)
                        {
                            bool isWrappedAny = !element.Any && IsWildcard(sm);
                            value = ReadSerializable((IXmlSerializable)ReflectionCreateObject(sm.TypeDesc!.Type!)!, isWrappedAny);
                        }
 
                        if (sm.DerivedMappings != null)
                        {
                            // https://github.com/dotnet/runtime/issues/1401:
                            // To Support SpecialMapping Types Having DerivedMappings
                            throw new NotImplementedException("sm.DerivedMappings != null");
                            //WriteDerivedSerializable(sm, sm, source, isWrappedAny);
                            //WriteUnknownNode("UnknownNode", "null", null, true);
                        }
                        break;
                    default:
                        throw new InvalidOperationException(SR.XmlInternalError);
                }
            }
            else
            {
                throw new InvalidOperationException(SR.XmlInternalError);
            }
 
            member?.ChoiceSource?.Invoke(element.Name);
 
            if (member?.ArraySource != null)
            {
                member?.ArraySource(value!);
            }
            else
            {
                member?.Source?.Invoke(value!);
                member?.CheckSpecifiedSource?.Invoke(true);
            }
 
            return value;
        }
 
        private XmlSerializationReadCallback CreateXmlSerializationReadCallback(TypeMapping mapping)
        {
            if (mapping is StructMapping structMapping)
            {
                                object? WriteStruct() => WriteStructMethod(structMapping, mapping.TypeDesc!.IsNullable, true, defaultNamespace: null);
                return WriteStruct;
            }
            else if (mapping is EnumMapping enumMapping)
            {
                return () => WriteEnumMethodSoap(enumMapping);
            }
            else if (mapping is NullableMapping nullableMapping)
            {
                return () => WriteNullableMethod(nullableMapping, null);
            }
 
            return DummyReadArrayMethod;
        }
 
        private static void NoopAction(object? o)
        {
        }
 
        private object? DummyReadArrayMethod()
        {
            UnknownNode(null);
            return null;
        }
 
        private static Type GetMemberType(MemberInfo memberInfo)
        {
            Type memberType;
 
            if (memberInfo is FieldInfo fieldInfo)
            {
                memberType = fieldInfo.FieldType;
            }
            else if (memberInfo is PropertyInfo propertyInfo)
            {
                memberType = propertyInfo.PropertyType;
            }
            else
            {
                throw new InvalidOperationException(SR.XmlInternalError);
            }
 
            return memberType;
        }
 
        private static bool IsWildcard(SpecialMapping mapping)
        {
            if (mapping is SerializableMapping serializableMapping)
                return serializableMapping.IsAny;
 
            return mapping.TypeDesc!.CanBeElementValue;
        }
 
        private object? WriteArray(ArrayMapping arrayMapping, bool readOnly, int fixupIndex = -1, Fixup? fixup = null, Member? member = null)
        {
            object? o = null;
            if (arrayMapping.IsSoap)
            {
                object? rre;
                if (fixupIndex >= 0)
                {
                    rre = ReadReferencingElement(arrayMapping.TypeName, arrayMapping.Namespace, out fixup!.Ids![fixupIndex]);
                }
                else
                {
                    rre = ReadReferencedElement(arrayMapping.TypeName, arrayMapping.Namespace);
                }
 
                TypeDesc td = arrayMapping.TypeDesc!;
                if (rre != null)
                {
                    if (td.IsEnumerable || td.IsCollection)
                    {
                        WriteAddCollectionFixup(member!.GetSource!, member.Source!, rre, td, readOnly);
 
                        // member.Source has been set at this point.
                        // Setting the source to no-op to avoid setting the
                        // source again.
                        member.Source = NoopAction;
                    }
                    else
                    {
                        if (member == null)
                        {
                            throw new InvalidOperationException(SR.XmlInternalError);
                        }
 
                        member.Source!(rre);
                    }
                }
 
                o = rre;
            }
            else
            {
                if (!ReadNull())
                {
                    var memberMapping = new MemberMapping()
                    {
                        Elements = arrayMapping.Elements,
                        TypeDesc = arrayMapping.TypeDesc,
                        ReadOnly = readOnly
                    };
 
                    Type collectionType = memberMapping.TypeDesc!.Type!;
                    o = ReflectionCreateObject(memberMapping.TypeDesc.Type!);
 
                    if (memberMapping.ChoiceIdentifier != null)
                    {
                        // https://github.com/dotnet/runtime/issues/1400:
                        // To Support ArrayMapping Types Having ChoiceIdentifier
                        throw new NotImplementedException("memberMapping.ChoiceIdentifier != null");
                    }
 
                    var arrayMember = new Member(memberMapping);
                    arrayMember.Collection = new CollectionMember();
                    arrayMember.ArraySource = arrayMember.Collection.Add;
 
                    if ((readOnly && o == null) || Reader.IsEmptyElement)
                    {
                        Reader.Skip();
                    }
                    else
                    {
                        Reader.ReadStartElement();
                        Reader.MoveToContent();
                        while (Reader.NodeType != XmlNodeType.EndElement && Reader.NodeType != XmlNodeType.None)
                        {
                            WriteMemberElements(new Member[] { arrayMember }, UnknownNode, UnknownNode, null, null);
                            Reader.MoveToContent();
                        }
                        ReadEndElement();
                    }
 
                    SetCollectionObjectWithCollectionMember(ref o, arrayMember.Collection, collectionType);
                }
            }
 
            return o;
        }
 
        private object WritePrimitive(TypeMapping mapping, Func<object, string> readFunc, object funcState)
        {
            if (mapping is EnumMapping enumMapping)
            {
                return WriteEnumMethod(enumMapping, readFunc, funcState);
            }
            else if (mapping.TypeDesc == StringTypeDesc)
            {
                return readFunc(funcState);
            }
            else if (mapping.TypeDesc!.FormatterName == "String")
            {
                if (mapping.TypeDesc.CollapseWhitespace)
                {
                    return CollapseWhitespace(readFunc(funcState));
                }
                else
                {
                    return readFunc(funcState);
                }
            }
            else
            {
                if (!mapping.TypeDesc.HasCustomFormatter)
                {
                    string value = readFunc(funcState);
                    object retObj = mapping.TypeDesc.FormatterName switch
                    {
                        "Boolean" => XmlConvert.ToBoolean(value),
                        "Int32" => XmlConvert.ToInt32(value),
                        "Int16" => XmlConvert.ToInt16(value),
                        "Int64" => XmlConvert.ToInt64(value),
                        "Single" => XmlConvert.ToSingle(value),
                        "Double" => XmlConvert.ToDouble(value),
                        "Decimal" => XmlConvert.ToDecimal(value),
                        "Byte" => XmlConvert.ToByte(value),
                        "SByte" => XmlConvert.ToSByte(value),
                        "UInt16" => XmlConvert.ToUInt16(value),
                        "UInt32" => XmlConvert.ToUInt32(value),
                        "UInt64" => XmlConvert.ToUInt64(value),
                        "Guid" => XmlConvert.ToGuid(value),
                        "Char" => XmlConvert.ToChar(value),
                        "TimeSpan" => XmlConvert.ToTimeSpan(value),
                        "DateTimeOffset" => XmlConvert.ToDateTimeOffset(value),
                        _ => throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, $"unknown FormatterName: {mapping.TypeDesc.FormatterName}")),
                    };
                    return retObj;
                }
                else
                {
                    string methodName = $"To{mapping.TypeDesc.FormatterName}";
                    MethodInfo? method = typeof(XmlSerializationReader).GetMethod(methodName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, new Type[] { typeof(string) });
                    if (method == null)
                    {
                        throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, $"unknown FormatterName: {mapping.TypeDesc.FormatterName}"));
                    }
 
                    return method.Invoke(this, new object[] { readFunc(funcState) })!;
                }
            }
        }
 
        private object? WriteStructMethod(StructMapping mapping, bool isNullable, bool checkType, string? defaultNamespace)
        {
            if (mapping.IsSoap)
                return WriteEncodedStructMethod(mapping);
            else
                return WriteLiteralStructMethod(mapping, isNullable, checkType, defaultNamespace);
        }
 
        private object? WriteNullableMethod(NullableMapping nullableMapping, string? defaultNamespace)
        {
            object? o = Activator.CreateInstance(nullableMapping.TypeDesc!.Type!);
            if (!ReadNull())
            {
                ElementAccessor element = new ElementAccessor();
                element.Mapping = nullableMapping.BaseMapping;
                element.Any = false;
                element.IsNullable = nullableMapping.BaseMapping!.TypeDesc!.IsNullable;
 
                o = WriteElement(element, false, false, defaultNamespace);
            }
 
            return o;
        }
 
        private object WriteEnumMethod(EnumMapping mapping, Func<object, string> readFunc, object funcState)
        {
            Debug.Assert(!mapping.IsSoap, "mapping.IsSoap was true. Use WriteEnumMethodSoap for reading SOAP encoded enum value.");
            string source = readFunc(funcState);
            return WriteEnumMethod(mapping, source);
        }
 
        private object WriteEnumMethodSoap(EnumMapping mapping)
        {
            string source = Reader.ReadElementString();
            return WriteEnumMethod(mapping, source);
        }
 
        private object WriteEnumMethod(EnumMapping mapping, string source)
        {
            if (mapping.IsFlags)
            {
                Hashtable table = WriteHashtable(mapping);
                return Enum.ToObject(mapping.TypeDesc!.Type!, ToEnum(source, table, mapping.TypeDesc.Name));
            }
            else
            {
                foreach (ConstantMapping c in mapping.Constants!)
                {
                    if (string.Equals(c.XmlName, source))
                    {
                        return Enum.Parse(mapping.TypeDesc!.Type!, c.Name);
                    }
                }
 
                throw CreateUnknownConstantException(source, mapping.TypeDesc!.Type!);
            }
        }
 
        private static Hashtable WriteHashtable(EnumMapping mapping)
        {
            var h = new Hashtable();
            ConstantMapping[] constants = mapping.Constants!;
            for (int i = 0; i < constants.Length; i++)
            {
                h.Add(constants[i].XmlName, constants[i].Value);
            }
 
            return h;
        }
 
        private static object? ReflectionCreateObject(
            [DynamicallyAccessedMembers(TrimmerConstants.AllMethods)] Type type)
        {
            object? obj;
            if (type.IsArray)
            {
                obj = Activator.CreateInstance(type, 32);
            }
            else
            {
                ConstructorInfo? ci = GetDefaultConstructor(type);
                if (ci != null)
                {
                    obj = ci.Invoke(Array.Empty<object>());
                }
                else
                {
                    obj = Activator.CreateInstance(type);
                }
            }
 
            return obj;
        }
 
        private static ConstructorInfo? GetDefaultConstructor(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors
                | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type) =>
            type.IsValueType ? null :
            type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null);
 
        private object? WriteEncodedStructMethod(StructMapping structMapping)
        {
            if (structMapping.TypeDesc!.IsRoot)
                return null;
 
            Member[]? members = null;
 
            if (structMapping.TypeDesc.IsAbstract)
            {
                throw CreateAbstractTypeException(structMapping.TypeName!, structMapping.Namespace);
            }
            else
            {
                object? o = ReflectionCreateObject(structMapping.TypeDesc.Type!);
 
                MemberMapping[] mappings = TypeScope.GetSettableMembers(structMapping);
                members = new Member[mappings.Length];
                for (int i = 0; i < mappings.Length; i++)
                {
                    MemberMapping mapping = mappings[i];
                    var member = new Member(mapping);
 
                    TypeDesc td = member.Mapping.TypeDesc!;
                    if (td.IsCollection || td.IsEnumerable)
                    {
                        member.Source = (value) => WriteAddCollectionFixup(o!, member, value!);
                    }
                    else if (!member.Mapping.ReadOnly)
                    {
                        var setterDelegate = GetSetMemberValueDelegate(o!, member.Mapping.MemberInfo!.Name);
                        member.Source = (value) => setterDelegate(o, value);
                    }
                    else
                    {
                        member.Source = NoopAction;
                    }
 
                    members[i] = member;
                }
 
                Fixup? fixup = WriteMemberFixupBegin(members, o);
                UnknownNodeAction unknownNodeAction = (_) => UnknownNode(o);
                WriteAttributes(members, null, unknownNodeAction, ref o);
                Reader.MoveToElement();
                if (Reader.IsEmptyElement)
                {
                    Reader.Skip();
                    return o;
                }
 
                Reader.ReadStartElement();
                Reader.MoveToContent();
                while (Reader.NodeType != XmlNodeType.EndElement && Reader.NodeType != XmlNodeType.None)
                {
                    WriteMemberElements(members, UnknownNode, UnknownNode, null, null, fixup: fixup);
                    Reader.MoveToContent();
                }
 
                ReadEndElement();
                return o;
            }
        }
 
        private Fixup? WriteMemberFixupBegin(Member[] members, object? o)
        {
            int fixupCount = 0;
            foreach (Member member in members)
            {
                if (member.Mapping.Elements!.Length == 0)
                    continue;
 
                TypeMapping? mapping = member.Mapping.Elements[0].Mapping;
                if (mapping is StructMapping || mapping is ArrayMapping || mapping is PrimitiveMapping || mapping is NullableMapping)
                {
                    member.MultiRef = true;
                    member.FixupIndex = fixupCount++;
                }
            }
 
            Fixup? fixup;
            if (fixupCount > 0)
            {
                fixup = new Fixup(o, CreateWriteFixupMethod(members), fixupCount);
                AddFixup(fixup);
            }
            else
            {
                fixup = null;
            }
 
            return fixup;
        }
 
        private XmlSerializationFixupCallback CreateWriteFixupMethod(Member[] members)
        {
            return (fixupObject) =>
            {
                var fixup = (Fixup)fixupObject;
                string[] ids = fixup.Ids!;
                foreach (Member member in members)
                {
                    if (member.MultiRef)
                    {
                        int fixupIndex = member.FixupIndex;
                        if (ids[fixupIndex] != null)
                        {
                            var memberValue = GetTarget(ids[fixupIndex]);
                            member.Source!(memberValue);
                        }
                    }
                }
            };
        }
 
        private void WriteAddCollectionFixup(object o, Member member, object memberValue)
        {
            TypeDesc typeDesc = member.Mapping.TypeDesc!;
            bool readOnly = member.Mapping.ReadOnly;
            Func<object?> getSource = () => GetMemberValue(o, member.Mapping.MemberInfo!);
            var setterDelegate = GetSetMemberValueDelegate(o, member.Mapping.MemberInfo!.Name);
            Action<object?> setSource = (value) => setterDelegate(o, value);
            WriteAddCollectionFixup(getSource, setSource, memberValue, typeDesc, readOnly);
        }
 
        private object? WriteAddCollectionFixup(Func<object?> getSource, Action<object?> setSource, object memberValue, TypeDesc typeDesc, bool readOnly)
        {
            object? memberSource = getSource();
            if (memberSource == null)
            {
                if (readOnly)
                {
                    throw CreateReadOnlyCollectionException(typeDesc.CSharpName);
                }
 
                memberSource = ReflectionCreateObject(typeDesc.Type!);
                setSource(memberSource);
            }
 
            var collectionFixup = new CollectionFixup(
                memberSource,
                new XmlSerializationCollectionFixupCallback(GetCreateCollectionOfObjectsCallback(typeDesc.Type!)),
                memberValue);
 
            AddFixup(collectionFixup);
            return memberSource;
        }
 
        private static XmlSerializationCollectionFixupCallback GetCreateCollectionOfObjectsCallback(Type collectionType)
        {
            return (object? collection, object? collectionItems) =>
            {
                if (collectionItems == null)
                    return;
 
                if (collection == null)
                    return;
 
                var listOfItems = new List<object?>();
                if (collectionItems is IEnumerable enumerableItems)
                {
                    foreach (var item in enumerableItems)
                    {
                        listOfItems.Add(item);
                    }
                }
                else
                {
                    throw new InvalidOperationException(SR.XmlInternalError);
                }
 
                AddObjectsIntoTargetCollection(collection, listOfItems, collectionType);
            };
        }
 
        private object? WriteLiteralStructMethod(StructMapping structMapping, bool isNullable, bool checkType, string? defaultNamespace)
        {
            XmlQualifiedName? xsiType = checkType ? GetXsiType() : null;
            bool isNull = false;
            if (isNullable)
            {
                isNull = ReadNull();
            }
 
            if (checkType)
            {
                if (structMapping.TypeDesc!.IsRoot && isNull)
                {
                    if (xsiType != null)
                    {
                        return ReadTypedNull(xsiType);
                    }
                    else
                    {
                        if (structMapping.TypeDesc.IsValueType)
                        {
                            return ReflectionCreateObject(structMapping.TypeDesc.Type!);
                        }
                        else
                        {
                            return null;
                        }
                    }
                }
 
                object? o = null;
                if (xsiType == null || (!structMapping.TypeDesc.IsRoot && QNameEqual(xsiType, structMapping.TypeName, defaultNamespace)))
                {
                    if (structMapping.TypeDesc.IsRoot)
                    {
                        return ReadTypedPrimitive(new XmlQualifiedName(Soap.UrType, XmlReservedNs.NsXs));
                    }
                }
                else if (WriteDerivedTypes(out o, structMapping, xsiType, defaultNamespace, checkType, isNullable))
                {
                    return o;
                }
                else if (structMapping.TypeDesc.IsRoot && WriteEnumAndArrayTypes(out o, xsiType, defaultNamespace))
                {
                    return o;
                }
                else
                {
                    if (structMapping.TypeDesc.IsRoot)
                        return ReadTypedPrimitive(xsiType);
                    else
                        throw CreateUnknownTypeException(xsiType);
                }
            }
 
            if (structMapping.TypeDesc!.IsNullable && isNull)
            {
                return null;
            }
            else if (structMapping.TypeDesc.IsAbstract)
            {
                throw CreateAbstractTypeException(structMapping.TypeName!, structMapping.Namespace);
            }
            else
            {
                if (structMapping.TypeDesc.Type != null && typeof(XmlSchemaObject).IsAssignableFrom(structMapping.TypeDesc.Type))
                {
                    // https://github.com/dotnet/runtime/issues/1399:
                    // To Support Serializing XmlSchemaObject
                    throw new NotImplementedException(nameof(XmlSchemaObject));
                }
 
                object? o = ReflectionCreateObject(structMapping.TypeDesc.Type!)!;
 
                MemberMapping[] mappings = TypeScope.GetSettableMembers(structMapping);
                MemberMapping? anyText = null;
                MemberMapping? anyElement = null;
                Member? anyAttribute = null;
                Member? anyElementMember = null;
                Member? anyTextMember = null;
 
                bool isSequence = structMapping.HasExplicitSequence();
 
                if (isSequence)
                {
                    // https://github.com/dotnet/runtime/issues/1402:
                    // Currently the reflection based method treat this kind of type as normal types.
                    // But potentially we can do some optimization for types that have ordered properties.
                }
 
                var allMembersList = new List<Member>(mappings.Length);
 
                for (int i = 0; i < mappings.Length; i++)
                {
                    MemberMapping mapping = mappings[i];
                    var member = new Member(mapping);
 
                    if (mapping.Text != null)
                    {
                        anyText = mapping;
                    }
 
                    if (mapping.Attribute != null)
                    {
                        member.Source = (value) => SetOrAddValueToMember(o!, value!, member.Mapping.MemberInfo!);
 
                        if (mapping.Attribute.Any)
                        {
                            anyAttribute = member;
                        }
                    }
 
                    if (!isSequence)
                    {
                        // find anyElement if present.
                        for (int j = 0; j < mapping.Elements!.Length; j++)
                        {
                            if (mapping.Elements[j].Any && string.IsNullOrEmpty(mapping.Elements[j].Name))
                            {
                                anyElement = mapping;
                                break;
                            }
                        }
                    }
                    else if (mapping.IsParticle && !mapping.IsSequence)
                    {
                        structMapping.FindDeclaringMapping(mapping, out StructMapping? declaringMapping, structMapping.TypeName!);
                        throw new InvalidOperationException(SR.Format(SR.XmlSequenceHierarchy, structMapping.TypeDesc.FullName, mapping.Name, declaringMapping!.TypeDesc!.FullName, "Order"));
                    }
 
                    if (mapping.TypeDesc!.IsArrayLike)
                    {
                        if (member.Source == null && mapping.TypeDesc.IsArrayLike && !(mapping.Elements!.Length == 1 && mapping.Elements[0].Mapping is ArrayMapping))
                        {
                            member.Source = (item) =>
                            {
                                member.Collection ??= new CollectionMember();
 
                                member.Collection.Add(item);
                            };
                            member.ArraySource = member.Source;
                        }
                    }
 
                    if (member.Source == null)
                    {
                        var pi = member.Mapping.MemberInfo as PropertyInfo;
                        if (pi != null && typeof(IList).IsAssignableFrom(pi.PropertyType)
                            && (pi.SetMethod == null || !pi.SetMethod.IsPublic))
                        {
                            member.Source = (value) =>
                            {
                                var getOnlyList = (IList)pi.GetValue(o)!;
                                if (value is IList valueList)
                                {
                                    foreach (var v in valueList)
                                    {
                                        getOnlyList.Add(v);
                                    }
                                }
                                else
                                {
                                    getOnlyList.Add(value);
                                }
                            };
                        }
                        else
                        {
                            if (member.Mapping.Xmlns != null)
                            {
                                var xmlSerializerNamespaces = new XmlSerializerNamespaces();
                                var setMemberValue = GetSetMemberValueDelegate(o!, member.Mapping.Name);
                                setMemberValue(o, xmlSerializerNamespaces);
                                member.XmlnsSource = xmlSerializerNamespaces.Add;
                            }
                            else
                            {
                                var setterDelegate = GetSetMemberValueDelegate(o!, member.Mapping.Name);
                                member.Source = (value) => setterDelegate(o, value);
                            }
                        }
                    }
 
                    if (member.Mapping.CheckSpecified == SpecifiedAccessor.ReadWrite)
                    {
                        member.CheckSpecifiedSource = (_) =>
                        {
                            string specifiedMemberName = $"{member.Mapping.Name}Specified";
                            MethodInfo? specifiedMethodInfo = o!.GetType().GetMethod($"set_{specifiedMemberName}");
                            specifiedMethodInfo?.Invoke(o, new object[] { true });
                        };
                    }
 
                    ChoiceIdentifierAccessor? choice = mapping.ChoiceIdentifier;
                    if (choice != null && o != null)
                    {
                        member.ChoiceSource = (elementNameObject) =>
                        {
                            string? elementName = elementNameObject as string;
                            foreach (var name in choice.MemberIds!)
                            {
                                if (name == elementName)
                                {
                                    object choiceValue = Enum.Parse(choice.Mapping!.TypeDesc!.Type!, name);
                                    SetOrAddValueToMember(o, choiceValue, choice.MemberInfo!);
 
                                    break;
                                }
                            }
                        };
                    }
 
                    allMembersList.Add(member);
 
                    if (mapping == anyElement)
                    {
                        anyElementMember = member;
                    }
                    else if (mapping == anyText)
                    {
                        anyTextMember = member;
                    }
                }
 
                Member[] allMembers = allMembersList.ToArray();
 
                UnknownNodeAction unknownNodeAction = (_) => UnknownNode(o);
                WriteAttributes(allMembers, anyAttribute, unknownNodeAction, ref o);
 
                Reader.MoveToElement();
                if (Reader.IsEmptyElement)
                {
                    Reader.Skip();
                    return o;
                }
 
                Reader.ReadStartElement();
                bool IsSequenceAllMembers = IsSequence();
                if (IsSequenceAllMembers)
                {
                    // https://github.com/dotnet/runtime/issues/1402:
                    // Currently the reflection based method treat this kind of type as normal types.
                    // But potentially we can do some optimization for types that have ordered properties.
                }
 
                WriteMembers(allMembers, unknownNodeAction, unknownNodeAction, anyElementMember, anyTextMember);
 
                foreach (Member member in allMembers)
                {
                    if (member.Collection != null)
                    {
                        MemberInfo[] memberInfos = o!.GetType().GetMember(member.Mapping.Name);
                        MemberInfo memberInfo = memberInfos[0];
                        object? collection = null;
                        SetCollectionObjectWithCollectionMember(ref collection, member.Collection, member.Mapping.TypeDesc!.Type!);
                        var setMemberValue = GetSetMemberValueDelegate(o, memberInfo.Name);
                        setMemberValue(o, collection);
                    }
                }
 
                ReadEndElement();
                return o;
            }
        }
 
        private bool WriteEnumAndArrayTypes(out object? o, XmlQualifiedName xsiType, string? defaultNamespace)
        {
            foreach (var m in _mapping.Scope!.TypeMappings)
            {
                if (m is EnumMapping enumMapping)
                {
                    if (QNameEqual(xsiType, enumMapping.TypeName, defaultNamespace))
                    {
                        Reader.ReadStartElement();
                        Func<object, string> functor = (state) =>
                        {
                            var reader = (ReflectionXmlSerializationReader)state;
                            return reader.CollapseWhitespace(reader.Reader.ReadString());
                        };
                        o = WriteEnumMethod(enumMapping, functor, this);
                        ReadEndElement();
                        return true;
                    }
 
                    continue;
                }
 
                if (m is ArrayMapping arrayMapping)
                {
                    if (QNameEqual(xsiType, arrayMapping.TypeName, defaultNamespace))
                    {
                        o = WriteArray(arrayMapping, false);
                        return true;
                    }
 
                    continue;
                }
            }
 
            o = null;
            return false;
        }
 
        private bool WriteDerivedTypes(out object? o, StructMapping mapping, XmlQualifiedName xsiType, string? defaultNamespace, bool checkType, bool isNullable)
        {
            for (StructMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
            {
                if (QNameEqual(xsiType, derived.TypeName, defaultNamespace))
                {
                    o = WriteStructMethod(derived, isNullable, checkType, defaultNamespace);
                    return true;
                }
 
                if (WriteDerivedTypes(out o, derived, xsiType, defaultNamespace, checkType, isNullable))
                {
                    return true;
                }
            }
 
            o = null;
            return false;
        }
 
        private void WriteAttributes(Member[] members, Member? anyAttribute, UnknownNodeAction elseCall, ref object? o)
        {
            Member? xmlnsMember = null;
            var attributes = new List<AttributeAccessor>();
            foreach (Member member in members)
            {
                if (member.Mapping.Xmlns != null)
                {
                    xmlnsMember = member;
                    break;
                }
            }
 
            while (Reader.MoveToNextAttribute())
            {
                bool memberFound = false;
                foreach (Member member in members)
                {
                    if (member.Mapping.Xmlns != null || member.Mapping.Ignore)
                    {
                        continue;
                    }
 
                    AttributeAccessor? attribute = member.Mapping.Attribute;
 
                    if (attribute == null) continue;
                    if (attribute.Any) continue;
 
                    attributes.Add(attribute);
 
                    if (attribute.IsSpecialXmlNamespace)
                    {
                        memberFound = XmlNodeEqual(Reader, attribute.Name, XmlReservedNs.NsXml);
                    }
                    else
                    {
                        memberFound = XmlNodeEqual(Reader, attribute.Name, attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : string.Empty);
                    }
 
                    if (memberFound)
                    {
                        WriteAttribute(member);
                        memberFound = true;
                        break;
                    }
                }
 
                if (memberFound)
                {
                    continue;
                }
 
                bool flag2 = false;
                if (xmlnsMember != null)
                {
                    if (IsXmlnsAttribute(Reader.Name))
                    {
                        Debug.Assert(xmlnsMember.XmlnsSource != null, "Xmlns member's source was not set.");
                        xmlnsMember.XmlnsSource(Reader.Name.Length == 5 ? string.Empty : Reader.LocalName, Reader.Value);
                    }
                    else
                    {
                        flag2 = true;
                    }
                }
                else if (!IsXmlnsAttribute(Reader.Name))
                {
                    flag2 = true;
                }
 
                if (flag2)
                {
                    if (anyAttribute != null)
                    {
                        var attr = (Document.ReadNode(Reader) as XmlAttribute)!;
                        ParseWsdlArrayType(attr);
                        WriteAttribute(anyAttribute, attr);
                    }
                    else
                    {
                        elseCall(o);
                    }
                }
            }
        }
 
        private void WriteAttribute(Member member, object? attr = null)
        {
            AttributeAccessor attribute = member.Mapping.Attribute!;
            object? value = null;
            if (attribute.Mapping is SpecialMapping special)
            {
                if (special.TypeDesc!.Kind == TypeKind.Attribute)
                {
                    value = attr;
                }
                else if (special.TypeDesc.CanBeAttributeValue)
                {
                    // https://github.com/dotnet/runtime/issues/1398:
                    // To Support special.TypeDesc.CanBeAttributeValue == true
                    throw new NotImplementedException("special.TypeDesc.CanBeAttributeValue");
                }
                else
                    throw new InvalidOperationException(SR.XmlInternalError);
            }
            else
            {
                if (attribute.IsList)
                {
                    string listValues = Reader.Value;
                    string[] vals = listValues.Split(null);
                    Array arrayValue = Array.CreateInstance(member.Mapping.TypeDesc!.Type!.GetElementType()!, vals.Length);
                    for (int i = 0; i < vals.Length; i++)
                    {
                        arrayValue.SetValue(WritePrimitive(attribute.Mapping!, (state) => ((string[])state)[i], vals), i);
                    }
 
                    value = arrayValue;
                }
                else
                {
                    value = WritePrimitive(attribute.Mapping!, (state) => ((XmlReader)state).Value, Reader);
                }
            }
 
            member.Source!(value);
 
            if (member.Mapping.CheckSpecified == SpecifiedAccessor.ReadWrite)
            {
                member.CheckSpecifiedSource?.Invoke(null);
            }
        }
 
        private static void SetOrAddValueToMember(object o, object value, MemberInfo memberInfo)
        {
            Type memberType = GetMemberType(memberInfo);
 
            if (memberType == value.GetType())
            {
                var setMemberValue = GetSetMemberValueDelegate(o, memberInfo.Name);
                setMemberValue(o, value);
            }
            else if (memberType.IsArray)
            {
                AddItemInArrayMember(o, memberInfo, memberType, value);
            }
            else
            {
                var setMemberValue = GetSetMemberValueDelegate(o, memberInfo.Name);
                setMemberValue(o, value);
            }
        }
 
        private static void AddItemInArrayMember(object o, MemberInfo memberInfo, Type memberType, object item)
        {
            var currentArray = (Array?)GetMemberValue(o, memberInfo);
            int length;
            if (currentArray == null)
            {
                length = 0;
            }
            else
            {
                length = currentArray.Length;
            }
 
            var newArray = Array.CreateInstance(memberType.GetElementType()!, length + 1);
            if (currentArray != null)
            {
                Array.Copy(currentArray, newArray, length);
            }
 
            newArray.SetValue(item, length);
            var setMemberValue = GetSetMemberValueDelegate(o, memberInfo.Name);
            setMemberValue(o, newArray);
        }
 
        // WriteXmlNodeEqual
        private static bool XmlNodeEqual(XmlReader source, string name, string? ns)
        {
            return source.LocalName == name && string.Equals(source.NamespaceURI, ns);
        }
 
        private static bool QNameEqual(XmlQualifiedName xsiType, string? name, string? defaultNamespace)
        {
            return xsiType.Name == name && string.Equals(xsiType.Namespace, defaultNamespace);
        }
 
        private void CreateUnknownNodeException(object? o)
        {
            CreateUnknownNodeException();
        }
 
        internal sealed class CollectionMember : List<object?>
        {
        }
 
        internal sealed class Member
        {
            public MemberMapping Mapping;
            public CollectionMember? Collection;
            public int FixupIndex = -1;
            public bool MultiRef;
            public Action<object?>? Source;
            public Func<object?>? GetSource;
            public Action<object>? ArraySource;
            public Action<object?>? CheckSpecifiedSource;
            public Action<object>? ChoiceSource;
            public Action<string, string>? XmlnsSource;
 
            public Member(MemberMapping mapping)
            {
                Mapping = mapping;
            }
        }
 
        internal sealed class CheckTypeSource
        {
            public string? Id { get; set; }
            public bool IsObject { get; set; }
            public Type? Type { get; set; }
            public object? RefObject { get; set; }
        }
 
        internal sealed class ObjectHolder
        {
            public object? Object;
        }
    }
 
    internal static class ReflectionXmlSerializationReaderHelper
    {
        public delegate void SetMemberValueDelegate(object? o, object? val);
 
        public static SetMemberValueDelegate GetSetMemberValueDelegateWithType<TObj, TParam>(MemberInfo memberInfo)
        {
            Debug.Assert(!typeof(TObj).IsValueType);
            Action<TObj, TParam>? setTypedDelegate = null;
            if (memberInfo is PropertyInfo propInfo)
            {
                var setMethod = propInfo.GetSetMethod(true);
                if (setMethod == null)
                {
                    return propInfo.SetValue;
                }
 
                setTypedDelegate = setMethod.CreateDelegate<Action<TObj, TParam>>();
            }
            else if (memberInfo is FieldInfo fieldInfo)
            {
                var objectParam = Expression.Parameter(typeof(TObj));
                var valueParam = Expression.Parameter(typeof(TParam));
                var fieldExpr = Expression.Field(objectParam, fieldInfo);
                var assignExpr = Expression.Assign(fieldExpr, valueParam);
                setTypedDelegate = Expression.Lambda<Action<TObj, TParam>>(assignExpr, objectParam, valueParam).Compile();
            }
 
            return delegate (object? o, object? p)
            {
                setTypedDelegate!((TObj)o!, (TParam)p!);
            };
        }
    }
}