| File: System\Runtime\Serialization\XmlDataContract.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.DataContractSerialization\src\System.Private.DataContractSerialization.csproj (System.Private.DataContractSerialization) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Threading; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using DataContractDictionary = System.Collections.Generic.Dictionary<System.Xml.XmlQualifiedName, System.Runtime.Serialization.DataContracts.DataContract>; namespace System.Runtime.Serialization.DataContracts { internal delegate IXmlSerializable CreateXmlSerializableDelegate(); public sealed class XmlDataContract : DataContract { internal const string ContractTypeString = nameof(XmlDataContract); public override string? ContractType => ContractTypeString; private readonly XmlDataContractCriticalHelper _helper; [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(type)) { _helper = (base.Helper as XmlDataContractCriticalHelper)!; } public override DataContractDictionary? KnownDataContracts { [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get => _helper.KnownDataContracts; internal set => _helper.KnownDataContracts = value; } public XmlSchemaType? XsdType { get => _helper.XsdType; internal set => _helper.XsdType = value; } public bool IsAnonymous { get => _helper.IsAnonymous; } public new bool IsValueType { get => _helper.IsValueType; set => _helper.IsValueType = value; } public new bool HasRoot { get => _helper.HasRoot; internal set => _helper.HasRoot = value; } public override XmlDictionaryString? TopLevelElementName { get => _helper.TopLevelElementName; internal set => _helper.TopLevelElementName = value; } public override XmlDictionaryString? TopLevelElementNamespace { get => _helper.TopLevelElementNamespace; internal set => _helper.TopLevelElementNamespace = value; } public bool IsTopLevelElementNullable { get => _helper.IsTopLevelElementNullable; internal set => _helper.IsTopLevelElementNullable = value; } public bool IsTypeDefinedOnImport { get => _helper.IsTypeDefinedOnImport; set => _helper.IsTypeDefinedOnImport = value; } internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate { [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { // We create XmlSerializableDelegate via CodeGen when CodeGen is enabled; // otherwise, we would create the delegate via reflection. if (DataContractSerializer.Option == SerializationOption.CodeGenOnly || DataContractSerializer.Option == SerializationOption.ReflectionAsBackup) { if (_helper.CreateXmlSerializableDelegate == null) { lock (this) { if (_helper.CreateXmlSerializableDelegate == null) { CreateXmlSerializableDelegate tempCreateXmlSerializable = GenerateCreateXmlSerializableDelegate(); Interlocked.MemoryBarrier(); _helper.CreateXmlSerializableDelegate = tempCreateXmlSerializable; } } } return _helper.CreateXmlSerializableDelegate; } return () => ReflectionCreateXmlSerializable(this.UnderlyingType); } } internal override bool CanContainReferences => false; public override bool IsBuiltInDataContract => UnderlyingType == Globals.TypeOfXmlElement || UnderlyingType == Globals.TypeOfXmlNodeArray; private sealed class XmlDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private DataContractDictionary? _knownDataContracts; private bool _isKnownTypeAttributeChecked; private XmlDictionaryString? _topLevelElementName; private XmlDictionaryString? _topLevelElementNamespace; private bool _isTopLevelElementNullable; private bool _isTypeDefinedOnImport; private CreateXmlSerializableDelegate? _createXmlSerializable; private XmlSchemaType? _xsdType; [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal XmlDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) { if (type.IsDefined(Globals.TypeOfDataContractAttribute, false)) throw new InvalidDataContractException(SR.Format(SR.IXmlSerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type))); if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) throw new InvalidDataContractException(SR.Format(SR.IXmlSerializableCannotHaveCollectionDataContract, DataContract.GetClrTypeFullName(type))); bool hasRoot; XmlSchemaType? xsdType; XmlQualifiedName xmlName; SchemaExporter.GetXmlTypeInfo(type, out xmlName, out xsdType, out hasRoot); XmlName = xmlName; XsdType = xsdType; HasRoot = hasRoot; XmlDictionary dictionary = new XmlDictionary(); Name = dictionary.Add(XmlName.Name); Namespace = dictionary.Add(XmlName.Namespace); object[]? xmlRootAttributes = UnderlyingType?.GetCustomAttributes(Globals.TypeOfXmlRootAttribute, false).ToArray(); if (xmlRootAttributes == null || xmlRootAttributes.Length == 0) { if (hasRoot) { _topLevelElementName = Name; _topLevelElementNamespace = (this.XmlName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace; _isTopLevelElementNullable = true; } } else { if (hasRoot) { XmlRootAttribute xmlRootAttribute = (XmlRootAttribute)xmlRootAttributes[0]; _isTopLevelElementNullable = xmlRootAttribute.IsNullable; string elementName = xmlRootAttribute.ElementName; _topLevelElementName = string.IsNullOrEmpty(elementName) ? Name : dictionary.Add(DataContract.EncodeLocalName(elementName)); string? elementNs = xmlRootAttribute.Namespace; _topLevelElementNamespace = string.IsNullOrEmpty(elementNs) ? DictionaryGlobals.EmptyString : dictionary.Add(elementNs); } else { throw new InvalidDataContractException(SR.Format(SR.IsAnyCannotHaveXmlRoot, DataContract.GetClrTypeFullName(UnderlyingType!))); } } } internal override DataContractDictionary? KnownDataContracts { [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { if (!_isKnownTypeAttributeChecked && UnderlyingType != null) { lock (this) { if (!_isKnownTypeAttributeChecked) { _knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType); Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } _knownDataContracts ??= new DataContractDictionary(); } } return _knownDataContracts; } set { _knownDataContracts = value; } } internal XmlSchemaType? XsdType { get => _xsdType; set => _xsdType = value; } internal bool IsAnonymous => _xsdType != null; internal override XmlDictionaryString? TopLevelElementName { get => _topLevelElementName; set => _topLevelElementName = value; } internal override XmlDictionaryString? TopLevelElementNamespace { get => _topLevelElementNamespace; set => _topLevelElementNamespace = value; } internal bool IsTopLevelElementNullable { get => _isTopLevelElementNullable; set => _isTopLevelElementNullable = value; } internal bool IsTypeDefinedOnImport { get => _isTypeDefinedOnImport; set => _isTypeDefinedOnImport = value; } internal CreateXmlSerializableDelegate? CreateXmlSerializableDelegate { get => _createXmlSerializable; set => _createXmlSerializable = value; } } private ConstructorInfo? GetConstructor() { if (UnderlyingType.IsValueType) return null; ConstructorInfo? ctor = UnderlyingType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.EmptyTypes); if (ctor == null) throw new InvalidDataContractException(SR.Format(SR.IXmlSerializableMustHaveDefaultConstructor, DataContract.GetClrTypeFullName(UnderlyingType))); return ctor; } internal void SetTopLevelElementName(XmlQualifiedName? elementName) { if (elementName != null) { XmlDictionary dictionary = new XmlDictionary(); TopLevelElementName = dictionary.Add(elementName.Name); TopLevelElementNamespace = dictionary.Add(elementName.Namespace); } } [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate() { Type type = this.UnderlyingType; CodeGenerator ilg = new CodeGenerator(); bool memberAccessFlag = RequiresMemberAccessForCreate(null) && !(type.FullName == "System.Xml.Linq.XElement"); try { ilg.BeginMethod("Create" + DataContract.GetClrTypeFullName(type), typeof(CreateXmlSerializableDelegate), memberAccessFlag); } catch (SecurityException securityException) { if (memberAccessFlag) { RequiresMemberAccessForCreate(securityException); } else { throw; } } if (type.IsValueType) { System.Reflection.Emit.LocalBuilder local = ilg.DeclareLocal(type); ilg.Ldloca(local); ilg.InitObj(type); ilg.Ldloc(local); } else { // Special case XElement // codegen the same as 'internal XElement : this("default") { }' ConstructorInfo ctor = GetConstructor()!; if (!ctor.IsPublic && type.FullName == "System.Xml.Linq.XElement") { Type? xName = type.Assembly.GetType("System.Xml.Linq.XName"); if (xName != null) { MethodInfo? XName_op_Implicit = xName.GetMethod( "op_Implicit", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, new Type[] { typeof(string) } ); ConstructorInfo? XElement_ctor = type.GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, new Type[] { xName } ); if (XName_op_Implicit != null && XElement_ctor != null) { ilg.Ldstr("default"); ilg.Call(XName_op_Implicit); ctor = XElement_ctor; } } } ilg.New(ctor); } ilg.ConvertValue(this.UnderlyingType, Globals.TypeOfIXmlSerializable); ilg.Ret(); return (CreateXmlSerializableDelegate)ilg.EndMethod(); } /// <SecurityNote> /// Review - calculates whether this Xml type requires MemberAccessPermission for deserialization. /// since this information is used to determine whether to give the generated code access /// permissions to private members, any changes to the logic should be reviewed. /// </SecurityNote> private bool RequiresMemberAccessForCreate(SecurityException? securityException) { if (!IsTypeVisible(UnderlyingType)) { if (securityException != null) { throw new SecurityException(SR.Format(SR.PartialTrustIXmlSerializableTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)), securityException); } return true; } if (ConstructorRequiresMemberAccess(GetConstructor())) { if (securityException != null) { throw new SecurityException(SR.Format(SR.PartialTrustIXmlSerialzableNoPublicConstructor, DataContract.GetClrTypeFullName(UnderlyingType)), securityException); } return true; } return false; } internal IXmlSerializable ReflectionCreateXmlSerializable(Type type) { if (type.IsValueType) { throw new NotImplementedException("ReflectionCreateXmlSerializable - value type"); } else { object? o; if (type == typeof(System.Xml.Linq.XElement)) { o = new System.Xml.Linq.XElement("default"); } else { ConstructorInfo ctor = GetConstructor()!; o = ctor.Invoke(Array.Empty<object>()); } return (IXmlSerializable)o; } } internal override bool Equals(object? other, HashSet<DataContractPairKey>? checkedContracts) { if (IsEqualOrChecked(other, checkedContracts)) return true; if (other is XmlDataContract dataContract) { if (this.HasRoot != dataContract.HasRoot) return false; if (this.IsAnonymous) { return dataContract.IsAnonymous; } else { return (XmlName.Name == dataContract.XmlName.Name && XmlName.Namespace == dataContract.XmlName.Namespace); } } return false; } [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { if (context == null) XmlObjectSerializerWriteContext.WriteRootIXmlSerializable(xmlWriter, obj); else context.WriteIXmlSerializable(xmlWriter, obj); } [RequiresDynamicCode(DataContract.SerializerAOTWarning)] [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object? o; if (context == null) { o = XmlObjectSerializerReadContext.ReadRootIXmlSerializable(xmlReader, this, true /*isMemberType*/); } else { o = context.ReadIXmlSerializable(xmlReader, this, true /*isMemberType*/); context.AddNewObject(o); } xmlReader.ReadEndElement(); return o; } } }