File: System\Runtime\Serialization\SchemaExporter.cs
Web Access
Project: src\src\libraries\System.Private.DataContractSerialization\src\System.Private.DataContractSerialization.csproj (System.Private.DataContractSerialization)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.DataContracts;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
 
namespace System.Runtime.Serialization
{
    internal sealed class SchemaExporter
    {
        private readonly XmlSchemaSet _schemas;
        private XmlDocument? _xmlDoc;
        private DataContractSet _dataContractSet;
 
        internal SchemaExporter(XmlSchemaSet schemas, DataContractSet dataContractSet)
        {
            _schemas = schemas;
            _dataContractSet = dataContractSet;
        }
 
        private XmlSchemaSet Schemas
        {
            get { return _schemas; }
        }
 
        private XmlDocument XmlDoc => _xmlDoc ??= new XmlDocument();
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal void Export()
        {
            try
            {
                // Remove this if we decide to publish serialization schema at well-known location
                ExportSerializationSchema();
                foreach (KeyValuePair<XmlQualifiedName, DataContract> pair in _dataContractSet.Contracts)
                {
                    DataContract dataContract = pair.Value;
                    if (!_dataContractSet.IsContractProcessed(dataContract))
                    {
                        ExportDataContract(dataContract);
                        _dataContractSet.SetContractProcessed(dataContract);
                    }
                }
            }
            finally
            {
                _xmlDoc = null;
                _dataContractSet = null!;
            }
        }
 
        private void ExportSerializationSchema()
        {
            if (!Schemas.Contains(Globals.SerializationNamespace))
            {
                StringReader reader = new StringReader(Globals.SerializationSchema);
                XmlSchema? schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null);
                if (schema == null)
                    throw new InvalidOperationException(SR.Format(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace));
                Schemas.Add(schema);
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private void ExportDataContract(DataContract dataContract)
        {
            if (dataContract.IsBuiltInDataContract)
                return;
            else if (dataContract is XmlDataContract)
                ExportXmlDataContract((XmlDataContract)dataContract);
            else
            {
                XmlSchema schema = GetSchema(dataContract.XmlName.Namespace);
 
                if (dataContract is ClassDataContract classDataContract)
                {
                    if (classDataContract.IsISerializable)
                        ExportISerializableDataContract(classDataContract, schema);
                    else
                        ExportClassDataContract(classDataContract, schema);
                }
                else if (dataContract is CollectionDataContract)
                    ExportCollectionDataContract((CollectionDataContract)dataContract, schema);
                else if (dataContract is EnumDataContract)
                    ExportEnumDataContract((EnumDataContract)dataContract, schema);
                ExportTopLevelElement(dataContract, schema);
                Schemas.Reprocess(schema);
            }
        }
 
        private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSchema? schema)
        {
            if (schema == null || dataContract.XmlName.Namespace != dataContract.TopLevelElementNamespace!.Value)
                schema = GetSchema(dataContract.TopLevelElementNamespace!.Value);
 
            XmlSchemaElement topLevelElement = new XmlSchemaElement();
            topLevelElement.Name = dataContract.TopLevelElementName!.Value;
            SetElementType(topLevelElement, dataContract, schema);
            topLevelElement.IsNillable = true;
            schema.Items.Add(topLevelElement);
            return topLevelElement;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = classDataContract.XmlName.Name;
            schema.Items.Add(type);
            XmlElement? genericInfoElement = null;
            if (classDataContract.UnderlyingType.IsGenericType)
                genericInfoElement = ExportGenericInfo(classDataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
 
            XmlSchemaSequence rootSequence = new XmlSchemaSequence();
            for (int i = 0; i < classDataContract.Members!.Count; i++)
            {
                DataMember dataMember = classDataContract.Members[i];
 
                XmlSchemaElement element = new XmlSchemaElement();
                element.Name = dataMember.Name;
                XmlElement? actualTypeElement = null;
                DataContract memberTypeContract = _dataContractSet.GetMemberTypeDataContract(dataMember);
                if (CheckIfMemberHasConflict(dataMember))
                {
                    element.SchemaTypeName = AnytypeQualifiedName;
                    actualTypeElement = ExportActualType(memberTypeContract.XmlName);
                    SchemaHelper.AddSchemaImport(memberTypeContract.XmlName.Namespace, schema);
                }
                else
                    SetElementType(element, memberTypeContract, schema);
                SchemaHelper.AddElementForm(element, schema);
                if (dataMember.IsNullable)
                    element.IsNillable = true;
                if (!dataMember.IsRequired)
                    element.MinOccurs = 0;
 
                element.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(dataMember), ExportEmitDefaultValue(dataMember));
                rootSequence.Items.Add(element);
            }
 
            XmlElement? isValueTypeElement = null;
            if (classDataContract.BaseClassContract != null)
            {
                XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseClassContract.XmlName, schema);
                extension.Particle = rootSequence;
                if (classDataContract.IsReference && !classDataContract.BaseClassContract.IsReference)
                {
                    AddReferenceAttributes(extension.Attributes, schema);
                }
            }
            else
            {
                type.Particle = rootSequence;
                if (classDataContract.IsValueType)
                    isValueTypeElement = GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(classDataContract.IsValueType), schema);
                if (classDataContract.IsReference)
                    AddReferenceAttributes(type.Attributes, schema);
            }
            type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(classDataContract), isValueTypeElement);
        }
 
        private static void AddReferenceAttributes(XmlSchemaObjectCollection attributes, XmlSchema schema)
        {
            SchemaHelper.AddSchemaImport(Globals.SerializationNamespace, schema);
            schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
            attributes.Add(IdAttribute);
            attributes.Add(RefAttribute);
        }
 
        private static void SetElementType(XmlSchemaElement element, DataContract dataContract, XmlSchema schema)
        {
            if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous)
            {
                element.SchemaType = xmlDataContract.XsdType;
            }
            else
            {
                element.SchemaTypeName = dataContract.XmlName;
 
                if (element.SchemaTypeName.Namespace.Equals(Globals.SerializationNamespace))
                    schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
 
                SchemaHelper.AddSchemaImport(dataContract.XmlName.Namespace, schema);
            }
        }
 
        private static bool CheckIfMemberHasConflict(DataMember dataMember)
        {
            if (dataMember.HasConflictingNameAndType)
                return true;
 
            DataMember? conflictingMember = dataMember.ConflictingMember;
            while (conflictingMember != null)
            {
                if (conflictingMember.HasConflictingNameAndType)
                    return true;
                conflictingMember = conflictingMember.ConflictingMember;
            }
 
            return false;
        }
 
        private XmlElement? ExportEmitDefaultValue(DataMember dataMember)
        {
            if (dataMember.EmitDefaultValue)
                return null;
            XmlElement defaultValueElement = XmlDoc.CreateElement(DefaultValueAnnotation.Name, DefaultValueAnnotation.Namespace);
            XmlAttribute emitDefaultValueAttribute = XmlDoc.CreateAttribute(Globals.EmitDefaultValueAttribute);
            emitDefaultValueAttribute.Value = Globals.False;
            defaultValueElement.Attributes.Append(emitDefaultValueAttribute);
            return defaultValueElement;
        }
 
        private XmlElement ExportActualType(XmlQualifiedName typeName)
        {
            return ExportActualType(typeName, XmlDoc);
        }
 
        private static XmlElement ExportActualType(XmlQualifiedName typeName, XmlDocument xmlDoc)
        {
            XmlElement actualTypeElement = xmlDoc.CreateElement(ActualTypeAnnotationName.Name, ActualTypeAnnotationName.Namespace);
 
            XmlAttribute nameAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNameAttribute);
            nameAttribute.Value = typeName.Name;
            actualTypeElement.Attributes.Append(nameAttribute);
 
            XmlAttribute nsAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNamespaceAttribute);
            nsAttribute.Value = typeName.Namespace;
            actualTypeElement.Attributes.Append(nsAttribute);
 
            return actualTypeElement;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlElement ExportGenericInfo(Type clrType, string elementName, string elementNs)
        {
            Type? itemType;
            int nestedCollectionLevel = 0;
            while (CollectionDataContract.IsCollection(clrType, out itemType))
            {
                if (DataContract.GetBuiltInDataContract(clrType) != null
                    || CollectionDataContract.IsCollectionDataContract(clrType))
                {
                    break;
                }
                clrType = itemType;
                nestedCollectionLevel++;
            }
 
            Type[]? genericArguments = null;
            List<int>? genericArgumentCounts = null;
            if (clrType.IsGenericType)
            {
                genericArguments = clrType.GetGenericArguments();
                string typeName;
                if (clrType.DeclaringType == null)
                    typeName = clrType.Name;
                else
                {
                    int nsLen = (clrType.Namespace == null) ? 0 : clrType.Namespace.Length;
                    if (nsLen > 0)
                        nsLen++; //include the . following namespace
                    typeName = DataContract.GetClrTypeFullName(clrType).Substring(nsLen).Replace('+', '.');
                }
                int iParam = typeName.IndexOf('[');
                if (iParam >= 0)
                    typeName = typeName.Substring(0, iParam);
                genericArgumentCounts = DataContract.GetDataContractNameForGenericName(typeName, null);
                clrType = clrType.GetGenericTypeDefinition();
            }
            XmlQualifiedName dcqname = DataContract.GetXmlName(clrType);
            if (nestedCollectionLevel > 0)
            {
                string collectionName = dcqname.Name;
                for (int n = 0; n < nestedCollectionLevel; n++)
                    collectionName = Globals.ArrayPrefix + collectionName;
                dcqname = new XmlQualifiedName(collectionName, DataContract.GetCollectionNamespace(dcqname.Namespace));
            }
            XmlElement typeElement = XmlDoc.CreateElement(elementName, elementNs);
 
            XmlAttribute nameAttribute = XmlDoc.CreateAttribute(Globals.GenericNameAttribute);
            nameAttribute.Value = genericArguments != null ? XmlConvert.DecodeName(dcqname.Name) : dcqname.Name;
            //nameAttribute.Value = dcqname.Name;
            typeElement.Attributes.Append(nameAttribute);
 
            XmlAttribute nsAttribute = XmlDoc.CreateAttribute(Globals.GenericNamespaceAttribute);
            nsAttribute.Value = dcqname.Namespace;
            typeElement.Attributes.Append(nsAttribute);
 
            if (genericArguments != null)
            {
                int argIndex = 0;
                int nestedLevel = 0;
                Debug.Assert(genericArgumentCounts != null);
                foreach (int genericArgumentCount in genericArgumentCounts)
                {
                    for (int i = 0; i < genericArgumentCount; i++, argIndex++)
                    {
                        XmlElement argumentElement = ExportGenericInfo(genericArguments[argIndex], Globals.GenericParameterLocalName, Globals.SerializationNamespace);
                        if (nestedLevel > 0)
                        {
                            XmlAttribute nestedLevelAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
                            nestedLevelAttribute.Value = nestedLevel.ToString(CultureInfo.InvariantCulture);
                            argumentElement.Attributes.Append(nestedLevelAttribute);
                        }
                        typeElement.AppendChild(argumentElement);
                    }
                    nestedLevel++;
                }
                if (genericArgumentCounts[nestedLevel - 1] == 0)
                {
                    XmlAttribute typeNestedLevelsAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
                    typeNestedLevelsAttribute.Value = genericArgumentCounts.Count.ToString(CultureInfo.InvariantCulture);
                    typeElement.Attributes.Append(typeNestedLevelsAttribute);
                }
            }
 
            return typeElement;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlElement? ExportSurrogateData(object key)
        {
            object? surrogateData = _dataContractSet.GetSurrogateData(key);
            if (surrogateData == null)
                return null;
            StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
            XmlWriterSettings writerSettings = new XmlWriterSettings();
            writerSettings.OmitXmlDeclaration = true;
            XmlWriter xmlWriter = XmlWriter.Create(stringWriter, writerSettings);
            Collection<Type> knownTypes = new Collection<Type>();
            if (_dataContractSet.SerializationExtendedSurrogateProvider != null)
                DataContractSurrogateCaller.GetKnownCustomDataTypes(_dataContractSet.SerializationExtendedSurrogateProvider, knownTypes);
            DataContractSerializer serializer = new DataContractSerializer(Globals.TypeOfObject,
                SurrogateDataAnnotationName.Name, SurrogateDataAnnotationName.Namespace, knownTypes,
                ignoreExtensionDataObject: false, preserveObjectReferences: true);
            serializer.WriteObject(xmlWriter, surrogateData);
            xmlWriter.Flush();
            return (XmlElement?)XmlDoc.ReadNode(XmlReader.Create(new StringReader(stringWriter.ToString())));
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = collectionDataContract.XmlName.Name;
            schema.Items.Add(type);
            XmlElement? genericInfoElement = null, isDictionaryElement = null;
            if (collectionDataContract.UnderlyingType.IsGenericType && CollectionDataContract.IsCollectionDataContract(collectionDataContract.UnderlyingType))
                genericInfoElement = ExportGenericInfo(collectionDataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
            if (collectionDataContract.IsDictionary)
                isDictionaryElement = ExportIsDictionary();
            type.Annotation = GetSchemaAnnotation(isDictionaryElement, genericInfoElement, ExportSurrogateData(collectionDataContract));
 
            XmlSchemaSequence rootSequence = new XmlSchemaSequence();
 
            XmlSchemaElement element = new XmlSchemaElement();
            element.Name = collectionDataContract.ItemName;
            element.MinOccurs = 0;
            element.MaxOccursString = Globals.OccursUnbounded;
            if (collectionDataContract.IsDictionary)
            {
                ClassDataContract keyValueContract = (collectionDataContract.ItemContract as ClassDataContract)!;
                XmlSchemaComplexType keyValueType = new XmlSchemaComplexType();
                XmlSchemaSequence keyValueSequence = new XmlSchemaSequence();
                foreach (DataMember dataMember in keyValueContract.Members!)
                {
                    XmlSchemaElement keyValueElement = new XmlSchemaElement();
                    keyValueElement.Name = dataMember.Name;
                    SetElementType(keyValueElement, _dataContractSet.GetMemberTypeDataContract(dataMember), schema);
                    SchemaHelper.AddElementForm(keyValueElement, schema);
                    if (dataMember.IsNullable)
                        keyValueElement.IsNillable = true;
                    keyValueElement.Annotation = GetSchemaAnnotation(ExportSurrogateData(dataMember));
                    keyValueSequence.Items.Add(keyValueElement);
                }
                keyValueType.Particle = keyValueSequence;
                element.SchemaType = keyValueType;
            }
            else
            {
                if (collectionDataContract.IsItemTypeNullable)
                    element.IsNillable = true;
                DataContract itemContract = _dataContractSet.GetItemTypeDataContract(collectionDataContract);
                SetElementType(element, itemContract, schema);
            }
            SchemaHelper.AddElementForm(element, schema);
            rootSequence.Items.Add(element);
 
            type.Particle = rootSequence;
 
            if (collectionDataContract.IsReference)
                AddReferenceAttributes(type.Attributes, schema);
        }
 
        private XmlElement ExportIsDictionary()
        {
            XmlElement isDictionaryElement = XmlDoc.CreateElement(IsDictionaryAnnotationName.Name, IsDictionaryAnnotationName.Namespace);
            isDictionaryElement.InnerText = Globals.True;
            return isDictionaryElement;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private void ExportEnumDataContract(EnumDataContract enumDataContract, XmlSchema schema)
        {
            XmlSchemaSimpleType type = new XmlSchemaSimpleType();
            type.Name = enumDataContract.XmlName.Name;
            XmlElement? actualTypeElement = (enumDataContract.BaseContractName == DefaultEnumBaseTypeName) ? null : ExportActualType(enumDataContract.BaseContractName);
            type.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(enumDataContract));
            schema.Items.Add(type);
 
            XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
            restriction.BaseTypeName = StringQualifiedName;
            SchemaHelper.AddSchemaImport(enumDataContract.BaseContractName.Namespace, schema);
            if (enumDataContract.Values != null)
            {
                for (int i = 0; i < enumDataContract.Values.Count; i++)
                {
                    XmlSchemaEnumerationFacet facet = new XmlSchemaEnumerationFacet();
                    facet.Value = enumDataContract.Members[i].Name;
                    if (enumDataContract.Values[i] != GetDefaultEnumValue(enumDataContract.IsFlags, i))
                        facet.Annotation = GetSchemaAnnotation(EnumerationValueAnnotationName, enumDataContract.GetStringFromEnumValue(enumDataContract.Values[i]), schema);
                    restriction.Facets.Add(facet);
                }
            }
            if (enumDataContract.IsFlags)
            {
                XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList();
                XmlSchemaSimpleType anonymousType = new XmlSchemaSimpleType();
                anonymousType.Content = restriction;
                list.ItemType = anonymousType;
                type.Content = list;
            }
            else
                type.Content = restriction;
        }
 
        internal static long GetDefaultEnumValue(bool isFlags, int index)
        {
            return isFlags ? (long)Math.Pow(2, index) : index;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = dataContract.XmlName.Name;
            schema.Items.Add(type);
            XmlElement? genericInfoElement = null;
            if (dataContract.UnderlyingType.IsGenericType)
                genericInfoElement = ExportGenericInfo(dataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
 
            XmlElement? isValueTypeElement = null;
            if (dataContract.BaseClassContract != null)
            {
                _ = CreateTypeContent(type, dataContract.BaseClassContract.XmlName, schema);
            }
            else
            {
                schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
                type.Particle = ISerializableSequence;
                XmlSchemaAttribute iSerializableFactoryTypeAttribute = ISerializableFactoryTypeAttribute;
                type.Attributes.Add(iSerializableFactoryTypeAttribute);
                SchemaHelper.AddSchemaImport(ISerializableFactoryTypeAttribute.RefName.Namespace, schema);
                if (dataContract.IsValueType)
                    isValueTypeElement = GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(dataContract.IsValueType), schema);
            }
            type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(dataContract), isValueTypeElement);
        }
 
        private static XmlSchemaComplexContentExtension CreateTypeContent(XmlSchemaComplexType type, XmlQualifiedName baseTypeName, XmlSchema schema)
        {
            SchemaHelper.AddSchemaImport(baseTypeName.Namespace, schema);
 
            XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
            extension.BaseTypeName = baseTypeName;
            type.ContentModel = new XmlSchemaComplexContent();
            type.ContentModel.Content = extension;
 
            return extension;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private void ExportXmlDataContract(XmlDataContract dataContract)
        {
            XmlQualifiedName? typeQName;
            bool hasRoot;
            XmlSchemaType? xsdType;
 
            Type clrType = dataContract.UnderlyingType;
            if (!IsSpecialXmlType(clrType, out typeQName, out xsdType, out hasRoot))
                if (!InvokeSchemaProviderMethod(clrType, _schemas, out typeQName, out xsdType, out hasRoot))
                    InvokeGetSchemaMethod(clrType, _schemas, typeQName);
 
            if (hasRoot)
            {
                if (!(typeQName.Equals(dataContract.XmlName)))
                {
                    Debug.Fail("XML data contract type name does not match schema name");
                }
 
                XmlSchema? schema;
                if (SchemaHelper.GetSchemaElement(Schemas,
                    new XmlQualifiedName(dataContract.TopLevelElementName!.Value, dataContract.TopLevelElementNamespace!.Value),
                    out schema) == null)
                {
                    XmlSchemaElement topLevelElement = ExportTopLevelElement(dataContract, schema);
                    topLevelElement.IsNillable = dataContract.IsTopLevelElementNullable;
                    ReprocessAll(_schemas);
                }
 
                XmlSchemaType? anonymousType = xsdType;
                xsdType = SchemaHelper.GetSchemaType(_schemas, typeQName, out schema);
                if (anonymousType == null && xsdType == null && typeQName.Namespace != XmlSchema.Namespace)
                {
                    throw new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeQName, DataContract.GetClrTypeFullName(clrType)));
                }
                if (xsdType != null)
                {
                    xsdType.Annotation = GetSchemaAnnotation(
                                           ExportSurrogateData(dataContract),
                                           dataContract.IsValueType ?
                                             GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(dataContract.IsValueType), schema!) :
                                             null
                                         );
                }
            }
        }
 
        private static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate items
        {
            Hashtable elements = new Hashtable();
            Hashtable types = new Hashtable();
            XmlSchema[] schemaArray = new XmlSchema[schemas.Count];
            schemas.CopyTo(schemaArray, 0);
            for (int i = 0; i < schemaArray.Length; i++)
            {
                XmlSchema schema = schemaArray[i];
                XmlSchemaObject[] itemArray = new XmlSchemaObject[schema.Items.Count];
                schema.Items.CopyTo(itemArray, 0);
                for (int j = 0; j < itemArray.Length; j++)
                {
                    XmlSchemaObject item = itemArray[j];
                    Hashtable items;
                    XmlQualifiedName qname;
                    if (item is XmlSchemaElement)
                    {
                        items = elements;
                        qname = new XmlQualifiedName(((XmlSchemaElement)item).Name, schema.TargetNamespace);
                    }
                    else if (item is XmlSchemaType)
                    {
                        items = types;
                        qname = new XmlQualifiedName(((XmlSchemaType)item).Name, schema.TargetNamespace);
                    }
                    else
                        continue;
                    object? otherItem = items[qname];
                    if (otherItem != null)
                    {
                        schema.Items.Remove(item);
                    }
                    else
                        items.Add(qname, item);
                }
                schemas.Reprocess(schema);
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot)
        {
            if (IsSpecialXmlType(type, out xmlName!, out xsdType, out hasRoot))
                return;
            XmlSchemaSet schemas = new XmlSchemaSet();
            schemas.XmlResolver = null;
            InvokeSchemaProviderMethod(type, schemas, out xmlName, out xsdType, out hasRoot);
            if (string.IsNullOrEmpty(xmlName.Name))
                throw new InvalidDataContractException(SR.Format(SR.InvalidXmlDataContractName, DataContract.GetClrTypeFullName(type)));
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot)
        {
            xsdType = null;
            hasRoot = true;
            object[] attrs = clrType.GetCustomAttributes(Globals.TypeOfXmlSchemaProviderAttribute, false);
            if (attrs == null || attrs.Length == 0)
            {
                xmlName = DataContract.GetDefaultXmlName(clrType);
                return false;
            }
 
            XmlSchemaProviderAttribute provider = (XmlSchemaProviderAttribute)attrs[0];
            if (provider.IsAny)
            {
                xsdType = CreateAnyElementType();
                hasRoot = false;
            }
            string? methodName = provider.MethodName;
            if (string.IsNullOrEmpty(methodName))
            {
                if (!provider.IsAny)
                    throw new InvalidDataContractException(SR.Format(SR.InvalidGetSchemaMethod, DataContract.GetClrTypeFullName(clrType)));
                xmlName = DataContract.GetDefaultXmlName(clrType);
            }
            else
            {
                MethodInfo? getMethod = clrType.GetMethod(methodName,  /*BindingFlags.DeclaredOnly |*/ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, new Type[] { typeof(XmlSchemaSet) });
                if (getMethod == null)
                    throw new InvalidDataContractException(SR.Format(SR.MissingGetSchemaMethod, DataContract.GetClrTypeFullName(clrType), methodName));
 
                if (!(Globals.TypeOfXmlQualifiedName.IsAssignableFrom(getMethod.ReturnType)) && !(Globals.TypeOfXmlSchemaType.IsAssignableFrom(getMethod.ReturnType)))
                    throw new InvalidDataContractException(SR.Format(SR.InvalidReturnTypeOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType), methodName, DataContract.GetClrTypeFullName(getMethod.ReturnType), DataContract.GetClrTypeFullName(Globals.TypeOfXmlQualifiedName), typeof(XmlSchemaType)));
 
                object? typeInfo = getMethod.Invoke(null, new object[] { schemas });
 
                if (provider.IsAny)
                {
                    if (typeInfo != null)
                        throw new InvalidDataContractException(SR.Format(SR.InvalidNonNullReturnValueByIsAny, DataContract.GetClrTypeFullName(clrType), methodName));
                    xmlName = DataContract.GetDefaultXmlName(clrType);
                }
                else if (typeInfo == null)
                {
                    xsdType = CreateAnyElementType();
                    hasRoot = false;
                    xmlName = DataContract.GetDefaultXmlName(clrType);
                }
                else
                {
                    if (typeInfo is XmlSchemaType providerXsdType)
                    {
                        string? typeName = providerXsdType.Name;
                        string? typeNs = null;
                        if (string.IsNullOrEmpty(typeName))
                        {
                            DataContract.GetDefaultXmlName(DataContract.GetClrTypeFullName(clrType), out typeName, out typeNs);
                            xmlName = new XmlQualifiedName(typeName, typeNs);
                            providerXsdType.Annotation = GetSchemaAnnotation(ExportActualType(xmlName, new XmlDocument()));
                            xsdType = providerXsdType;
                        }
                        else
                        {
                            foreach (XmlSchema schema in schemas.Schemas())
                            {
                                foreach (XmlSchemaObject schemaItem in schema.Items)
                                {
                                    if ((object)schemaItem == (object)providerXsdType)
                                    {
                                        typeNs = schema.TargetNamespace ?? string.Empty;
                                        break;
                                    }
                                }
                                if (typeNs != null)
                                    break;
                            }
                            if (typeNs == null)
                                throw new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeName, DataContract.GetClrTypeFullName(clrType)));
                            xmlName = new XmlQualifiedName(typeName, typeNs);
                        }
                    }
                    else
                        xmlName = (XmlQualifiedName)typeInfo;
                }
            }
            return true;
        }
 
        private static void InvokeGetSchemaMethod(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
            Type clrType,
            XmlSchemaSet schemas, XmlQualifiedName xmlName)
        {
            IXmlSerializable ixmlSerializable = (IXmlSerializable)Activator.CreateInstance(clrType)!;
            XmlSchema? schema = ixmlSerializable.GetSchema();
            if (schema == null)
            {
                AddDefaultDatasetType(schemas, xmlName.Name, xmlName.Namespace);
            }
            else
            {
                if (string.IsNullOrEmpty(schema.Id))
                    throw new InvalidDataContractException(SR.Format(SR.InvalidReturnSchemaOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType)));
                AddDefaultTypedDatasetType(schemas, schema, xmlName.Name, xmlName.Namespace);
            }
        }
 
        internal static void AddDefaultXmlType(XmlSchemaSet schemas, string localName, string ns)
        {
            XmlSchemaComplexType defaultXmlType = CreateAnyType();
            defaultXmlType.Name = localName;
            XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
            schema.Items.Add(defaultXmlType);
            schemas.Reprocess(schema);
        }
 
        private static XmlSchemaComplexType CreateAnyType()
        {
            XmlSchemaComplexType anyType = new XmlSchemaComplexType();
            anyType.IsMixed = true;
            anyType.Particle = new XmlSchemaSequence();
            XmlSchemaAny any = new XmlSchemaAny();
            any.MinOccurs = 0;
            any.MaxOccurs = decimal.MaxValue;
            any.ProcessContents = XmlSchemaContentProcessing.Lax;
            ((XmlSchemaSequence)anyType.Particle).Items.Add(any);
            anyType.AnyAttribute = new XmlSchemaAnyAttribute();
            return anyType;
        }
 
        private static XmlSchemaComplexType CreateAnyElementType()
        {
            XmlSchemaComplexType anyElementType = new XmlSchemaComplexType();
            anyElementType.IsMixed = false;
            anyElementType.Particle = new XmlSchemaSequence();
            XmlSchemaAny any = new XmlSchemaAny();
            any.MinOccurs = 0;
            any.ProcessContents = XmlSchemaContentProcessing.Lax;
            ((XmlSchemaSequence)anyElementType.Particle).Items.Add(any);
            return anyElementType;
        }
 
        internal static bool IsSpecialXmlType(Type type, [NotNullWhen(true)] out XmlQualifiedName? typeName, [NotNullWhen(true)] out XmlSchemaType? xsdType, out bool hasRoot)
        {
            xsdType = null;
            hasRoot = true;
            if (type == Globals.TypeOfXmlElement || type == Globals.TypeOfXmlNodeArray)
            {
                string? name;
                if (type == Globals.TypeOfXmlElement)
                {
                    xsdType = CreateAnyElementType();
                    name = "XmlElement";
                    hasRoot = false;
                }
                else
                {
                    xsdType = CreateAnyType();
                    name = "ArrayOfXmlNode";
                    hasRoot = true;
                }
                typeName = new XmlQualifiedName(name, DataContract.GetDefaultXmlNamespace(type));
                return true;
            }
            typeName = null;
            return false;
        }
 
        private static void AddDefaultDatasetType(XmlSchemaSet schemas, string localName, string ns)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = localName;
            type.Particle = new XmlSchemaSequence();
            XmlSchemaElement schemaRefElement = new XmlSchemaElement();
            schemaRefElement.RefName = new XmlQualifiedName(Globals.SchemaLocalName, XmlSchema.Namespace);
            ((XmlSchemaSequence)type.Particle).Items.Add(schemaRefElement);
            XmlSchemaAny any = new XmlSchemaAny();
            ((XmlSchemaSequence)type.Particle).Items.Add(any);
            XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
            schema.Items.Add(type);
            schemas.Reprocess(schema);
        }
 
        private static void AddDefaultTypedDatasetType(XmlSchemaSet schemas, XmlSchema datasetSchema, string localName, string ns)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = localName;
            type.Particle = new XmlSchemaSequence();
            XmlSchemaAny any = new XmlSchemaAny();
            any.Namespace = datasetSchema.TargetNamespace ?? string.Empty;
            ((XmlSchemaSequence)type.Particle).Items.Add(any);
            schemas.Add(datasetSchema);
            XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
            schema.Items.Add(type);
            schemas.Reprocess(datasetSchema);
            schemas.Reprocess(schema);
        }
 
        private XmlSchemaAnnotation GetSchemaAnnotation(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
        {
            XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
            XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
            XmlElement annotationElement = GetAnnotationMarkup(annotationQualifiedName, innerText, schema);
            appInfo.Markup = new XmlNode[1] { annotationElement };
            annotation.Items.Add(appInfo);
            return annotation;
        }
 
        private static XmlSchemaAnnotation? GetSchemaAnnotation(params XmlNode?[]? nodes)
        {
            if (nodes == null || nodes.Length == 0)
                return null;
            bool hasAnnotation = false;
            for (int i = 0; i < nodes.Length; i++)
                if (nodes[i] != null)
                {
                    hasAnnotation = true;
                    break;
                }
            if (!hasAnnotation)
                return null;
 
            XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
            XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
            annotation.Items.Add(appInfo);
            appInfo.Markup = nodes;
            return annotation;
        }
 
        private XmlElement GetAnnotationMarkup(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
        {
            XmlElement annotationElement = XmlDoc.CreateElement(annotationQualifiedName.Name, annotationQualifiedName.Namespace);
            SchemaHelper.AddSchemaImport(annotationQualifiedName.Namespace, schema);
            annotationElement.InnerText = innerText;
            return annotationElement;
        }
 
        private XmlSchema GetSchema(string ns)
        {
            return SchemaHelper.GetSchema(ns, Schemas);
        }
 
        // Property is not stored in a local because XmlSchemaSequence is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaSequence ISerializableSequence
        {
            get
            {
                XmlSchemaSequence iSerializableSequence = new XmlSchemaSequence();
                iSerializableSequence.Items.Add(ISerializableWildcardElement);
                return iSerializableSequence;
            }
        }
 
        // Property is not stored in a local because XmlSchemaAny is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAny ISerializableWildcardElement
        {
            get
            {
                XmlSchemaAny iSerializableWildcardElement = new XmlSchemaAny();
                iSerializableWildcardElement.MinOccurs = 0;
                iSerializableWildcardElement.MaxOccursString = Globals.OccursUnbounded;
                iSerializableWildcardElement.Namespace = "##local";
                iSerializableWildcardElement.ProcessContents = XmlSchemaContentProcessing.Skip;
                return iSerializableWildcardElement;
            }
        }
 
        private static XmlQualifiedName? s_anytypeQualifiedName;
        internal static XmlQualifiedName AnytypeQualifiedName => s_anytypeQualifiedName ??= new XmlQualifiedName(Globals.AnyTypeLocalName, Globals.SchemaNamespace);
 
        private static XmlQualifiedName? s_stringQualifiedName;
        internal static XmlQualifiedName StringQualifiedName => s_stringQualifiedName ??= new XmlQualifiedName(Globals.StringLocalName, Globals.SchemaNamespace);
 
        private static XmlQualifiedName? s_defaultEnumBaseTypeName;
        internal static XmlQualifiedName DefaultEnumBaseTypeName => s_defaultEnumBaseTypeName ??= new XmlQualifiedName(Globals.IntLocalName, Globals.SchemaNamespace);
 
        private static XmlQualifiedName? s_enumerationValueAnnotationName;
        internal static XmlQualifiedName EnumerationValueAnnotationName => s_enumerationValueAnnotationName ??= new XmlQualifiedName(Globals.EnumerationValueLocalName, Globals.SerializationNamespace);
 
        private static XmlQualifiedName? s_surrogateDataAnnotationName;
        internal static XmlQualifiedName SurrogateDataAnnotationName => s_surrogateDataAnnotationName ??= new XmlQualifiedName(Globals.SurrogateDataLocalName, Globals.SerializationNamespace);
 
        private static XmlQualifiedName? s_defaultValueAnnotation;
        internal static XmlQualifiedName DefaultValueAnnotation => s_defaultValueAnnotation ??= new XmlQualifiedName(Globals.DefaultValueLocalName, Globals.SerializationNamespace);
 
        private static XmlQualifiedName? s_actualTypeAnnotationName;
        internal static XmlQualifiedName ActualTypeAnnotationName => s_actualTypeAnnotationName ??= new XmlQualifiedName(Globals.ActualTypeLocalName, Globals.SerializationNamespace);
 
        private static XmlQualifiedName? s_isDictionaryAnnotationName;
        internal static XmlQualifiedName IsDictionaryAnnotationName => s_isDictionaryAnnotationName ??= new XmlQualifiedName(Globals.IsDictionaryLocalName, Globals.SerializationNamespace);
 
        private static XmlQualifiedName? s_isValueTypeName;
        internal static XmlQualifiedName IsValueTypeName => s_isValueTypeName ??= new XmlQualifiedName(Globals.IsValueTypeLocalName, Globals.SerializationNamespace);
 
        // Property is not stored in a local because XmlSchemaAttribute is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAttribute ISerializableFactoryTypeAttribute
        {
            get
            {
                XmlSchemaAttribute iSerializableFactoryTypeAttribute = new XmlSchemaAttribute();
                iSerializableFactoryTypeAttribute.RefName = new XmlQualifiedName(Globals.ISerializableFactoryTypeLocalName, Globals.SerializationNamespace);
                return iSerializableFactoryTypeAttribute;
            }
        }
 
        // Property is not stored in a local because XmlSchemaAttribute is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAttribute RefAttribute
        {
            get
            {
                XmlSchemaAttribute refAttribute = new XmlSchemaAttribute();
                refAttribute.RefName = Globals.RefQualifiedName;
                return refAttribute;
            }
        }
 
        // Property is not stored in a local because XmlSchemaAttribute is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAttribute IdAttribute
        {
            get
            {
                XmlSchemaAttribute idAttribute = new XmlSchemaAttribute();
                idAttribute.RefName = Globals.IdQualifiedName;
                return idAttribute;
            }
        }
    }
}