|
// 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.CodeDom;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization.DataContracts;
using System.Xml;
using System.Xml.Schema;
using ExceptionUtil = System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility;
namespace System.Runtime.Serialization
{
/// <summary>
/// Allows the transformation of a set of XML schema files (.xsd) into common language runtime (CLR) types.
/// </summary>
/// <remarks>
/// Use the <see cref="XsdDataContractImporter"/> if you are creating a Web service that must interoperate with an existing
/// Web service, or to create data contract types from XML schemas. <see cref="XsdDataContractImporter"/> will transform a
/// set of XML schemas and create the .NET Framework types that represent the data contract in a selected programming language.
/// To create the code, use the classes in the <see cref="System.CodeDom"/> namespace.
///
/// Conversely, use the <see cref="XsdDataContractExporter"/> class when you have created a Web service that incorporates
/// data represented by CLR types and when you need to export XML schemas for each data type to be consumed by other Web
/// services.That is, <see cref="XsdDataContractExporter"/> transforms a set of CLR types into a set of XML schemas.
/// </remarks>
public class XsdDataContractImporter
{
private CodeCompileUnit _codeCompileUnit = null!; // Not directly referenced. Always lazy initialized by property getter.
private DataContractSet? _dataContractSet;
private static readonly XmlQualifiedName[] s_emptyTypeNameArray = Array.Empty<XmlQualifiedName>();
private XmlQualifiedName[] _singleTypeNameArray = null!; // Not directly referenced. Always lazy initialized by property getter.
private XmlSchemaElement[] _singleElementArray = null!; // Not directly referenced. Always lazy initialized by property getter.
/// <summary>
/// Initializes a new instance of the <see cref="XsdDataContractImporter"/> class.
/// </summary>
public XsdDataContractImporter()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="XsdDataContractImporter"/> class with the <see cref="System.CodeDom.CodeCompileUnit"/> that will be used to generate CLR code.
/// </summary>
/// <param name="codeCompileUnit">The <see cref="System.CodeDom.CodeCompileUnit"/> that will be used to store the code.</param>
public XsdDataContractImporter(CodeCompileUnit codeCompileUnit)
{
_codeCompileUnit = codeCompileUnit;
}
/// <summary>
/// Gets or sets an <see cref="ImportOptions"/> that contains settable options for the import operation.
/// </summary>
public ImportOptions? Options { get; set; }
/// <summary>
/// Gets a <see cref="System.CodeDom.CodeCompileUnit"/> used for storing the CLR types generated.
/// </summary>
public CodeCompileUnit CodeCompileUnit => _codeCompileUnit ??= new CodeCompileUnit();
private DataContractSet DataContractSet
{
get
{
return _dataContractSet ??= Options == null ? new DataContractSet(null, null, null) :
new DataContractSet(Options.DataContractSurrogate, Options.ReferencedTypes, Options.ReferencedCollectionTypes);
}
}
/// <summary>
/// Transforms the specified set of XML schemas contained in an <see cref="XmlSchemaSet"/> into a <see cref="System.CodeDom.CodeCompileUnit"/>.
/// </summary>
/// <param name="schemas">A <see cref="XmlSchemaSet"/> that contains the schema representations to generate CLR types for.</param>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public void Import(XmlSchemaSet schemas)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
InternalImport(schemas, null, null);
}
/// <summary>
/// Transforms the specified set of schema types contained in an <see cref="XmlSchemaSet"/> into CLR types generated into a <see cref="System.CodeDom.CodeCompileUnit"/>.
/// </summary>
/// <param name="schemas">A <see cref="XmlSchemaSet"/> that contains the schema representations.</param>
/// <param name="typeNames">The set of schema types to import.</param>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public void Import(XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
if (typeNames == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeNames)));
InternalImport(schemas, typeNames, null);
}
/// <summary>
/// Transforms the specified XML schema type contained in an <see cref="XmlSchemaSet"/> into a <see cref="System.CodeDom.CodeCompileUnit"/>.
/// </summary>
/// <param name="schemas">A <see cref="XmlSchemaSet"/> that contains the schema representations.</param>
/// <param name="typeName">A <see cref="XmlQualifiedName"/> that represents a specific schema type to import.</param>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public void Import(XmlSchemaSet schemas, XmlQualifiedName typeName)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
if (typeName == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName)));
SingleTypeNameArray[0] = typeName;
InternalImport(schemas, SingleTypeNameArray, null);
}
/// <summary>
/// Transforms the specified schema element in the set of specified XML schemas into a <see cref="System.CodeDom.CodeCompileUnit"/> and
/// returns an <see cref="XmlQualifiedName"/> that represents the data contract name for the specified element.
/// </summary>
/// <param name="schemas">An <see cref="XmlSchemaSet"/> that contains the schemas to transform.</param>
/// <param name="element">An <see cref="XmlSchemaElement"/> that represents the specific schema element to transform.</param>
/// <returns>An <see cref="XmlQualifiedName"/> that represents the specified element.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public XmlQualifiedName? Import(XmlSchemaSet schemas, XmlSchemaElement element)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
if (element == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(element)));
SingleElementArray[0] = element;
IList<XmlQualifiedName>? elementNames = InternalImport(schemas, s_emptyTypeNameArray, SingleElementArray);
Debug.Assert(elementNames != null && elementNames.Count > 0);
return elementNames[0];
}
/// <summary>
/// Gets a value that indicates whether the schemas contained in an <see cref="XmlSchemaSet"/> can be transformed into a <see cref="System.CodeDom.CodeCompileUnit"/>.
/// </summary>
/// <param name="schemas">A <see cref="XmlSchemaSet"/> that contains the schemas to transform.</param>
/// <returns><see langword="true" /> if the schemas can be transformed to data contract types; otherwise, <see langword="false" />.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public bool CanImport(XmlSchemaSet schemas)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
return InternalCanImport(schemas, null, null);
}
/// <summary>
/// Gets a value that indicates whether the specified set of types contained in an <see cref="XmlSchemaSet"/> can be transformed into CLR types generated into a <see cref="System.CodeDom.CodeCompileUnit"/>.
/// </summary>
/// <param name="schemas">The schemas to transform.</param>
/// <param name="typeNames">The set of schema types to import.</param>
/// <returns><see langword="true" /> if the schemas can be transformed; otherwise, <see langword="false" />.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public bool CanImport(XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
if (typeNames == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeNames)));
return InternalCanImport(schemas, typeNames, null);
}
/// <summary>
/// Gets a value that indicates whether the schemas contained in an <see cref="XmlSchemaSet"/> can be transformed into a <see cref="System.CodeDom.CodeCompileUnit"/>.
/// </summary>
/// <param name="schemas">The schema representations.</param>
/// <param name="typeName">The names of the schema types that need to be imported from the <see cref="XmlSchemaSet"/>.</param>
/// <returns><see langword="true" /> if the schemas can be transformed to data contract types; otherwise, <see langword="false" />.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public bool CanImport(XmlSchemaSet schemas, XmlQualifiedName typeName)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
if (typeName == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName)));
return InternalCanImport(schemas, new XmlQualifiedName[] { typeName }, null);
}
/// <summary>
/// Gets a value that indicates whether a specific schema element contained in an <see cref="XmlSchemaSet"/> can be imported.
/// </summary>
/// <param name="schemas">An <see cref="XmlSchemaSet"/> to import.</param>
/// <param name="element">A specific <see cref="XmlSchemaElement"/> to check in the set of schemas.</param>
/// <returns><see langword="true" /> if the element can be imported; otherwise, <see langword="false" />.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public bool CanImport(XmlSchemaSet schemas, XmlSchemaElement element)
{
if (schemas == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas)));
if (element == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(element)));
SingleElementArray[0] = element;
return InternalCanImport(schemas, s_emptyTypeNameArray, SingleElementArray);
}
/// <summary>
/// Returns a <see cref="CodeTypeReference"/> to the CLR type generated for the schema type with the specified <see cref="XmlQualifiedName"/>.
/// </summary>
/// <param name="typeName">The <see cref="XmlQualifiedName"/> that specifies the schema type to look up.</param>
/// <returns>A <see cref="CodeTypeReference"/> reference to the CLR type generated for the schema type with the typeName specified.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public CodeTypeReference GetCodeTypeReference(XmlQualifiedName typeName)
{
DataContract dataContract = FindDataContract(typeName);
CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit);
return codeExporter.GetCodeTypeReference(dataContract);
}
/// <summary>
/// Returns a <see cref="CodeTypeReference"/> for the specified XML qualified element and schema element.
/// </summary>
/// <param name="typeName">An <see cref="XmlQualifiedName"/> that specifies the XML qualified name of the schema type to look up.</param>
/// <param name="element">An <see cref="XmlSchemaElement"/> that specifies an element in an XML schema.</param>
/// <returns>A <see cref="CodeTypeReference"/> that represents the type that was generated for the specified schema type.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public CodeTypeReference GetCodeTypeReference(XmlQualifiedName typeName, XmlSchemaElement element)
{
if (element == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(element)));
if (typeName == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName)));
DataContract dataContract = FindDataContract(typeName);
CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit);
return codeExporter.GetElementTypeReference(dataContract, element.IsNillable);
}
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
internal DataContract FindDataContract(XmlQualifiedName typeName)
{
if (typeName == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName)));
DataContract? dataContract = DataContract.GetBuiltInDataContract(typeName.Name, typeName.Namespace);
if (dataContract == null)
{
dataContract = DataContractSet.GetDataContract(typeName);
if (dataContract == null)
throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.TypeHasNotBeenImported, typeName.Name, typeName.Namespace)));
}
return dataContract;
}
/// <summary>
/// Returns a list of <see cref="CodeTypeReference"/> objects that represents the known types generated when generating code for the specified schema type.
/// </summary>
/// <param name="typeName">An <see cref="XmlQualifiedName"/> that represents the schema type to look up known types for.</param>
/// <returns>A collection of type <see cref="CodeTypeReference"/>.</returns>
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
public ICollection<CodeTypeReference>? GetKnownTypeReferences(XmlQualifiedName typeName)
{
if (typeName == null)
throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(typeName)));
DataContract? dataContract = DataContract.GetBuiltInDataContract(typeName.Name, typeName.Namespace);
if (dataContract == null)
{
dataContract = DataContractSet.GetDataContract(typeName);
if (dataContract == null)
throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.TypeHasNotBeenImported, typeName.Name, typeName.Namespace)));
}
CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit);
return codeExporter.GetKnownTypeReferences(dataContract);
}
private XmlQualifiedName[] SingleTypeNameArray
{
get
{
return _singleTypeNameArray ??= new XmlQualifiedName[1];
}
}
private XmlSchemaElement[] SingleElementArray
{
get
{
return _singleElementArray ??= new XmlSchemaElement[1];
}
}
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
private IList<XmlQualifiedName>? InternalImport(XmlSchemaSet schemas, ICollection<XmlQualifiedName>? typeNames, ICollection<XmlSchemaElement>? elements)
{
DataContractSet? oldValue = (_dataContractSet == null) ? null : new DataContractSet(_dataContractSet);
IList<XmlQualifiedName>? elementTypeNames = null;
try
{
if (elements != null)
elementTypeNames = DataContractSet.ImportSchemaSet(schemas, elements, ImportXmlDataType);
else
DataContractSet.ImportSchemaSet(schemas, typeNames, ImportXmlDataType);
CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit);
codeExporter.Export();
return elementTypeNames;
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
throw;
_dataContractSet = oldValue;
throw;
}
}
private bool ImportXmlDataType
{
get
{
return Options == null ? false : Options.ImportXmlType;
}
}
[RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)]
private bool InternalCanImport(XmlSchemaSet schemas, ICollection<XmlQualifiedName>? typeNames, ICollection<XmlSchemaElement>? elements)
{
DataContractSet? oldValue = (_dataContractSet == null) ? null : new DataContractSet(_dataContractSet);
try
{
if (elements != null)
DataContractSet.ImportSchemaSet(schemas, elements, ImportXmlDataType);
else
DataContractSet.ImportSchemaSet(schemas, typeNames, ImportXmlDataType);
return true;
}
catch (InvalidDataContractException)
{
_dataContractSet = oldValue;
return false;
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
throw;
_dataContractSet = oldValue;
throw;
}
}
private static XmlQualifiedName? s_actualTypeAnnotationName;
internal static XmlQualifiedName ActualTypeAnnotationName => s_actualTypeAnnotationName ??= new XmlQualifiedName(ImportGlobals.ActualTypeLocalName, ImportGlobals.SerializationNamespace);
internal static XmlQualifiedName ImportActualType(XmlSchemaAnnotation? annotation, XmlQualifiedName defaultTypeName, XmlQualifiedName typeName)
{
XmlElement? actualTypeElement = ImportAnnotation(annotation, ActualTypeAnnotationName);
if (actualTypeElement == null)
return defaultTypeName;
XmlNode? nameAttribute = actualTypeElement.Attributes.GetNamedItem(ImportGlobals.ActualTypeNameAttribute);
if (nameAttribute?.Value == null)
throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, ImportGlobals.ActualTypeNameAttribute)));
XmlNode? nsAttribute = actualTypeElement.Attributes.GetNamedItem(ImportGlobals.ActualTypeNamespaceAttribute);
if (nsAttribute?.Value == null)
throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, ImportGlobals.ActualTypeNamespaceAttribute)));
return new XmlQualifiedName(nameAttribute.Value, nsAttribute.Value);
}
private static XmlElement? ImportAnnotation(XmlSchemaAnnotation? annotation, XmlQualifiedName annotationQualifiedName)
{
if (annotation != null && annotation.Items != null && annotation.Items.Count > 0 && annotation.Items[0] is XmlSchemaAppInfo)
{
XmlSchemaAppInfo appInfo = (XmlSchemaAppInfo)annotation.Items[0];
XmlNode?[]? markup = appInfo.Markup;
if (markup != null)
{
for (int i = 0; i < markup.Length; i++)
{
XmlElement? annotationElement = markup[i] as XmlElement;
if (annotationElement != null && annotationElement.LocalName == annotationQualifiedName.Name && annotationElement.NamespaceURI == annotationQualifiedName.Namespace)
return annotationElement;
}
}
}
return null;
}
}
}
|