3a93e66279d2630910f512b60be1bf458e38b537
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 using System;
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Collections.ObjectModel;
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Globalization;
11 using System.IO;
12 using System.Reflection;
13 using System.Runtime.Serialization.DataContracts;
14 using System.Xml;
15 using System.Xml.Schema;
16 using System.Xml.Serialization;
17
18 namespace System.Runtime.Serialization
19 {
20     internal sealed class SchemaExporter
21     {
22         private readonly XmlSchemaSet _schemas;
23         private XmlDocument? _xmlDoc;
24         private DataContractSet _dataContractSet;
25
26         internal SchemaExporter(XmlSchemaSet schemas, DataContractSet dataContractSet)
27         {
28             _schemas = schemas;
29             _dataContractSet = dataContractSet;
30         }
31
32         private XmlSchemaSet Schemas
33         {
34             get { return _schemas; }
35         }
36
37         private XmlDocument XmlDoc => _xmlDoc ??= new XmlDocument();
38
39         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
40         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
41         internal void Export()
42         {
43             try
44             {
45                 // Remove this if we decide to publish serialization schema at well-known location
46                 ExportSerializationSchema();
47                 foreach (KeyValuePair<XmlQualifiedName, DataContract> pair in _dataContractSet.Contracts)
48                 {
49                     DataContract dataContract = pair.Value;
50                     if (!_dataContractSet.IsContractProcessed(dataContract))
51                     {
52                         ExportDataContract(dataContract);
53                         _dataContractSet.SetContractProcessed(dataContract);
54                     }
55                 }
56             }
57             finally
58             {
59                 _xmlDoc = null;
60                 _dataContractSet = null!;
61             }
62         }
63
64         private void ExportSerializationSchema()
65         {
66             if (!Schemas.Contains(Globals.SerializationNamespace))
67             {
68                 StringReader reader = new StringReader(Globals.SerializationSchema);
69                 XmlSchema? schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null);
70                 if (schema == null)
71                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace)));
72                 Schemas.Add(schema);
73             }
74         }
75
76         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
77         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
78         private void ExportDataContract(DataContract dataContract)
79         {
80             if (dataContract.IsBuiltInDataContract)
81                 return;
82             else if (dataContract is XmlDataContract)
83                 ExportXmlDataContract((XmlDataContract)dataContract);
84             else
85             {
86                 XmlSchema schema = GetSchema(dataContract.XmlName.Namespace);
87
88                 if (dataContract is ClassDataContract classDataContract)
89                 {
90                     if (classDataContract.IsISerializable)
91                         ExportISerializableDataContract(classDataContract, schema);
92                     else
93                         ExportClassDataContract(classDataContract, schema);
94                 }
95                 else if (dataContract is CollectionDataContract)
96                     ExportCollectionDataContract((CollectionDataContract)dataContract, schema);
97                 else if (dataContract is EnumDataContract)
98                     ExportEnumDataContract((EnumDataContract)dataContract, schema);
99                 ExportTopLevelElement(dataContract, schema);
100                 Schemas.Reprocess(schema);
101             }
102         }
103
104         private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSchema? schema)
105         {
106             if (schema == null || dataContract.XmlName.Namespace != dataContract.TopLevelElementNamespace!.Value)
107                 schema = GetSchema(dataContract.TopLevelElementNamespace!.Value);
108
109             XmlSchemaElement topLevelElement = new XmlSchemaElement();
110             topLevelElement.Name = dataContract.TopLevelElementName!.Value;
111             SetElementType(topLevelElement, dataContract, schema);
112             topLevelElement.IsNillable = true;
113             schema.Items.Add(topLevelElement);
114             return topLevelElement;
115         }
116
117         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
118         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
119         private void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema)
120         {
121             XmlSchemaComplexType type = new XmlSchemaComplexType();
122             type.Name = classDataContract.XmlName.Name;
123             schema.Items.Add(type);
124             XmlElement? genericInfoElement = null;
125             if (classDataContract.UnderlyingType.IsGenericType)
126                 genericInfoElement = ExportGenericInfo(classDataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
127
128             XmlSchemaSequence rootSequence = new XmlSchemaSequence();
129             for (int i = 0; i < classDataContract.Members!.Count; i++)
130             {
131                 DataMember dataMember = classDataContract.Members[i];
132
133                 XmlSchemaElement element = new XmlSchemaElement();
134                 element.Name = dataMember.Name;
135                 XmlElement? actualTypeElement = null;
136                 DataContract memberTypeContract = _dataContractSet.GetMemberTypeDataContract(dataMember);
137                 if (CheckIfMemberHasConflict(dataMember))
138                 {
139                     element.SchemaTypeName = AnytypeQualifiedName;
140                     actualTypeElement = ExportActualType(memberTypeContract.XmlName);
141                     SchemaHelper.AddSchemaImport(memberTypeContract.XmlName.Namespace, schema);
142                 }
143                 else
144                     SetElementType(element, memberTypeContract, schema);
145                 SchemaHelper.AddElementForm(element, schema);
146                 if (dataMember.IsNullable)
147                     element.IsNillable = true;
148                 if (!dataMember.IsRequired)
149                     element.MinOccurs = 0;
150
151                 element.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(dataMember), ExportEmitDefaultValue(dataMember));
152                 rootSequence.Items.Add(element);
153             }
154
155             XmlElement? isValueTypeElement = null;
156             if (classDataContract.BaseClassContract != null)
157             {
158                 XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseClassContract.XmlName, schema);
159                 extension.Particle = rootSequence;
160                 if (classDataContract.IsReference && !classDataContract.BaseClassContract.IsReference)
161                 {
162                     AddReferenceAttributes(extension.Attributes, schema);
163                 }
164             }
165             else
166             {
167                 type.Particle = rootSequence;
168                 if (classDataContract.IsValueType)
169                     isValueTypeElement = GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(classDataContract.IsValueType), schema);
170                 if (classDataContract.IsReference)
171                     AddReferenceAttributes(type.Attributes, schema);
172             }
173             type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(classDataContract), isValueTypeElement);
174         }
175
176         private static void AddReferenceAttributes(XmlSchemaObjectCollection attributes, XmlSchema schema)
177         {
178             SchemaHelper.AddSchemaImport(Globals.SerializationNamespace, schema);
179             schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
180             attributes.Add(IdAttribute);
181             attributes.Add(RefAttribute);
182         }
183
184         private static void SetElementType(XmlSchemaElement element, DataContract dataContract, XmlSchema schema)
185         {
186             if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous)
187             {
188                 element.SchemaType = xmlDataContract.XsdType;
189             }
190             else
191             {
192                 element.SchemaTypeName = dataContract.XmlName;
193
194                 if (element.SchemaTypeName.Namespace.Equals(Globals.SerializationNamespace))
195                     schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
196
197                 SchemaHelper.AddSchemaImport(dataContract.XmlName.Namespace, schema);
198             }
199         }
200
201         private static bool CheckIfMemberHasConflict(DataMember dataMember)
202         {
203             if (dataMember.HasConflictingNameAndType)
204                 return true;
205
206             DataMember? conflictingMember = dataMember.ConflictingMember;
207             while (conflictingMember != null)
208             {
209                 if (conflictingMember.HasConflictingNameAndType)
210                     return true;
211                 conflictingMember = conflictingMember.ConflictingMember;
212             }
213
214             return false;
215         }
216
217         private XmlElement? ExportEmitDefaultValue(DataMember dataMember)
218         {
219             if (dataMember.EmitDefaultValue)
220                 return null;
221             XmlElement defaultValueElement = XmlDoc.CreateElement(DefaultValueAnnotation.Name, DefaultValueAnnotation.Namespace);
222             XmlAttribute emitDefaultValueAttribute = XmlDoc.CreateAttribute(Globals.EmitDefaultValueAttribute);
223             emitDefaultValueAttribute.Value = Globals.False;
224             defaultValueElement.Attributes.Append(emitDefaultValueAttribute);
225             return defaultValueElement;
226         }
227
228         private XmlElement ExportActualType(XmlQualifiedName typeName)
229         {
230             return ExportActualType(typeName, XmlDoc);
231         }
232
233         private static XmlElement ExportActualType(XmlQualifiedName typeName, XmlDocument xmlDoc)
234         {
235             XmlElement actualTypeElement = xmlDoc.CreateElement(ActualTypeAnnotationName.Name, ActualTypeAnnotationName.Namespace);
236
237             XmlAttribute nameAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNameAttribute);
238             nameAttribute.Value = typeName.Name;
239             actualTypeElement.Attributes.Append(nameAttribute);
240
241             XmlAttribute nsAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNamespaceAttribute);
242             nsAttribute.Value = typeName.Namespace;
243             actualTypeElement.Attributes.Append(nsAttribute);
244
245             return actualTypeElement;
246         }
247
248         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
249         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
250         private XmlElement ExportGenericInfo(Type clrType, string elementName, string elementNs)
251         {
252             Type? itemType;
253             int nestedCollectionLevel = 0;
254             while (CollectionDataContract.IsCollection(clrType, out itemType))
255             {
256                 if (DataContract.GetBuiltInDataContract(clrType) != null
257                     || CollectionDataContract.IsCollectionDataContract(clrType))
258                 {
259                     break;
260                 }
261                 clrType = itemType;
262                 nestedCollectionLevel++;
263             }
264
265             Type[]? genericArguments = null;
266             IList<int>? genericArgumentCounts = null;
267             if (clrType.IsGenericType)
268             {
269                 genericArguments = clrType.GetGenericArguments();
270                 string typeName;
271                 if (clrType.DeclaringType == null)
272                     typeName = clrType.Name;
273                 else
274                 {
275                     int nsLen = (clrType.Namespace == null) ? 0 : clrType.Namespace.Length;
276                     if (nsLen > 0)
277                         nsLen++; //include the . following namespace
278                     typeName = DataContract.GetClrTypeFullName(clrType).Substring(nsLen).Replace('+', '.');
279                 }
280                 int iParam = typeName.IndexOf('[');
281                 if (iParam >= 0)
282                     typeName = typeName.Substring(0, iParam);
283                 genericArgumentCounts = DataContract.GetDataContractNameForGenericName(typeName, null);
284                 clrType = clrType.GetGenericTypeDefinition();
285             }
286             XmlQualifiedName dcqname = DataContract.GetXmlName(clrType);
287             if (nestedCollectionLevel > 0)
288             {
289                 string collectionName = dcqname.Name;
290                 for (int n = 0; n < nestedCollectionLevel; n++)
291                     collectionName = Globals.ArrayPrefix + collectionName;
292                 dcqname = new XmlQualifiedName(collectionName, DataContract.GetCollectionNamespace(dcqname.Namespace));
293             }
294             XmlElement typeElement = XmlDoc.CreateElement(elementName, elementNs);
295
296             XmlAttribute nameAttribute = XmlDoc.CreateAttribute(Globals.GenericNameAttribute);
297             nameAttribute.Value = genericArguments != null ? XmlConvert.DecodeName(dcqname.Name) : dcqname.Name;
298             //nameAttribute.Value = dcqname.Name;
299             typeElement.Attributes.Append(nameAttribute);
300
301             XmlAttribute nsAttribute = XmlDoc.CreateAttribute(Globals.GenericNamespaceAttribute);
302             nsAttribute.Value = dcqname.Namespace;
303             typeElement.Attributes.Append(nsAttribute);
304
305             if (genericArguments != null)
306             {
307                 int argIndex = 0;
308                 int nestedLevel = 0;
309                 Debug.Assert(genericArgumentCounts != null);
310                 foreach (int genericArgumentCount in genericArgumentCounts)
311                 {
312                     for (int i = 0; i < genericArgumentCount; i++, argIndex++)
313                     {
314                         XmlElement argumentElement = ExportGenericInfo(genericArguments[argIndex], Globals.GenericParameterLocalName, Globals.SerializationNamespace);
315                         if (nestedLevel > 0)
316                         {
317                             XmlAttribute nestedLevelAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
318                             nestedLevelAttribute.Value = nestedLevel.ToString(CultureInfo.InvariantCulture);
319                             argumentElement.Attributes.Append(nestedLevelAttribute);
320                         }
321                         typeElement.AppendChild(argumentElement);
322                     }
323                     nestedLevel++;
324                 }
325                 if (genericArgumentCounts[nestedLevel - 1] == 0)
326                 {
327                     XmlAttribute typeNestedLevelsAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
328                     typeNestedLevelsAttribute.Value = genericArgumentCounts.Count.ToString(CultureInfo.InvariantCulture);
329                     typeElement.Attributes.Append(typeNestedLevelsAttribute);
330                 }
331             }
332
333             return typeElement;
334         }
335
336         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
337         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
338         private XmlElement? ExportSurrogateData(object key)
339         {
340             object? surrogateData = _dataContractSet.GetSurrogateData(key);
341             if (surrogateData == null)
342                 return null;
343             StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
344             XmlWriterSettings writerSettings = new XmlWriterSettings();
345             writerSettings.OmitXmlDeclaration = true;
346             XmlWriter xmlWriter = XmlWriter.Create(stringWriter, writerSettings);
347             Collection<Type> knownTypes = new Collection<Type>();
348             if (_dataContractSet.SerializationExtendedSurrogateProvider != null)
349                 DataContractSurrogateCaller.GetKnownCustomDataTypes(_dataContractSet.SerializationExtendedSurrogateProvider, knownTypes);
350             DataContractSerializer serializer = new DataContractSerializer(Globals.TypeOfObject,
351                 SurrogateDataAnnotationName.Name, SurrogateDataAnnotationName.Namespace, knownTypes,
352                 ignoreExtensionDataObject: false, preserveObjectReferences: true);
353             serializer.WriteObject(xmlWriter, surrogateData);
354             xmlWriter.Flush();
355             return (XmlElement?)XmlDoc.ReadNode(XmlReader.Create(new StringReader(stringWriter.ToString())));
356         }
357
358         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
359         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
360         private void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema)
361         {
362             XmlSchemaComplexType type = new XmlSchemaComplexType();
363             type.Name = collectionDataContract.XmlName.Name;
364             schema.Items.Add(type);
365             XmlElement? genericInfoElement = null, isDictionaryElement = null;
366             if (collectionDataContract.UnderlyingType.IsGenericType && CollectionDataContract.IsCollectionDataContract(collectionDataContract.UnderlyingType))
367                 genericInfoElement = ExportGenericInfo(collectionDataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
368             if (collectionDataContract.IsDictionary)
369                 isDictionaryElement = ExportIsDictionary();
370             type.Annotation = GetSchemaAnnotation(isDictionaryElement, genericInfoElement, ExportSurrogateData(collectionDataContract));
371
372             XmlSchemaSequence rootSequence = new XmlSchemaSequence();
373
374             XmlSchemaElement element = new XmlSchemaElement();
375             element.Name = collectionDataContract.ItemName;
376             element.MinOccurs = 0;
377             element.MaxOccursString = Globals.OccursUnbounded;
378             if (collectionDataContract.IsDictionary)
379             {
380                 ClassDataContract keyValueContract = (collectionDataContract.ItemContract as ClassDataContract)!;
381                 XmlSchemaComplexType keyValueType = new XmlSchemaComplexType();
382                 XmlSchemaSequence keyValueSequence = new XmlSchemaSequence();
383                 foreach (DataMember dataMember in keyValueContract.Members!)
384                 {
385                     XmlSchemaElement keyValueElement = new XmlSchemaElement();
386                     keyValueElement.Name = dataMember.Name;
387                     SetElementType(keyValueElement, _dataContractSet.GetMemberTypeDataContract(dataMember), schema);
388                     SchemaHelper.AddElementForm(keyValueElement, schema);
389                     if (dataMember.IsNullable)
390                         keyValueElement.IsNillable = true;
391                     keyValueElement.Annotation = GetSchemaAnnotation(ExportSurrogateData(dataMember));
392                     keyValueSequence.Items.Add(keyValueElement);
393                 }
394                 keyValueType.Particle = keyValueSequence;
395                 element.SchemaType = keyValueType;
396             }
397             else
398             {
399                 if (collectionDataContract.IsItemTypeNullable)
400                     element.IsNillable = true;
401                 DataContract itemContract = _dataContractSet.GetItemTypeDataContract(collectionDataContract);
402                 SetElementType(element, itemContract, schema);
403             }
404             SchemaHelper.AddElementForm(element, schema);
405             rootSequence.Items.Add(element);
406
407             type.Particle = rootSequence;
408
409             if (collectionDataContract.IsReference)
410                 AddReferenceAttributes(type.Attributes, schema);
411         }
412
413         private XmlElement ExportIsDictionary()
414         {
415             XmlElement isDictionaryElement = XmlDoc.CreateElement(IsDictionaryAnnotationName.Name, IsDictionaryAnnotationName.Namespace);
416             isDictionaryElement.InnerText = Globals.True;
417             return isDictionaryElement;
418         }
419
420         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
421         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
422         private void ExportEnumDataContract(EnumDataContract enumDataContract, XmlSchema schema)
423         {
424             XmlSchemaSimpleType type = new XmlSchemaSimpleType();
425             type.Name = enumDataContract.XmlName.Name;
426             XmlElement? actualTypeElement = (enumDataContract.BaseContractName == DefaultEnumBaseTypeName) ? null : ExportActualType(enumDataContract.BaseContractName);
427             type.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(enumDataContract));
428             schema.Items.Add(type);
429
430             XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
431             restriction.BaseTypeName = StringQualifiedName;
432             SchemaHelper.AddSchemaImport(enumDataContract.BaseContractName.Namespace, schema);
433             if (enumDataContract.Values != null)
434             {
435                 for (int i = 0; i < enumDataContract.Values.Count; i++)
436                 {
437                     XmlSchemaEnumerationFacet facet = new XmlSchemaEnumerationFacet();
438                     facet.Value = enumDataContract.Members[i].Name;
439                     if (enumDataContract.Values[i] != GetDefaultEnumValue(enumDataContract.IsFlags, i))
440                         facet.Annotation = GetSchemaAnnotation(EnumerationValueAnnotationName, enumDataContract.GetStringFromEnumValue(enumDataContract.Values[i]), schema);
441                     restriction.Facets.Add(facet);
442                 }
443             }
444             if (enumDataContract.IsFlags)
445             {
446                 XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList();
447                 XmlSchemaSimpleType anonymousType = new XmlSchemaSimpleType();
448                 anonymousType.Content = restriction;
449                 list.ItemType = anonymousType;
450                 type.Content = list;
451             }
452             else
453                 type.Content = restriction;
454         }
455
456         internal static long GetDefaultEnumValue(bool isFlags, int index)
457         {
458             return isFlags ? (long)Math.Pow(2, index) : index;
459         }
460
461         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
462         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
463         private void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema)
464         {
465             XmlSchemaComplexType type = new XmlSchemaComplexType();
466             type.Name = dataContract.XmlName.Name;
467             schema.Items.Add(type);
468             XmlElement? genericInfoElement = null;
469             if (dataContract.UnderlyingType.IsGenericType)
470                 genericInfoElement = ExportGenericInfo(dataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
471
472             XmlElement? isValueTypeElement = null;
473             if (dataContract.BaseClassContract != null)
474             {
475                 _ = CreateTypeContent(type, dataContract.BaseClassContract.XmlName, schema);
476             }
477             else
478             {
479                 schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
480                 type.Particle = ISerializableSequence;
481                 XmlSchemaAttribute iSerializableFactoryTypeAttribute = ISerializableFactoryTypeAttribute;
482                 type.Attributes.Add(iSerializableFactoryTypeAttribute);
483                 SchemaHelper.AddSchemaImport(ISerializableFactoryTypeAttribute.RefName.Namespace, schema);
484                 if (dataContract.IsValueType)
485                     isValueTypeElement = GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(dataContract.IsValueType), schema);
486             }
487             type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(dataContract), isValueTypeElement);
488         }
489
490         private static XmlSchemaComplexContentExtension CreateTypeContent(XmlSchemaComplexType type, XmlQualifiedName baseTypeName, XmlSchema schema)
491         {
492             SchemaHelper.AddSchemaImport(baseTypeName.Namespace, schema);
493
494             XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
495             extension.BaseTypeName = baseTypeName;
496             type.ContentModel = new XmlSchemaComplexContent();
497             type.ContentModel.Content = extension;
498
499             return extension;
500         }
501
502         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
503         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
504         private void ExportXmlDataContract(XmlDataContract dataContract)
505         {
506             XmlQualifiedName? typeQName;
507             bool hasRoot;
508             XmlSchemaType? xsdType;
509
510             Type clrType = dataContract.UnderlyingType;
511             if (!IsSpecialXmlType(clrType, out typeQName, out xsdType, out hasRoot))
512                 if (!InvokeSchemaProviderMethod(clrType, _schemas, out typeQName, out xsdType, out hasRoot))
513                     InvokeGetSchemaMethod(clrType, _schemas, typeQName);
514
515             if (hasRoot)
516             {
517                 if (!(typeQName.Equals(dataContract.XmlName)))
518                 {
519                     Fx.Assert("XML data contract type name does not match schema name");
520                 }
521
522                 XmlSchema? schema;
523                 if (SchemaHelper.GetSchemaElement(Schemas,
524                     new XmlQualifiedName(dataContract.TopLevelElementName!.Value, dataContract.TopLevelElementNamespace!.Value),
525                     out schema) == null)
526                 {
527                     XmlSchemaElement topLevelElement = ExportTopLevelElement(dataContract, schema);
528                     topLevelElement.IsNillable = dataContract.IsTopLevelElementNullable;
529                     ReprocessAll(_schemas);
530                 }
531
532                 XmlSchemaType? anonymousType = xsdType;
533                 xsdType = SchemaHelper.GetSchemaType(_schemas, typeQName, out schema);
534                 if (anonymousType == null && xsdType == null && typeQName.Namespace != XmlSchema.Namespace)
535                 {
536                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeQName, DataContract.GetClrTypeFullName(clrType))));
537                 }
538                 if (xsdType != null)
539                 {
540                     xsdType.Annotation = GetSchemaAnnotation(
541                                            ExportSurrogateData(dataContract),
542                                            dataContract.IsValueType ?
543                                              GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(dataContract.IsValueType), schema!) :
544                                              null
545                                          );
546                 }
547             }
548         }
549
550         private static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate items
551         {
552             Hashtable elements = new Hashtable();
553             Hashtable types = new Hashtable();
554             XmlSchema[] schemaArray = new XmlSchema[schemas.Count];
555             schemas.CopyTo(schemaArray, 0);
556             for (int i = 0; i < schemaArray.Length; i++)
557             {
558                 XmlSchema schema = schemaArray[i];
559                 XmlSchemaObject[] itemArray = new XmlSchemaObject[schema.Items.Count];
560                 schema.Items.CopyTo(itemArray, 0);
561                 for (int j = 0; j < itemArray.Length; j++)
562                 {
563                     XmlSchemaObject item = itemArray[j];
564                     Hashtable items;
565                     XmlQualifiedName qname;
566                     if (item is XmlSchemaElement)
567                     {
568                         items = elements;
569                         qname = new XmlQualifiedName(((XmlSchemaElement)item).Name, schema.TargetNamespace);
570                     }
571                     else if (item is XmlSchemaType)
572                     {
573                         items = types;
574                         qname = new XmlQualifiedName(((XmlSchemaType)item).Name, schema.TargetNamespace);
575                     }
576                     else
577                         continue;
578                     object? otherItem = items[qname];
579                     if (otherItem != null)
580                     {
581                         schema.Items.Remove(item);
582                     }
583                     else
584                         items.Add(qname, item);
585                 }
586                 schemas.Reprocess(schema);
587             }
588         }
589
590         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
591         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
592         internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot)
593         {
594             if (IsSpecialXmlType(type, out xmlName!, out xsdType, out hasRoot))
595                 return;
596             XmlSchemaSet schemas = new XmlSchemaSet();
597             schemas.XmlResolver = null;
598             InvokeSchemaProviderMethod(type, schemas, out xmlName, out xsdType, out hasRoot);
599             if (xmlName.Name == null || xmlName.Name.Length == 0)
600                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidXmlDataContractName, DataContract.GetClrTypeFullName(type))));
601         }
602
603         [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
604         [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
605         private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot)
606         {
607             xsdType = null;
608             hasRoot = true;
609             object[] attrs = clrType.GetCustomAttributes(Globals.TypeOfXmlSchemaProviderAttribute, false);
610             if (attrs == null || attrs.Length == 0)
611             {
612                 xmlName = DataContract.GetDefaultXmlName(clrType);
613                 return false;
614             }
615
616             XmlSchemaProviderAttribute provider = (XmlSchemaProviderAttribute)attrs[0];
617             if (provider.IsAny)
618             {
619                 xsdType = CreateAnyElementType();
620                 hasRoot = false;
621             }
622             string? methodName = provider.MethodName;
623             if (methodName == null || methodName.Length == 0)
624             {
625                 if (!provider.IsAny)
626                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidGetSchemaMethod, DataContract.GetClrTypeFullName(clrType))));
627                 xmlName = DataContract.GetDefaultXmlName(clrType);
628             }
629             else
630             {
631                 MethodInfo? getMethod = clrType.GetMethod(methodName,  /*BindingFlags.DeclaredOnly |*/ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, new Type[] { typeof(XmlSchemaSet) });
632                 if (getMethod == null)
633                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.MissingGetSchemaMethod, DataContract.GetClrTypeFullName(clrType), methodName)));
634
635                 if (!(Globals.TypeOfXmlQualifiedName.IsAssignableFrom(getMethod.ReturnType)) && !(Globals.TypeOfXmlSchemaType.IsAssignableFrom(getMethod.ReturnType)))
636                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidReturnTypeOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType), methodName, DataContract.GetClrTypeFullName(getMethod.ReturnType), DataContract.GetClrTypeFullName(Globals.TypeOfXmlQualifiedName), typeof(XmlSchemaType))));
637
638                 object? typeInfo = getMethod.Invoke(null, new object[] { schemas });
639
640                 if (provider.IsAny)
641                 {
642                     if (typeInfo != null)
643                         throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidNonNullReturnValueByIsAny, DataContract.GetClrTypeFullName(clrType), methodName)));
644                     xmlName = DataContract.GetDefaultXmlName(clrType);
645                 }
646                 else if (typeInfo == null)
647                 {
648                     xsdType = CreateAnyElementType();
649                     hasRoot = false;
650                     xmlName = DataContract.GetDefaultXmlName(clrType);
651                 }
652                 else
653                 {
654                     if (typeInfo is XmlSchemaType providerXsdType)
655                     {
656                         string? typeName = providerXsdType.Name;
657                         string? typeNs = null;
658                         if (typeName == null || typeName.Length == 0)
659                         {
660                             DataContract.GetDefaultXmlName(DataContract.GetClrTypeFullName(clrType), out typeName, out typeNs);
661                             xmlName = new XmlQualifiedName(typeName, typeNs);
662                             providerXsdType.Annotation = GetSchemaAnnotation(ExportActualType(xmlName, new XmlDocument()));
663                             xsdType = providerXsdType;
664                         }
665                         else
666                         {
667                             foreach (XmlSchema schema in schemas.Schemas())
668                             {
669                                 foreach (XmlSchemaObject schemaItem in schema.Items)
670                                 {
671                                     if ((object)schemaItem == (object)providerXsdType)
672                                     {
673                                         typeNs = schema.TargetNamespace ?? string.Empty;
674                                         break;
675                                     }
676                                 }
677                                 if (typeNs != null)
678                                     break;
679                             }
680                             if (typeNs == null)
681                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeName, DataContract.GetClrTypeFullName(clrType))));
682                             xmlName = new XmlQualifiedName(typeName, typeNs);
683                         }
684                     }
685                     else
686                         xmlName = (XmlQualifiedName)typeInfo;
687                 }
688             }
689             return true;
690         }
691
692         private static void InvokeGetSchemaMethod(
693             [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
694             Type clrType,
695             XmlSchemaSet schemas, XmlQualifiedName xmlName)
696         {
697             IXmlSerializable ixmlSerializable = (IXmlSerializable)Activator.CreateInstance(clrType)!;
698             XmlSchema? schema = ixmlSerializable.GetSchema();
699             if (schema == null)
700             {
701                 AddDefaultDatasetType(schemas, xmlName.Name, xmlName.Namespace);
702             }
703             else
704             {
705                 if (schema.Id == null || schema.Id.Length == 0)
706                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidReturnSchemaOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType))));
707                 AddDefaultTypedDatasetType(schemas, schema, xmlName.Name, xmlName.Namespace);
708             }
709         }
710
711         internal static void AddDefaultXmlType(XmlSchemaSet schemas, string localName, string ns)
712         {
713             XmlSchemaComplexType defaultXmlType = CreateAnyType();
714             defaultXmlType.Name = localName;
715             XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
716             schema.Items.Add(defaultXmlType);
717             schemas.Reprocess(schema);
718         }
719
720         private static XmlSchemaComplexType CreateAnyType()
721         {
722             XmlSchemaComplexType anyType = new XmlSchemaComplexType();
723             anyType.IsMixed = true;
724             anyType.Particle = new XmlSchemaSequence();
725             XmlSchemaAny any = new XmlSchemaAny();
726             any.MinOccurs = 0;
727             any.MaxOccurs = decimal.MaxValue;
728             any.ProcessContents = XmlSchemaContentProcessing.Lax;
729             ((XmlSchemaSequence)anyType.Particle).Items.Add(any);
730             anyType.AnyAttribute = new XmlSchemaAnyAttribute();
731             return anyType;
732         }
733
734         private static XmlSchemaComplexType CreateAnyElementType()
735         {
736             XmlSchemaComplexType anyElementType = new XmlSchemaComplexType();
737             anyElementType.IsMixed = false;
738             anyElementType.Particle = new XmlSchemaSequence();
739             XmlSchemaAny any = new XmlSchemaAny();
740             any.MinOccurs = 0;
741             any.ProcessContents = XmlSchemaContentProcessing.Lax;
742             ((XmlSchemaSequence)anyElementType.Particle).Items.Add(any);
743             return anyElementType;
744         }
745
746         internal static bool IsSpecialXmlType(Type type, [NotNullWhen(true)] out XmlQualifiedName? typeName, [NotNullWhen(true)] out XmlSchemaType? xsdType, out bool hasRoot)
747         {
748             xsdType = null;
749             hasRoot = true;
750             if (type == Globals.TypeOfXmlElement || type == Globals.TypeOfXmlNodeArray)
751             {
752                 string? name;
753                 if (type == Globals.TypeOfXmlElement)
754                 {
755                     xsdType = CreateAnyElementType();
756                     name = "XmlElement";
757                     hasRoot = false;
758                 }
759                 else
760                 {
761                     xsdType = CreateAnyType();
762                     name = "ArrayOfXmlNode";
763                     hasRoot = true;
764                 }
765                 typeName = new XmlQualifiedName(name, DataContract.GetDefaultXmlNamespace(type));
766                 return true;
767             }
768             typeName = null;
769             return false;
770         }
771
772         private static void AddDefaultDatasetType(XmlSchemaSet schemas, string localName, string ns)
773         {
774             XmlSchemaComplexType type = new XmlSchemaComplexType();
775             type.Name = localName;
776             type.Particle = new XmlSchemaSequence();
777             XmlSchemaElement schemaRefElement = new XmlSchemaElement();
778             schemaRefElement.RefName = new XmlQualifiedName(Globals.SchemaLocalName, XmlSchema.Namespace);
779             ((XmlSchemaSequence)type.Particle).Items.Add(schemaRefElement);
780             XmlSchemaAny any = new XmlSchemaAny();
781             ((XmlSchemaSequence)type.Particle).Items.Add(any);
782             XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
783             schema.Items.Add(type);
784             schemas.Reprocess(schema);
785         }
786
787         private static void AddDefaultTypedDatasetType(XmlSchemaSet schemas, XmlSchema datasetSchema, string localName, string ns)
788         {
789             XmlSchemaComplexType type = new XmlSchemaComplexType();
790             type.Name = localName;
791             type.Particle = new XmlSchemaSequence();
792             XmlSchemaAny any = new XmlSchemaAny();
793             any.Namespace = datasetSchema.TargetNamespace ?? string.Empty;
794             ((XmlSchemaSequence)type.Particle).Items.Add(any);
795             schemas.Add(datasetSchema);
796             XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
797             schema.Items.Add(type);
798             schemas.Reprocess(datasetSchema);
799             schemas.Reprocess(schema);
800         }
801
802         private XmlSchemaAnnotation GetSchemaAnnotation(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
803         {
804             XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
805             XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
806             XmlElement annotationElement = GetAnnotationMarkup(annotationQualifiedName, innerText, schema);
807             appInfo.Markup = new XmlNode[1] { annotationElement };
808             annotation.Items.Add(appInfo);
809             return annotation;
810         }
811
812         private static XmlSchemaAnnotation? GetSchemaAnnotation(params XmlNode?[]? nodes)
813         {
814             if (nodes == null || nodes.Length == 0)
815                 return null;
816             bool hasAnnotation = false;
817             for (int i = 0; i < nodes.Length; i++)
818                 if (nodes[i] != null)
819                 {
820                     hasAnnotation = true;
821                     break;
822                 }
823             if (!hasAnnotation)
824                 return null;
825
826             XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
827             XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
828             annotation.Items.Add(appInfo);
829             appInfo.Markup = nodes;
830             return annotation;
831         }
832
833         private XmlElement GetAnnotationMarkup(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
834         {
835             XmlElement annotationElement = XmlDoc.CreateElement(annotationQualifiedName.Name, annotationQualifiedName.Namespace);
836             SchemaHelper.AddSchemaImport(annotationQualifiedName.Namespace, schema);
837             annotationElement.InnerText = innerText;
838             return annotationElement;
839         }
840
841         private XmlSchema GetSchema(string ns)
842         {
843             return SchemaHelper.GetSchema(ns, Schemas);
844         }
845
846         // Property is not stored in a local because XmlSchemaSequence is mutable.
847         // The schema export process should not expose objects that may be modified later.
848         internal static XmlSchemaSequence ISerializableSequence
849         {
850             get
851             {
852                 XmlSchemaSequence iSerializableSequence = new XmlSchemaSequence();
853                 iSerializableSequence.Items.Add(ISerializableWildcardElement);
854                 return iSerializableSequence;
855             }
856         }
857
858         // Property is not stored in a local because XmlSchemaAny is mutable.
859         // The schema export process should not expose objects that may be modified later.
860         internal static XmlSchemaAny ISerializableWildcardElement
861         {
862             get
863             {
864                 XmlSchemaAny iSerializableWildcardElement = new XmlSchemaAny();
865                 iSerializableWildcardElement.MinOccurs = 0;
866                 iSerializableWildcardElement.MaxOccursString = Globals.OccursUnbounded;
867                 iSerializableWildcardElement.Namespace = "##local";
868                 iSerializableWildcardElement.ProcessContents = XmlSchemaContentProcessing.Skip;
869                 return iSerializableWildcardElement;
870             }
871         }
872
873         private static XmlQualifiedName? s_anytypeQualifiedName;
874         internal static XmlQualifiedName AnytypeQualifiedName => s_anytypeQualifiedName ??= new XmlQualifiedName(Globals.AnyTypeLocalName, Globals.SchemaNamespace);
875
876         private static XmlQualifiedName? s_stringQualifiedName;
877         internal static XmlQualifiedName StringQualifiedName => s_stringQualifiedName ??= new XmlQualifiedName(Globals.StringLocalName, Globals.SchemaNamespace);
878
879         private static XmlQualifiedName? s_defaultEnumBaseTypeName;
880         internal static XmlQualifiedName DefaultEnumBaseTypeName => s_defaultEnumBaseTypeName ??= new XmlQualifiedName(Globals.IntLocalName, Globals.SchemaNamespace);
881
882         private static XmlQualifiedName? s_enumerationValueAnnotationName;
883         internal static XmlQualifiedName EnumerationValueAnnotationName => s_enumerationValueAnnotationName ??= new XmlQualifiedName(Globals.EnumerationValueLocalName, Globals.SerializationNamespace);
884
885         private static XmlQualifiedName? s_surrogateDataAnnotationName;
886         internal static XmlQualifiedName SurrogateDataAnnotationName => s_surrogateDataAnnotationName ??= new XmlQualifiedName(Globals.SurrogateDataLocalName, Globals.SerializationNamespace);
887
888         private static XmlQualifiedName? s_defaultValueAnnotation;
889         internal static XmlQualifiedName DefaultValueAnnotation => s_defaultValueAnnotation ??= new XmlQualifiedName(Globals.DefaultValueLocalName, Globals.SerializationNamespace);
890
891         private static XmlQualifiedName? s_actualTypeAnnotationName;
892         internal static XmlQualifiedName ActualTypeAnnotationName => s_actualTypeAnnotationName ??= new XmlQualifiedName(Globals.ActualTypeLocalName, Globals.SerializationNamespace);
893
894         private static XmlQualifiedName? s_isDictionaryAnnotationName;
895         internal static XmlQualifiedName IsDictionaryAnnotationName => s_isDictionaryAnnotationName ??= new XmlQualifiedName(Globals.IsDictionaryLocalName, Globals.SerializationNamespace);
896
897         private static XmlQualifiedName? s_isValueTypeName;
898         internal static XmlQualifiedName IsValueTypeName => s_isValueTypeName ??= new XmlQualifiedName(Globals.IsValueTypeLocalName, Globals.SerializationNamespace);
899
900         // Property is not stored in a local because XmlSchemaAttribute is mutable.
901         // The schema export process should not expose objects that may be modified later.
902         internal static XmlSchemaAttribute ISerializableFactoryTypeAttribute
903         {
904             get
905             {
906                 XmlSchemaAttribute iSerializableFactoryTypeAttribute = new XmlSchemaAttribute();
907                 iSerializableFactoryTypeAttribute.RefName = new XmlQualifiedName(Globals.ISerializableFactoryTypeLocalName, Globals.SerializationNamespace);
908                 return iSerializableFactoryTypeAttribute;
909             }
910         }
911
912         // Property is not stored in a local because XmlSchemaAttribute is mutable.
913         // The schema export process should not expose objects that may be modified later.
914         internal static XmlSchemaAttribute RefAttribute
915         {
916             get
917             {
918                 XmlSchemaAttribute refAttribute = new XmlSchemaAttribute();
919                 refAttribute.RefName = Globals.RefQualifiedName;
920                 return refAttribute;
921             }
922         }
923
924         // Property is not stored in a local because XmlSchemaAttribute is mutable.
925         // The schema export process should not expose objects that may be modified later.
926         internal static XmlSchemaAttribute IdAttribute
927         {
928             get
929             {
930                 XmlSchemaAttribute idAttribute = new XmlSchemaAttribute();
931                 idAttribute.RefName = Globals.IdQualifiedName;
932                 return idAttribute;
933             }
934         }
935     }
936 }