1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
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;
12 using System.Reflection;
13 using System.Runtime.Serialization.DataContracts;
15 using System.Xml.Schema;
16 using System.Xml.Serialization;
18 namespace System.Runtime.Serialization
20 internal sealed class SchemaExporter
22 private readonly XmlSchemaSet _schemas;
23 private XmlDocument? _xmlDoc;
24 private DataContractSet _dataContractSet;
26 internal SchemaExporter(XmlSchemaSet schemas, DataContractSet dataContractSet)
29 _dataContractSet = dataContractSet;
32 private XmlSchemaSet Schemas
34 get { return _schemas; }
37 private XmlDocument XmlDoc => _xmlDoc ??= new XmlDocument();
39 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
40 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
41 internal void Export()
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)
49 DataContract dataContract = pair.Value;
50 if (!_dataContractSet.IsContractProcessed(dataContract))
52 ExportDataContract(dataContract);
53 _dataContractSet.SetContractProcessed(dataContract);
60 _dataContractSet = null!;
64 private void ExportSerializationSchema()
66 if (!Schemas.Contains(Globals.SerializationNamespace))
68 StringReader reader = new StringReader(Globals.SerializationSchema);
69 XmlSchema? schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null);
71 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace)));
76 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
77 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
78 private void ExportDataContract(DataContract dataContract)
80 if (dataContract.IsBuiltInDataContract)
82 else if (dataContract is XmlDataContract)
83 ExportXmlDataContract((XmlDataContract)dataContract);
86 XmlSchema schema = GetSchema(dataContract.XmlName.Namespace);
88 if (dataContract is ClassDataContract classDataContract)
90 if (classDataContract.IsISerializable)
91 ExportISerializableDataContract(classDataContract, schema);
93 ExportClassDataContract(classDataContract, schema);
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);
104 private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSchema? schema)
106 if (schema == null || dataContract.XmlName.Namespace != dataContract.TopLevelElementNamespace!.Value)
107 schema = GetSchema(dataContract.TopLevelElementNamespace!.Value);
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;
117 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
118 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
119 private void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema)
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);
128 XmlSchemaSequence rootSequence = new XmlSchemaSequence();
129 for (int i = 0; i < classDataContract.Members!.Count; i++)
131 DataMember dataMember = classDataContract.Members[i];
133 XmlSchemaElement element = new XmlSchemaElement();
134 element.Name = dataMember.Name;
135 XmlElement? actualTypeElement = null;
136 DataContract memberTypeContract = _dataContractSet.GetMemberTypeDataContract(dataMember);
137 if (CheckIfMemberHasConflict(dataMember))
139 element.SchemaTypeName = AnytypeQualifiedName;
140 actualTypeElement = ExportActualType(memberTypeContract.XmlName);
141 SchemaHelper.AddSchemaImport(memberTypeContract.XmlName.Namespace, schema);
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;
151 element.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(dataMember), ExportEmitDefaultValue(dataMember));
152 rootSequence.Items.Add(element);
155 XmlElement? isValueTypeElement = null;
156 if (classDataContract.BaseClassContract != null)
158 XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseClassContract.XmlName, schema);
159 extension.Particle = rootSequence;
160 if (classDataContract.IsReference && !classDataContract.BaseClassContract.IsReference)
162 AddReferenceAttributes(extension.Attributes, schema);
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);
173 type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(classDataContract), isValueTypeElement);
176 private static void AddReferenceAttributes(XmlSchemaObjectCollection attributes, XmlSchema schema)
178 SchemaHelper.AddSchemaImport(Globals.SerializationNamespace, schema);
179 schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
180 attributes.Add(IdAttribute);
181 attributes.Add(RefAttribute);
184 private static void SetElementType(XmlSchemaElement element, DataContract dataContract, XmlSchema schema)
186 if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous)
188 element.SchemaType = xmlDataContract.XsdType;
192 element.SchemaTypeName = dataContract.XmlName;
194 if (element.SchemaTypeName.Namespace.Equals(Globals.SerializationNamespace))
195 schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
197 SchemaHelper.AddSchemaImport(dataContract.XmlName.Namespace, schema);
201 private static bool CheckIfMemberHasConflict(DataMember dataMember)
203 if (dataMember.HasConflictingNameAndType)
206 DataMember? conflictingMember = dataMember.ConflictingMember;
207 while (conflictingMember != null)
209 if (conflictingMember.HasConflictingNameAndType)
211 conflictingMember = conflictingMember.ConflictingMember;
217 private XmlElement? ExportEmitDefaultValue(DataMember dataMember)
219 if (dataMember.EmitDefaultValue)
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;
228 private XmlElement ExportActualType(XmlQualifiedName typeName)
230 return ExportActualType(typeName, XmlDoc);
233 private static XmlElement ExportActualType(XmlQualifiedName typeName, XmlDocument xmlDoc)
235 XmlElement actualTypeElement = xmlDoc.CreateElement(ActualTypeAnnotationName.Name, ActualTypeAnnotationName.Namespace);
237 XmlAttribute nameAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNameAttribute);
238 nameAttribute.Value = typeName.Name;
239 actualTypeElement.Attributes.Append(nameAttribute);
241 XmlAttribute nsAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNamespaceAttribute);
242 nsAttribute.Value = typeName.Namespace;
243 actualTypeElement.Attributes.Append(nsAttribute);
245 return actualTypeElement;
248 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
249 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
250 private XmlElement ExportGenericInfo(Type clrType, string elementName, string elementNs)
253 int nestedCollectionLevel = 0;
254 while (CollectionDataContract.IsCollection(clrType, out itemType))
256 if (DataContract.GetBuiltInDataContract(clrType) != null
257 || CollectionDataContract.IsCollectionDataContract(clrType))
262 nestedCollectionLevel++;
265 Type[]? genericArguments = null;
266 IList<int>? genericArgumentCounts = null;
267 if (clrType.IsGenericType)
269 genericArguments = clrType.GetGenericArguments();
271 if (clrType.DeclaringType == null)
272 typeName = clrType.Name;
275 int nsLen = (clrType.Namespace == null) ? 0 : clrType.Namespace.Length;
277 nsLen++; //include the . following namespace
278 typeName = DataContract.GetClrTypeFullName(clrType).Substring(nsLen).Replace('+', '.');
280 int iParam = typeName.IndexOf('[');
282 typeName = typeName.Substring(0, iParam);
283 genericArgumentCounts = DataContract.GetDataContractNameForGenericName(typeName, null);
284 clrType = clrType.GetGenericTypeDefinition();
286 XmlQualifiedName dcqname = DataContract.GetXmlName(clrType);
287 if (nestedCollectionLevel > 0)
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));
294 XmlElement typeElement = XmlDoc.CreateElement(elementName, elementNs);
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);
301 XmlAttribute nsAttribute = XmlDoc.CreateAttribute(Globals.GenericNamespaceAttribute);
302 nsAttribute.Value = dcqname.Namespace;
303 typeElement.Attributes.Append(nsAttribute);
305 if (genericArguments != null)
309 Debug.Assert(genericArgumentCounts != null);
310 foreach (int genericArgumentCount in genericArgumentCounts)
312 for (int i = 0; i < genericArgumentCount; i++, argIndex++)
314 XmlElement argumentElement = ExportGenericInfo(genericArguments[argIndex], Globals.GenericParameterLocalName, Globals.SerializationNamespace);
317 XmlAttribute nestedLevelAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
318 nestedLevelAttribute.Value = nestedLevel.ToString(CultureInfo.InvariantCulture);
319 argumentElement.Attributes.Append(nestedLevelAttribute);
321 typeElement.AppendChild(argumentElement);
325 if (genericArgumentCounts[nestedLevel - 1] == 0)
327 XmlAttribute typeNestedLevelsAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
328 typeNestedLevelsAttribute.Value = genericArgumentCounts.Count.ToString(CultureInfo.InvariantCulture);
329 typeElement.Attributes.Append(typeNestedLevelsAttribute);
336 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
337 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
338 private XmlElement? ExportSurrogateData(object key)
340 object? surrogateData = _dataContractSet.GetSurrogateData(key);
341 if (surrogateData == 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);
355 return (XmlElement?)XmlDoc.ReadNode(XmlReader.Create(new StringReader(stringWriter.ToString())));
358 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
359 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
360 private void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema)
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));
372 XmlSchemaSequence rootSequence = new XmlSchemaSequence();
374 XmlSchemaElement element = new XmlSchemaElement();
375 element.Name = collectionDataContract.ItemName;
376 element.MinOccurs = 0;
377 element.MaxOccursString = Globals.OccursUnbounded;
378 if (collectionDataContract.IsDictionary)
380 ClassDataContract keyValueContract = (collectionDataContract.ItemContract as ClassDataContract)!;
381 XmlSchemaComplexType keyValueType = new XmlSchemaComplexType();
382 XmlSchemaSequence keyValueSequence = new XmlSchemaSequence();
383 foreach (DataMember dataMember in keyValueContract.Members!)
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);
394 keyValueType.Particle = keyValueSequence;
395 element.SchemaType = keyValueType;
399 if (collectionDataContract.IsItemTypeNullable)
400 element.IsNillable = true;
401 DataContract itemContract = _dataContractSet.GetItemTypeDataContract(collectionDataContract);
402 SetElementType(element, itemContract, schema);
404 SchemaHelper.AddElementForm(element, schema);
405 rootSequence.Items.Add(element);
407 type.Particle = rootSequence;
409 if (collectionDataContract.IsReference)
410 AddReferenceAttributes(type.Attributes, schema);
413 private XmlElement ExportIsDictionary()
415 XmlElement isDictionaryElement = XmlDoc.CreateElement(IsDictionaryAnnotationName.Name, IsDictionaryAnnotationName.Namespace);
416 isDictionaryElement.InnerText = Globals.True;
417 return isDictionaryElement;
420 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
421 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
422 private void ExportEnumDataContract(EnumDataContract enumDataContract, XmlSchema schema)
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);
430 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
431 restriction.BaseTypeName = StringQualifiedName;
432 SchemaHelper.AddSchemaImport(enumDataContract.BaseContractName.Namespace, schema);
433 if (enumDataContract.Values != null)
435 for (int i = 0; i < enumDataContract.Values.Count; i++)
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);
444 if (enumDataContract.IsFlags)
446 XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList();
447 XmlSchemaSimpleType anonymousType = new XmlSchemaSimpleType();
448 anonymousType.Content = restriction;
449 list.ItemType = anonymousType;
453 type.Content = restriction;
456 internal static long GetDefaultEnumValue(bool isFlags, int index)
458 return isFlags ? (long)Math.Pow(2, index) : index;
461 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
462 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
463 private void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema)
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);
472 XmlElement? isValueTypeElement = null;
473 if (dataContract.BaseClassContract != null)
475 _ = CreateTypeContent(type, dataContract.BaseClassContract.XmlName, schema);
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);
487 type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(dataContract), isValueTypeElement);
490 private static XmlSchemaComplexContentExtension CreateTypeContent(XmlSchemaComplexType type, XmlQualifiedName baseTypeName, XmlSchema schema)
492 SchemaHelper.AddSchemaImport(baseTypeName.Namespace, schema);
494 XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
495 extension.BaseTypeName = baseTypeName;
496 type.ContentModel = new XmlSchemaComplexContent();
497 type.ContentModel.Content = extension;
502 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
503 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
504 private void ExportXmlDataContract(XmlDataContract dataContract)
506 XmlQualifiedName? typeQName;
508 XmlSchemaType? xsdType;
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);
517 if (!(typeQName.Equals(dataContract.XmlName)))
519 Fx.Assert("XML data contract type name does not match schema name");
523 if (SchemaHelper.GetSchemaElement(Schemas,
524 new XmlQualifiedName(dataContract.TopLevelElementName!.Value, dataContract.TopLevelElementNamespace!.Value),
527 XmlSchemaElement topLevelElement = ExportTopLevelElement(dataContract, schema);
528 topLevelElement.IsNillable = dataContract.IsTopLevelElementNullable;
529 ReprocessAll(_schemas);
532 XmlSchemaType? anonymousType = xsdType;
533 xsdType = SchemaHelper.GetSchemaType(_schemas, typeQName, out schema);
534 if (anonymousType == null && xsdType == null && typeQName.Namespace != XmlSchema.Namespace)
536 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeQName, DataContract.GetClrTypeFullName(clrType))));
540 xsdType.Annotation = GetSchemaAnnotation(
541 ExportSurrogateData(dataContract),
542 dataContract.IsValueType ?
543 GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(dataContract.IsValueType), schema!) :
550 private static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate items
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++)
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++)
563 XmlSchemaObject item = itemArray[j];
565 XmlQualifiedName qname;
566 if (item is XmlSchemaElement)
569 qname = new XmlQualifiedName(((XmlSchemaElement)item).Name, schema.TargetNamespace);
571 else if (item is XmlSchemaType)
574 qname = new XmlQualifiedName(((XmlSchemaType)item).Name, schema.TargetNamespace);
578 object? otherItem = items[qname];
579 if (otherItem != null)
581 schema.Items.Remove(item);
584 items.Add(qname, item);
586 schemas.Reprocess(schema);
590 [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
591 [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
592 internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot)
594 if (IsSpecialXmlType(type, out xmlName!, out xsdType, out hasRoot))
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))));
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)
609 object[] attrs = clrType.GetCustomAttributes(Globals.TypeOfXmlSchemaProviderAttribute, false);
610 if (attrs == null || attrs.Length == 0)
612 xmlName = DataContract.GetDefaultXmlName(clrType);
616 XmlSchemaProviderAttribute provider = (XmlSchemaProviderAttribute)attrs[0];
619 xsdType = CreateAnyElementType();
622 string? methodName = provider.MethodName;
623 if (methodName == null || methodName.Length == 0)
626 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidGetSchemaMethod, DataContract.GetClrTypeFullName(clrType))));
627 xmlName = DataContract.GetDefaultXmlName(clrType);
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)));
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))));
638 object? typeInfo = getMethod.Invoke(null, new object[] { schemas });
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);
646 else if (typeInfo == null)
648 xsdType = CreateAnyElementType();
650 xmlName = DataContract.GetDefaultXmlName(clrType);
654 if (typeInfo is XmlSchemaType providerXsdType)
656 string? typeName = providerXsdType.Name;
657 string? typeNs = null;
658 if (typeName == null || typeName.Length == 0)
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;
667 foreach (XmlSchema schema in schemas.Schemas())
669 foreach (XmlSchemaObject schemaItem in schema.Items)
671 if ((object)schemaItem == (object)providerXsdType)
673 typeNs = schema.TargetNamespace ?? string.Empty;
681 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeName, DataContract.GetClrTypeFullName(clrType))));
682 xmlName = new XmlQualifiedName(typeName, typeNs);
686 xmlName = (XmlQualifiedName)typeInfo;
692 private static void InvokeGetSchemaMethod(
693 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
695 XmlSchemaSet schemas, XmlQualifiedName xmlName)
697 IXmlSerializable ixmlSerializable = (IXmlSerializable)Activator.CreateInstance(clrType)!;
698 XmlSchema? schema = ixmlSerializable.GetSchema();
701 AddDefaultDatasetType(schemas, xmlName.Name, xmlName.Namespace);
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);
711 internal static void AddDefaultXmlType(XmlSchemaSet schemas, string localName, string ns)
713 XmlSchemaComplexType defaultXmlType = CreateAnyType();
714 defaultXmlType.Name = localName;
715 XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
716 schema.Items.Add(defaultXmlType);
717 schemas.Reprocess(schema);
720 private static XmlSchemaComplexType CreateAnyType()
722 XmlSchemaComplexType anyType = new XmlSchemaComplexType();
723 anyType.IsMixed = true;
724 anyType.Particle = new XmlSchemaSequence();
725 XmlSchemaAny any = new XmlSchemaAny();
727 any.MaxOccurs = decimal.MaxValue;
728 any.ProcessContents = XmlSchemaContentProcessing.Lax;
729 ((XmlSchemaSequence)anyType.Particle).Items.Add(any);
730 anyType.AnyAttribute = new XmlSchemaAnyAttribute();
734 private static XmlSchemaComplexType CreateAnyElementType()
736 XmlSchemaComplexType anyElementType = new XmlSchemaComplexType();
737 anyElementType.IsMixed = false;
738 anyElementType.Particle = new XmlSchemaSequence();
739 XmlSchemaAny any = new XmlSchemaAny();
741 any.ProcessContents = XmlSchemaContentProcessing.Lax;
742 ((XmlSchemaSequence)anyElementType.Particle).Items.Add(any);
743 return anyElementType;
746 internal static bool IsSpecialXmlType(Type type, [NotNullWhen(true)] out XmlQualifiedName? typeName, [NotNullWhen(true)] out XmlSchemaType? xsdType, out bool hasRoot)
750 if (type == Globals.TypeOfXmlElement || type == Globals.TypeOfXmlNodeArray)
753 if (type == Globals.TypeOfXmlElement)
755 xsdType = CreateAnyElementType();
761 xsdType = CreateAnyType();
762 name = "ArrayOfXmlNode";
765 typeName = new XmlQualifiedName(name, DataContract.GetDefaultXmlNamespace(type));
772 private static void AddDefaultDatasetType(XmlSchemaSet schemas, string localName, string ns)
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);
787 private static void AddDefaultTypedDatasetType(XmlSchemaSet schemas, XmlSchema datasetSchema, string localName, string ns)
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);
802 private XmlSchemaAnnotation GetSchemaAnnotation(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
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);
812 private static XmlSchemaAnnotation? GetSchemaAnnotation(params XmlNode?[]? nodes)
814 if (nodes == null || nodes.Length == 0)
816 bool hasAnnotation = false;
817 for (int i = 0; i < nodes.Length; i++)
818 if (nodes[i] != null)
820 hasAnnotation = true;
826 XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
827 XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
828 annotation.Items.Add(appInfo);
829 appInfo.Markup = nodes;
833 private XmlElement GetAnnotationMarkup(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
835 XmlElement annotationElement = XmlDoc.CreateElement(annotationQualifiedName.Name, annotationQualifiedName.Namespace);
836 SchemaHelper.AddSchemaImport(annotationQualifiedName.Namespace, schema);
837 annotationElement.InnerText = innerText;
838 return annotationElement;
841 private XmlSchema GetSchema(string ns)
843 return SchemaHelper.GetSchema(ns, Schemas);
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
852 XmlSchemaSequence iSerializableSequence = new XmlSchemaSequence();
853 iSerializableSequence.Items.Add(ISerializableWildcardElement);
854 return iSerializableSequence;
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
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;
873 private static XmlQualifiedName? s_anytypeQualifiedName;
874 internal static XmlQualifiedName AnytypeQualifiedName => s_anytypeQualifiedName ??= new XmlQualifiedName(Globals.AnyTypeLocalName, Globals.SchemaNamespace);
876 private static XmlQualifiedName? s_stringQualifiedName;
877 internal static XmlQualifiedName StringQualifiedName => s_stringQualifiedName ??= new XmlQualifiedName(Globals.StringLocalName, Globals.SchemaNamespace);
879 private static XmlQualifiedName? s_defaultEnumBaseTypeName;
880 internal static XmlQualifiedName DefaultEnumBaseTypeName => s_defaultEnumBaseTypeName ??= new XmlQualifiedName(Globals.IntLocalName, Globals.SchemaNamespace);
882 private static XmlQualifiedName? s_enumerationValueAnnotationName;
883 internal static XmlQualifiedName EnumerationValueAnnotationName => s_enumerationValueAnnotationName ??= new XmlQualifiedName(Globals.EnumerationValueLocalName, Globals.SerializationNamespace);
885 private static XmlQualifiedName? s_surrogateDataAnnotationName;
886 internal static XmlQualifiedName SurrogateDataAnnotationName => s_surrogateDataAnnotationName ??= new XmlQualifiedName(Globals.SurrogateDataLocalName, Globals.SerializationNamespace);
888 private static XmlQualifiedName? s_defaultValueAnnotation;
889 internal static XmlQualifiedName DefaultValueAnnotation => s_defaultValueAnnotation ??= new XmlQualifiedName(Globals.DefaultValueLocalName, Globals.SerializationNamespace);
891 private static XmlQualifiedName? s_actualTypeAnnotationName;
892 internal static XmlQualifiedName ActualTypeAnnotationName => s_actualTypeAnnotationName ??= new XmlQualifiedName(Globals.ActualTypeLocalName, Globals.SerializationNamespace);
894 private static XmlQualifiedName? s_isDictionaryAnnotationName;
895 internal static XmlQualifiedName IsDictionaryAnnotationName => s_isDictionaryAnnotationName ??= new XmlQualifiedName(Globals.IsDictionaryLocalName, Globals.SerializationNamespace);
897 private static XmlQualifiedName? s_isValueTypeName;
898 internal static XmlQualifiedName IsValueTypeName => s_isValueTypeName ??= new XmlQualifiedName(Globals.IsValueTypeLocalName, Globals.SerializationNamespace);
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
906 XmlSchemaAttribute iSerializableFactoryTypeAttribute = new XmlSchemaAttribute();
907 iSerializableFactoryTypeAttribute.RefName = new XmlQualifiedName(Globals.ISerializableFactoryTypeLocalName, Globals.SerializationNamespace);
908 return iSerializableFactoryTypeAttribute;
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
918 XmlSchemaAttribute refAttribute = new XmlSchemaAttribute();
919 refAttribute.RefName = Globals.RefQualifiedName;
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
930 XmlSchemaAttribute idAttribute = new XmlSchemaAttribute();
931 idAttribute.RefName = Globals.IdQualifiedName;