diff --git a/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx b/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx index f772c16d39646..4cee84fe5e639 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx +++ b/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx @@ -312,6 +312,12 @@ Member '{0}' in type '{1}' cannot have DataMemberAttribute attribute Name set to null or empty string. + + '{0}' is an invalid data node when deserializing extension data. + + + Type '{0}' in namespace '{1}' is not a valid base type for enum '{2}' in namespace '{3}'. + '{0}' in type '{1}' cannot have EnumMemberAttribute attribute Value set to null or empty string. @@ -624,6 +630,9 @@ Unrecognized Byte Order Mark. + + The maximum nametable character count quota ({0}) has been exceeded while reading XML data. The nametable is a data structure used to store strings encountered during XML processing - long XML documents with non-repeating element names, attribute names and attribute values may trigger this quota. This quota may be increased by changing the MaxNameTableCharCount property on the XmlDictionaryReaderQuotas object used when creating the XML reader. + Base64 encoded data expected. Found {0}. @@ -786,6 +795,9 @@ The maximum array length quota ({0}) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. + + The maximum array length quota ({0}) or the maximum items in object graph quota has been exceeded while reading XML data. These quotas may be increased by changing the MaxArrayLength property on XmlDictionaryReaderQuotas or the MaxItemsInObjectGraph setting. + The 'maximum bytes per Read operation' quota ({0}) has been exceeded while reading XML data. Long element start tags (consisting of the element name, attribute names and attribute values) may trigger this quota. This quota may be increased by changing the MaxBytesPerRead property on the XmlDictionaryReaderQuotas object used when creating the XML reader. @@ -1059,6 +1071,9 @@ DataContract for type '{0}' cannot be added to DataContractSet since type '{1}' with the same data contract name '{2}' in namespace '{3}' is already present and the contracts are not equivalent. + + DataContract name '{0}' from namespace '{1}' does not match the generic name '{2}' from namespace '{3}'. + Type '{0}' cannot be exported as a schema type because it is an open generic type. You can only export a generic type if all its generic parameter types are actual types. @@ -1155,10 +1170,235 @@ '{0}' must be greater than 0. + + {0} The class cannot be deserialized. + Collection type '{0}' cannot be deserialized since it Unknown Type for null value - \ No newline at end of file + + List of referenced types contains more than one type with same data contract name. Need to exclude all but one of the following types. Only matching types can be valid references: {0} + + + List of referenced types contains more than one type with data contract name '{0}' in namespace '{1}'. Need to exclude all but one of the following types. Only matching types can be valid references: {2} + + + List of referenced collection types contains more than one type with same data contract name. Include only one of the following types. Only matching types can be valid references: {0} + + + List of referenced collection types contains more than one type with data contract name '{0}' in namespace '{1}'. Include only one of the following types. Only matching types can be valid references: {2} + + + ReferencedCollectionTypes specified via ImportOptions must contain valid types. Cannot contain null. + + + (matching) + + + (not matching) + + + ReferencedTypes specified via ImportOptions must contain valid types. Cannot contain null. + + + Using surrogates with get-only collection properties is not supported. Consider removing the surrogate associated with '{0}' or adding a setter to '{1}.{2}'. + + + The element cannot have 'abstract' set to 'true'. + + + The type cannot have 'abstract' set to 'true'. + + + Invalid '{0}' annotation in type '{1}' from namespace '{2}'. Attribute '{3}' not present. + + + Anonymous type in element '{0}' from namespace '{1}' is not supported. + + + 'anyAttribute' is not supported. + + + Form for element '{0}' must be qualified. + + + Array type '{0}' in namespace '{1}' cannot be imported. {2} + + + One of its base types, '{0}' from namespace '{1}' is not ISerializable. + + + A unique name cannot be computed for '{0}' because there are already Int32.MaxValue types of with the same name. + + + The type contains two attributes with the same name '{0}'. Multiple attributes with the same name in one type are not supported. + + + The type contains two elements with the same name '{0}'. Multiple elements with the same name in one type are not supported because members marked with DataMemberAttribute attribute must have unique names. + + + Cannot import invalid schemas. Compilation on the XmlSchemaSet failed. + + + Cannot import type for null XmlQualifiedName specified via parameter. + + + Cannot import null XmlSchema contained in XmlSchemaSet specified via parameter. + + + An internal error has occurred. Could not load serialization schema. Consider providing schema with namespace '{0}'. + + + Default value on element '{0}' is not supported. + + + It is not ISerializable but its base type '{0}' in namespace '{1}' is ISerializable. + + + 'maxOccurs' on element '{0}' must be 1. + + + 'minOccurs' on element '{0}' must be 0 or 1. + + + Ref to element '{0}' in '{1}' namespace is not supported. + + + Enumeration facets without 'value' are not supported. + + + Anonymous type with <list> cannot be used to create Flags enumeration because it is not a valid enum type. + + + Simple type list must contain an anonymous type specifying enumeration facets. + + + Facets other than enumeration facets are not supported. + + + Anonymous type with <restriction> cannot be used to create Flags enumeration because it is not a valid enum type. + + + Enum type '{0}' in namespace '{1}' cannot be imported. {2} + + + Anonymous type with <union>. cannot be used to create Flags enumeration because it is not a valid enum type. + + + Fixed value on element '{0}' is not supported. + + + Form on element '{0}' must be qualified. + + + Annotation for generic type '{0}' did not have attribute '{1}'. + + + Nested level on annotation elements '{0}' from namespace '{1}' for generic type '{2}' must be in increasing order. + + + Annotation element '{0}' from namespace '{1}' for generic type '{2}' has an invalid value '{3}' for attribute '{4}'. Expecting value to be of type '{5}'. + + + Annotation for generic type '{2}' has an invalid element '{0}' from namespace '{1}'. + + + Annotation '{0}' from namespace '{1}' has an invalid element '{2}' from namespace '{3}'. Expecting text. + + + Type '{0}' in namespace '{1}' cannot be used as the base type of a data contract type, because it itself does not have a data contract. Consider marking type '{0}' with the DataContractAttribute attribute. + + + Annotation for element {0} in type {1} from namespace {2} specifies EmitDefaultValue as 'true'. This requires the element to be either nillable or the element's type must be a value type. + + + Cannot import type '{0}' in namespace '{1}' as its base type because derived type is ISerializable but the base type is not ISerializable. + + + It is an invalid dictionary type. Element '{0}' must reference a complex type containing a sequence with two required elements. Either fix the schema or remove the IsDictionary annotation. + + + It is an invalid dictionary type since element '{0}' references a type from a different namespace '{1}'. Either fix the schema or remove the IsDictionary annotation. + + + '{0}' is an invalid value for IsDictionary annotation. {1} + + + '{0}' is an invalid value for IsValueType annotation. {1} + + + Its root sequence contains more than one particle. + + + Derived ISerializable types cannot contain any particles. + + + It does not contain root sequence with a wildcard element <any>. + + + It does not reference attribute '{0}' from namespace '{1}'. + + + ISerializable type '{0}' in namespace '{1}' cannot be imported. '{2}' + + + 'maxOccurs' on the wildcard element must be '{0}'. + + + 'minOccurs' on the wildcard element must be '{0}'. + + + Namespace on the wildcard element must be '{0}'. + + + ProcessContents on the wildcard element must be '{0}'. + + + Complex type with mixed content is not supported. + + + The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. + + + Redefine is not supported. + + + The root particle must be a sequence. + + + 'maxOccurs' on the root sequence must be 1. + + + 'minOccurs' on the root sequence must be 1. + + + Complex types with simple content extension are not supported. + + + Simple type restriction must specify a base type. + + + Simple types with <union> content are not supported. + + + Invalid type specified. Type with name '{0}' not found in schema with namespace '{1}'. + + + Substitution group on element '{0}' is not supported. + + + The global element found in the schema with same name references a different type '{0}' in namespace '{1}'. Data contract types must have the same name as their root element name. Consider removing the global element or changing its type. + + + Type '{0}' in namespace '{1}' cannot be imported. {2} + + + {0} Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer. + + + Attributes must be optional and from namespace '{0}'. + + diff --git a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj index f8df7d3599de5..f81bacc216dca 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj +++ b/src/libraries/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent) $(NoWarn);1634;1691;649 @@ -15,11 +15,11 @@ - - + + + + @@ -29,44 +29,56 @@ + + + + - + + + + + + + + + + + + + - + + + + - - - - - - - - - - + + + @@ -81,6 +93,7 @@ + @@ -89,6 +102,7 @@ + @@ -96,59 +110,42 @@ + + + + + + + + - + + + + + - - - - - - + + - - - - - - - - + + - - - - - - - - - - - - - - - - - - - @@ -168,6 +165,7 @@ + diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs index e6af588406a6d..23f1ea8aeeb70 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/AccessorBuilder.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs index 1200e4ee07097..a395b82d6f99f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs @@ -2,44 +2,35 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using System.Security; using System.Threading; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { internal sealed class ClassDataContract : DataContract { + internal const string ContractTypeString = nameof(ClassDataContract); + public override string? ContractType => ContractTypeString; + public XmlDictionaryString[]? ContractNamespaces; public XmlDictionaryString[]? MemberNames; - public XmlDictionaryString[]? MemberNamespaces; + internal XmlDictionaryString[]? MemberNamespaces; private XmlDictionaryString?[]? _childElementNamespaces; private ClassDataContractCriticalHelper _helper; - private bool _isScriptObject; - - internal const DynamicallyAccessedMemberTypes DataContractPreserveMemberTypes = - DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicConstructors | - DynamicallyAccessedMemberTypes.NonPublicConstructors | - DynamicallyAccessedMemberTypes.PublicFields | - DynamicallyAccessedMemberTypes.PublicProperties; - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal ClassDataContract(Type type) : base(new ClassDataContractCriticalHelper(type)) { @@ -56,23 +47,32 @@ private ClassDataContract(Type type, XmlDictionaryString ns, string[] memberName private void InitClassDataContract() { _helper = (base.Helper as ClassDataContractCriticalHelper)!; - this.ContractNamespaces = _helper.ContractNamespaces; - this.MemberNames = _helper.MemberNames; - this.MemberNamespaces = _helper.MemberNamespaces; - _isScriptObject = _helper.IsScriptObject; + ContractNamespaces = _helper.ContractNamespaces; + MemberNames = _helper.MemberNames; + MemberNamespaces = _helper.MemberNamespaces; + } + + public override DataContract? BaseContract + { + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => BaseClassContract; } - internal ClassDataContract? BaseContract + internal ClassDataContract? BaseClassContract { - get { return _helper.BaseContract; } + get => _helper.BaseClassContract; + set => _helper.BaseClassContract = value; } internal List? Members { - get { return _helper.Members; } + get => _helper.Members; + set => _helper.Members = value; } - public XmlDictionaryString?[]? ChildElementNamespaces + public override ReadOnlyCollection DataMembers => (Members == null) ? DataContract.s_emptyDataMemberList : Members.AsReadOnly(); + + internal XmlDictionaryString?[]? ChildElementNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get @@ -95,95 +95,39 @@ public XmlDictionaryString?[]? ChildElementNamespaces } return _childElementNamespaces; } - set - { - _childElementNamespaces = value; - } } - internal MethodInfo? OnSerializing - { - get - { return _helper.OnSerializing; } - } + internal MethodInfo? OnSerializing => _helper.OnSerializing; - internal MethodInfo? OnSerialized - { - get - { return _helper.OnSerialized; } - } + internal MethodInfo? OnSerialized => _helper.OnSerialized; - internal MethodInfo? OnDeserializing - { - get - { return _helper.OnDeserializing; } - } + internal MethodInfo? OnDeserializing => _helper.OnDeserializing; - internal MethodInfo? OnDeserialized - { - get - { return _helper.OnDeserialized; } - } + internal MethodInfo? OnDeserialized => _helper.OnDeserialized; - internal MethodInfo? ExtensionDataSetMethod - { - get { return _helper.ExtensionDataSetMethod; } - } + internal MethodInfo? ExtensionDataSetMethod => _helper.ExtensionDataSetMethod; public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } public override bool IsISerializable { - get { return _helper.IsISerializable; } - set { _helper.IsISerializable = value; } - } - - internal bool IsNonAttributedType - { - get - { return _helper.IsNonAttributedType; } - } - - public bool HasExtensionData - { - get - { return _helper.HasExtensionData; } - set { _helper.HasExtensionData = value; } + get => _helper.IsISerializable; + internal set => _helper.IsISerializable = value; } - [MemberNotNullWhen(true, nameof(KeyValuePairGenericArguments))] - [MemberNotNullWhen(true, nameof(KeyValuePairAdapterConstructorInfo))] - [MemberNotNullWhen(true, nameof(GetKeyValuePairMethodInfo))] - internal bool IsKeyValuePairAdapter - { - get -#pragma warning disable CS8775 // Member must have a non-null value when exiting in some condition. - { return _helper.IsKeyValuePairAdapter; } -#pragma warning restore CS8775 // Member must have a non-null value when exiting in some condition. - } + internal bool IsNonAttributedType => _helper.IsNonAttributedType; - internal Type[]? KeyValuePairGenericArguments - { - get - { return _helper.KeyValuePairGenericArguments; } - } + internal bool HasExtensionData => _helper.HasExtensionData; - internal ConstructorInfo? KeyValuePairAdapterConstructorInfo - { - get - { return _helper.KeyValuePairAdapterConstructorInfo; } - } + internal string? SerializationExceptionMessage => _helper.SerializationExceptionMessage; + internal string? DeserializationExceptionMessage => _helper.DeserializationExceptionMessage; - internal MethodInfo? GetKeyValuePairMethodInfo - { - get - { return _helper.GetKeyValuePairMethodInfo; } - } + internal bool IsReadOnlyContract => DeserializationExceptionMessage != null; internal ConstructorInfo? GetISerializableConstructor() { @@ -209,7 +153,7 @@ internal MethodInfo? GetKeyValuePairMethodInfo internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out object? obj) { ConstructorInfo? ci = GetNonAttributedTypeConstructor(); - if (ci == null) + if (ci == null || UnderlyingType == Globals.TypeOfSchemaDefinedType) { obj = null; return false; @@ -231,6 +175,7 @@ internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out obj [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private XmlFormatClassWriterDelegate CreateXmlFormatWriterDelegate() { + Debug.Assert(UnderlyingType != Globals.TypeOfSchemaDefinedType); return new XmlFormatWriterGenerator().GenerateClassWriter(this); } @@ -253,14 +198,12 @@ internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate } return _helper.XmlFormatWriterDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private XmlFormatClassReaderDelegate CreateXmlFormatReaderDelegate() { + Debug.Assert(UnderlyingType != Globals.TypeOfSchemaDefinedType); return new XmlFormatReaderGenerator().GenerateClassReader(this); } @@ -275,6 +218,10 @@ internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate { if (_helper.XmlFormatReaderDelegate == null) { + if (IsReadOnlyContract) + { + ThrowInvalidDataContractException(DeserializationExceptionMessage, type: null); + } XmlFormatClassReaderDelegate tempDelegate = CreateXmlFormatReaderDelegate(); Interlocked.MemoryBarrier(); _helper.XmlFormatReaderDelegate = tempDelegate; @@ -283,31 +230,17 @@ internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate } return _helper.XmlFormatReaderDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal static ClassDataContract CreateClassDataContractForKeyValue(Type type, XmlDictionaryString ns, string[] memberNames) { - ClassDataContract? cdc = (ClassDataContract?)DataContract.GetDataContractFromGeneratedAssembly(type); - if (cdc == null) - { - return new ClassDataContract(type, ns, memberNames); - } - else - { - ClassDataContract cloned = cdc.Clone(); - cloned.UpdateNamespaceAndMembers(type, ns, memberNames); - return cloned; - } + return new ClassDataContract(type, ns, memberNames); } internal static void CheckAndAddMember(List members, DataMember memberContract, Dictionary memberNamesTable) { - DataMember? existingMemberContract; - if (memberNamesTable.TryGetValue(memberContract.Name, out existingMemberContract)) + if (memberNamesTable.TryGetValue(memberContract.Name, out DataMember? existingMemberContract)) { Type declaringType = memberContract.MemberInfo.DeclaringType!; DataContract.ThrowInvalidDataContractException( @@ -329,7 +262,7 @@ internal static void CheckAndAddMember(List members, DataMember memb if (!childType.IsEnum && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(childType) && DataContract.GetBuiltInDataContract(childType) == null && childType != Globals.TypeOfDBNull) { - string ns = DataContract.GetStableName(childType).Namespace; + string ns = DataContract.GetXmlName(childType).Namespace; if (ns.Length > 0 && ns != dataContract.Namespace.Value) return dictionary.Add(ns); } @@ -370,11 +303,9 @@ internal static bool IsNonAttributedTypeValidForSerialization( if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) return false; - Type[] interfaceTypes = type.GetInterfaces(); - if (!IsArraySegment(type)) { - foreach (Type interfaceType in interfaceTypes) + foreach (Type interfaceType in type.GetInterfaces()) { if (CollectionDataContract.IsCollectionInterface(interfaceType)) return false; @@ -389,60 +320,15 @@ internal static bool IsNonAttributedTypeValidForSerialization( if (type.IsDefined(Globals.TypeOfDataContractAttribute, false)) return false; - if (type.IsValueType) - { - return type.IsVisible; - } - else - { - return (type.IsVisible && - type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.EmptyTypes) != null); - } - } - private static readonly Dictionary s_knownSerializableTypeInfos = new Dictionary { - { "System.Collections.Generic.KeyValuePair`2", Array.Empty() }, - { "System.Collections.Generic.Queue`1", new[] { "_syncRoot" } }, - { "System.Collections.Generic.Stack`1", new[] {"_syncRoot" } }, - { "System.Collections.ObjectModel.ReadOnlyCollection`1", new[] {"_syncRoot" } }, - { "System.Collections.ObjectModel.ReadOnlyDictionary`2", new[] {"_syncRoot", "_keys", "_values" } }, - { "System.Tuple`1", Array.Empty() }, - { "System.Tuple`2", Array.Empty() }, - { "System.Tuple`3", Array.Empty() }, - { "System.Tuple`4", Array.Empty() }, - { "System.Tuple`5", Array.Empty() }, - { "System.Tuple`6", Array.Empty() }, - { "System.Tuple`7", Array.Empty() }, - { "System.Tuple`8", Array.Empty() }, - { "System.Collections.Queue", new[] {"_syncRoot" } }, - { "System.Collections.Stack", new[] {"_syncRoot" } }, - { "System.Globalization.CultureInfo", Array.Empty() }, - { "System.Version", Array.Empty() }, - }; - - private static string GetGeneralTypeName(Type type) - { - return type.IsGenericType && !type.IsGenericParameter - ? type.GetGenericTypeDefinition().FullName! - : type.FullName!; - } - - internal static bool IsKnownSerializableType(Type type) - { - // Applies to known types that DCS understands how to serialize/deserialize - // - string typeFullName = GetGeneralTypeName(type); - - return s_knownSerializableTypeInfos.ContainsKey(typeFullName); - } + if (type == Globals.TypeOfExtensionDataObject) + return false; - internal static bool IsNonSerializedMember(Type type, string memberName) - { - string typeFullName = GetGeneralTypeName(type); + if (type.IsValueType) + return type.IsVisible; - string[]? members; - return s_knownSerializableTypeInfos.TryGetValue(typeFullName, out members) - && members.Contains(memberName); + return (type.IsVisible && + type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.EmptyTypes) != null); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -452,17 +338,17 @@ internal static bool IsNonSerializedMember(Type type, string memberName) return null; XmlDictionaryString?[]? baseChildElementNamespaces = null; - if (this.BaseContract != null) - baseChildElementNamespaces = this.BaseContract.ChildElementNamespaces; + if (BaseClassContract != null) + baseChildElementNamespaces = BaseClassContract.ChildElementNamespaces; int baseChildElementNamespaceCount = (baseChildElementNamespaces != null) ? baseChildElementNamespaces.Length : 0; XmlDictionaryString?[] childElementNamespaces = new XmlDictionaryString?[Members.Count + baseChildElementNamespaceCount]; if (baseChildElementNamespaceCount > 0) Array.Copy(baseChildElementNamespaces!, childElementNamespaces, baseChildElementNamespaces!.Length); XmlDictionary dictionary = new XmlDictionary(); - for (int i = 0; i < this.Members.Count; i++) + for (int i = 0; i < Members.Count; i++) { - childElementNamespaces[i + baseChildElementNamespaceCount] = GetChildNamespaceToDeclare(this, this.Members[i].MemberType, dictionary); + childElementNamespaces[i + baseChildElementNamespaceCount] = GetChildNamespaceToDeclare(this, Members[i].MemberType, dictionary); } return childElementNamespaces; @@ -474,24 +360,15 @@ private void EnsureMethodsImported() } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); - - if (_isScriptObject) - { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); - } XmlFormatWriterDelegate(xmlWriter, obj, context, this); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { - if (_isScriptObject) - { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); - } xmlReader.Read(); object? o = XmlFormatReaderDelegate(xmlReader, context, MemberNames, MemberNamespaces); xmlReader.ReadEndElement(); @@ -518,15 +395,24 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) } return true; } - if (this.BaseContract != null && this.BaseContract.RequiresMemberAccessForRead(securityException)) + if (BaseClassContract != null && BaseClassContract.RequiresMemberAccessForRead(securityException)) return true; - if (ConstructorRequiresMemberAccess(GetNonAttributedTypeConstructor())) + if (ConstructorRequiresMemberAccess(GetISerializableConstructor())) { - if (Globals.TypeOfScriptObject_IsAssignableFrom(UnderlyingType)) + if (securityException != null) { - return true; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new SecurityException(SR.Format( + SR.PartialTrustIXmlSerialzableNoPublicConstructor, + DataContract.GetClrTypeFullName(UnderlyingType)), + securityException)); } + return true; + } + + if (ConstructorRequiresMemberAccess(GetNonAttributedTypeConstructor())) + { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( @@ -538,7 +424,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) return true; } - if (MethodRequiresMemberAccess(this.OnDeserializing)) + if (MethodRequiresMemberAccess(OnDeserializing)) { if (securityException != null) { @@ -546,13 +432,13 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractOnDeserializingNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.OnDeserializing!.Name), + OnDeserializing!.Name), securityException)); } return true; } - if (MethodRequiresMemberAccess(this.OnDeserialized)) + if (MethodRequiresMemberAccess(OnDeserialized)) { if (securityException != null) { @@ -560,27 +446,27 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractOnDeserializedNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.OnDeserialized!.Name), + OnDeserialized!.Name), securityException)); } return true; } - if (this.Members != null) + if (Members != null) { - for (int i = 0; i < this.Members.Count; i++) + for (int i = 0; i < Members.Count; i++) { - if (this.Members[i].RequiresMemberAccessForSet()) + if (Members[i].RequiresMemberAccessForSet()) { if (securityException != null) { - if (this.Members[i].MemberInfo is FieldInfo) + if (Members[i].MemberInfo is FieldInfo) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.Format( SR.PartialTrustDataContractFieldSetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } else @@ -589,7 +475,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractPropertySetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } } @@ -623,24 +509,24 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) return true; } - if (this.BaseContract != null && this.BaseContract.RequiresMemberAccessForWrite(securityException)) + if (BaseClassContract != null && BaseClassContract.RequiresMemberAccessForWrite(securityException)) return true; - if (MethodRequiresMemberAccess(this.OnSerializing)) + if (MethodRequiresMemberAccess(OnSerializing)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.Format( SR.PartialTrustDataContractOnSerializingNotPublic, - DataContract.GetClrTypeFullName(this.UnderlyingType), - this.OnSerializing!.Name), + DataContract.GetClrTypeFullName(UnderlyingType), + OnSerializing!.Name), securityException)); } return true; } - if (MethodRequiresMemberAccess(this.OnSerialized)) + if (MethodRequiresMemberAccess(OnSerialized)) { if (securityException != null) { @@ -648,27 +534,27 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractOnSerializedNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.OnSerialized!.Name), + OnSerialized!.Name), securityException)); } return true; } - if (this.Members != null) + if (Members != null) { - for (int i = 0; i < this.Members.Count; i++) + for (int i = 0; i < Members.Count; i++) { - if (this.Members[i].RequiresMemberAccessForGet()) + if (Members[i].RequiresMemberAccessForGet()) { if (securityException != null) { - if (this.Members[i].MemberInfo is FieldInfo) + if (Members[i].MemberInfo is FieldInfo) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.Format( SR.PartialTrustDataContractFieldGetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } else @@ -677,7 +563,7 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustDataContractPropertyGetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.Members[i].MemberInfo.Name), + Members[i].MemberInfo.Name), securityException)); } } @@ -692,8 +578,6 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) private sealed class ClassDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static Type[]? s_serInfoCtorArgs; - private static readonly MethodInfo s_getKeyValuePairMethod = typeof(KeyValuePairAdapter<,>).GetMethod("GetKeyValuePair", Globals.ScanAllMembers)!; - private static readonly ConstructorInfo s_ctorGenericMethod = typeof(KeyValuePairAdapter<,>).GetConstructor(Globals.ScanAllMembers, new Type[] { typeof(KeyValuePair<,>).MakeGenericType(typeof(KeyValuePairAdapter<,>).GetGenericArguments()) })!; private ClassDataContract? _baseContract; private List? _members; @@ -701,82 +585,65 @@ private sealed class ClassDataContractCriticalHelper : DataContract.DataContract private MethodInfo? _onDeserializing, _onDeserialized; private MethodInfo? _extensionDataSetMethod; private DataContractDictionary? _knownDataContracts; - private bool _isISerializable; + private string? _serializationExceptionMessage; private bool _isKnownTypeAttributeChecked; private bool _isMethodChecked; + /// - /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and hasDataContract + /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and isNonAttributedType /// private bool _isNonAttributedType; /// - /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and isNonAttributedType + /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and hasDataContract /// private bool _hasDataContract; private bool _hasExtensionData; - private readonly bool _isScriptObject; - - private XmlDictionaryString?[]? _childElementNamespaces; - private XmlFormatClassReaderDelegate? _xmlFormatReaderDelegate; - private XmlFormatClassWriterDelegate? _xmlFormatWriterDelegate; - public XmlDictionaryString[]? ContractNamespaces; - public XmlDictionaryString[]? MemberNames; - public XmlDictionaryString[]? MemberNamespaces; + internal XmlDictionaryString[]? ContractNamespaces; + internal XmlDictionaryString[]? MemberNames; + internal XmlDictionaryString[]? MemberNamespaces; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type) : base(type) { - XmlQualifiedName stableName = GetStableNameAndSetHasDataContract(type); + XmlQualifiedName xmlName = GetXmlNameAndSetHasDataContract(type); if (type == Globals.TypeOfDBNull) { - this.StableName = stableName; + XmlName = xmlName; _members = new List(); XmlDictionary dictionary = new XmlDictionary(2); - this.Name = dictionary.Add(StableName.Name); - this.Namespace = dictionary.Add(StableName.Namespace); - this.ContractNamespaces = this.MemberNames = this.MemberNamespaces = Array.Empty(); + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); + ContractNamespaces = MemberNames = MemberNamespaces = Array.Empty(); EnsureMethodsImported(); return; } Type? baseType = type.BaseType; - _isISerializable = (Globals.TypeOfISerializable.IsAssignableFrom(type)); + IsISerializable = (Globals.TypeOfISerializable.IsAssignableFrom(type)); SetIsNonAttributedType(type); - if (_isISerializable) + if (IsISerializable) { if (HasDataContract) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.ISerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type)))); if (baseType != null && !(baseType.IsSerializable && Globals.TypeOfISerializable.IsAssignableFrom(baseType))) baseType = null; } - SetKeyValuePairAdapterFlags(type); - this.IsValueType = type.IsValueType; + IsValueType = type.IsValueType; if (baseType != null && baseType != Globals.TypeOfObject && baseType != Globals.TypeOfValueType && baseType != Globals.TypeOfUri) { - // https://github.com/dotnet/corefx/pull/19629: - // A DataContract created at runtime does not work with the base DataContract pre-generated by SG. - // It's because the runtime DataContract requires full information of a base DataContract while a - // pre-generated DataContract is incomplete. - // - // At this point in code, we're in the midlle of creating a new DataContract at runtime, so we need to make - // sure that we create our own base type DataContract when the base type could potentially have SG generated - // DataContract. - // - // We wanted to enable the fix for the issue described above only when SG generated DataContracts are available. - // Currently we don't have a good way of detecting usage of SG (either globally or per data contract). - DataContract baseContract = DataContract.GetDataContract(baseType); - if (baseContract is CollectionDataContract) + if (baseContract is CollectionDataContract collectionDC) { - this.BaseContract = ((CollectionDataContract)baseContract).SharedTypeContract as ClassDataContract; + BaseClassContract = collectionDC.SharedTypeContract as ClassDataContract; } else { - this.BaseContract = baseContract as ClassDataContract; + BaseClassContract = baseContract as ClassDataContract; } - if (this.BaseContract != null && this.BaseContract.IsNonAttributedType && !_isNonAttributedType) + if (BaseClassContract != null && BaseClassContract.IsNonAttributedType && !_isNonAttributedType) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError (new InvalidDataContractException(SR.Format(SR.AttributedTypesCannotInheritFromNonAttributedSerializableTypes, @@ -785,7 +652,7 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } else { - this.BaseContract = null; + BaseClassContract = null; } _hasExtensionData = (Globals.TypeOfIExtensibleDataObject.IsAssignableFrom(type)); @@ -794,21 +661,21 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.OnlyDataContractTypesCanHaveExtensionData, DataContract.GetClrTypeFullName(type)))); } - if (_isISerializable) + if (IsISerializable) { - SetDataContractName(stableName); + SetDataContractName(xmlName); } else { - this.StableName = stableName; + XmlName = xmlName; ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(2 + Members.Count); - Name = dictionary.Add(StableName.Name); - Namespace = dictionary.Add(StableName.Namespace); + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); int baseMemberCount = 0; int baseContractCount = 0; - if (BaseContract == null) + if (BaseClassContract == null) { MemberNames = new XmlDictionaryString[Members.Count]; MemberNamespaces = new XmlDictionaryString[Members.Count]; @@ -816,14 +683,18 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } else { - baseMemberCount = BaseContract.MemberNames!.Length; + if (BaseClassContract.IsReadOnlyContract) + { + _serializationExceptionMessage = BaseClassContract.SerializationExceptionMessage; + } + baseMemberCount = BaseClassContract.MemberNames!.Length; MemberNames = new XmlDictionaryString[Members.Count + baseMemberCount]; - Array.Copy(BaseContract.MemberNames, MemberNames, baseMemberCount); + Array.Copy(BaseClassContract.MemberNames, MemberNames, baseMemberCount); MemberNamespaces = new XmlDictionaryString[Members.Count + baseMemberCount]; - Array.Copy(BaseContract.MemberNamespaces!, MemberNamespaces, baseMemberCount); - baseContractCount = BaseContract.ContractNamespaces!.Length; + Array.Copy(BaseClassContract.MemberNamespaces!, MemberNamespaces, baseMemberCount); + baseContractCount = BaseClassContract.ContractNamespaces!.Length; ContractNamespaces = new XmlDictionaryString[1 + baseContractCount]; - Array.Copy(BaseContract.ContractNamespaces, ContractNamespaces, baseContractCount); + Array.Copy(BaseClassContract.ContractNamespaces, ContractNamespaces, baseContractCount); } ContractNamespaces[baseContractCount] = Namespace; for (int i = 0; i < Members.Count; i++) @@ -834,8 +705,6 @@ internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContrac } EnsureMethodsImported(); - _isScriptObject = this.IsNonAttributedType && - Globals.TypeOfScriptObject_IsAssignableFrom(this.UnderlyingType); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -843,10 +712,10 @@ internal ClassDataContractCriticalHelper( [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] Type type, XmlDictionaryString ns, string[] memberNames) : base(type) { - this.StableName = new XmlQualifiedName(GetStableNameAndSetHasDataContract(type).Name, ns.Value); + XmlName = new XmlQualifiedName(GetXmlNameAndSetHasDataContract(type).Name, ns.Value); ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(1 + Members.Count); - Name = dictionary.Add(StableName.Name); + Name = dictionary.Add(XmlName.Name); Namespace = ns; ContractNamespaces = new XmlDictionaryString[] { Namespace }; MemberNames = new XmlDictionaryString[Members.Count]; @@ -862,15 +731,14 @@ internal ClassDataContractCriticalHelper( private void EnsureIsReferenceImported(Type type) { - DataContractAttribute? dataContractAttribute; bool isReference = false; - bool hasDataContractAttribute = TryGetDCAttribute(type, out dataContractAttribute); + bool hasDataContractAttribute = TryGetDCAttribute(type, out DataContractAttribute? dataContractAttribute); - if (BaseContract != null) + if (BaseClassContract != null) { if (hasDataContractAttribute && dataContractAttribute!.IsReferenceSetExplicitly) { - bool baseIsReference = this.BaseContract.IsReference; + bool baseIsReference = BaseClassContract.IsReference; if ((baseIsReference && !dataContractAttribute.IsReference) || (!baseIsReference && dataContractAttribute.IsReference)) { @@ -878,8 +746,8 @@ private void EnsureIsReferenceImported(Type type) SR.Format(SR.InconsistentIsReference, DataContract.GetClrTypeFullName(type), dataContractAttribute.IsReference, - DataContract.GetClrTypeFullName(this.BaseContract.UnderlyingType), - this.BaseContract.IsReference), + DataContract.GetClrTypeFullName(BaseClassContract.UnderlyingType), + BaseClassContract.IsReference), type); } else @@ -889,7 +757,7 @@ private void EnsureIsReferenceImported(Type type) } else { - isReference = this.BaseContract.IsReference; + isReference = BaseClassContract.IsReference; } } else if (hasDataContractAttribute) @@ -909,7 +777,7 @@ private void EnsureIsReferenceImported(Type type) return; } - this.IsReference = isReference; + IsReference = isReference; } [MemberNotNull(nameof(_members))] @@ -917,16 +785,14 @@ private void EnsureIsReferenceImported(Type type) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void ImportDataMembers() { - Type type = this.UnderlyingType; + Type type = UnderlyingType; EnsureIsReferenceImported(type); List tempMembers = new List(); Dictionary memberNamesTable = new Dictionary(); MemberInfo[] memberInfos; - bool isPodSerializable = !_isNonAttributedType || IsKnownSerializableType(type); - - if (!isPodSerializable) + if (_isNonAttributedType) { memberInfos = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); } @@ -960,9 +826,9 @@ private void ImportDataMembers() ThrowInvalidDataContractException(SR.Format(SR.NoGetMethodForProperty, property.DeclaringType, property.Name)); if (setMethod == null) { - if (!SetIfGetOnlyCollection(memberContract)) + if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: false)) { - ThrowInvalidDataContractException(SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property.Name)); + _serializationExceptionMessage = SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property.Name); } } if (getMethod.GetParameters().Length > 0) @@ -984,7 +850,7 @@ private void ImportDataMembers() memberContract.Name = DataContract.EncodeLocalName(memberContract.Name); memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType); memberContract.IsRequired = memberAttribute.IsRequired; - if (memberAttribute.IsRequired && this.IsReference) + if (memberAttribute.IsRequired && IsReference) { ThrowInvalidDataContractException( SR.Format(SR.IsRequiredDataMemberOnIsReferenceDataContractType, @@ -996,7 +862,7 @@ private void ImportDataMembers() CheckAndAddMember(tempMembers, memberContract, memberNamesTable); } } - else if (!isPodSerializable) + else if (_isNonAttributedType) { FieldInfo? field = member as FieldInfo; PropertyInfo? property = member as PropertyInfo; @@ -1021,7 +887,7 @@ private void ImportDataMembers() MethodInfo? setMethod = property.SetMethod; if (setMethod == null) { - if (!SetIfGetOnlyCollection(memberContract)) + if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: true)) continue; } else @@ -1029,6 +895,11 @@ private void ImportDataMembers() if (!setMethod.IsPublic || IsMethodOverriding(setMethod)) continue; } + + //skip ExtensionData member of type ExtensionDataObject if IExtensibleDataObject is implemented in non-attributed type + if (_hasExtensionData && memberContract.MemberType == Globals.TypeOfExtensionDataObject + && member.Name == Globals.ExtensionDataObjectPropertyName) + continue; } memberContract.Name = DataContract.EncodeLocalName(member.Name); @@ -1039,23 +910,7 @@ private void ImportDataMembers() { FieldInfo? field = member as FieldInfo; - bool canSerializeMember; - - // Previously System.SerializableAttribute was not available in NetCore, so we had - // a list of known [Serializable] types for type in the framework. Although now SerializableAttribute - // is available in NetCore, some framework types still do not have [Serializable] - // yet, e.g. ReadOnlyDictionary. So, we still need to maintain the known serializable - // type list. - if (IsKnownSerializableType(type)) - { - canSerializeMember = CanSerializeMember(field); - } - else - { - canSerializeMember = field != null && !field.IsNotSerialized; - } - - if (canSerializeMember) + if (field != null && !field.IsNotSerialized) { DataMember memberContract = new DataMember(member); @@ -1063,7 +918,7 @@ private void ImportDataMembers() object[] optionalFields = field!.GetCustomAttributes(Globals.TypeOfOptionalFieldAttribute, false); if (optionalFields == null || optionalFields.Length == 0) { - if (this.IsReference) + if (IsReference) { ThrowInvalidDataContractException( SR.Format(SR.NonOptionalFieldMemberOnIsReferenceSerializableType, @@ -1087,16 +942,11 @@ private void ImportDataMembers() Debug.Assert(Members != null); } - private static bool CanSerializeMember(FieldInfo? field) - { - return field != null && !ClassDataContract.IsNonSerializedMember(field.DeclaringType!, field.Name); - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool SetIfGetOnlyCollection(DataMember memberContract) + private static bool SetIfGetOnlyCollection(DataMember memberContract, bool skipIfReadOnlyContract) { //OK to call IsCollection here since the use of surrogated collection types is not supported in get-only scenarios - if (CollectionDataContract.IsCollection(memberContract.MemberType, false /*isConstructorRequired*/) && !memberContract.MemberType.IsValueType) + if (CollectionDataContract.IsCollection(memberContract.MemberType, false /*isConstructorRequired*/, skipIfReadOnlyContract) && !memberContract.MemberType.IsValueType) { memberContract.IsGetOnlyCollection = true; return true; @@ -1106,25 +956,25 @@ private static bool SetIfGetOnlyCollection(DataMember memberContract) private void SetIfMembersHaveConflict(List members) { - if (BaseContract == null) + if (BaseClassContract == null) return; int baseTypeIndex = 0; List membersInHierarchy = new List(); foreach (DataMember member in members) { - membersInHierarchy.Add(new Member(member, this.StableName!.Namespace, baseTypeIndex)); + membersInHierarchy.Add(new Member(member, XmlName!.Namespace, baseTypeIndex)); } - ClassDataContract? currContract = BaseContract; + ClassDataContract? currContract = BaseClassContract; while (currContract != null) { baseTypeIndex++; foreach (DataMember member in currContract.Members!) { - membersInHierarchy.Add(new Member(member, currContract.StableName!.Namespace, baseTypeIndex)); + membersInHierarchy.Add(new Member(member, currContract.XmlName!.Namespace, baseTypeIndex)); } - currContract = currContract.BaseContract; + currContract = currContract.BaseClassContract; } IComparer comparer = DataMemberConflictComparer.Singleton; @@ -1136,19 +986,19 @@ private void SetIfMembersHaveConflict(List members) int endIndex = i; bool hasConflictingType = false; while (endIndex < membersInHierarchy.Count - 1 - && string.CompareOrdinal(membersInHierarchy[endIndex].member.Name, membersInHierarchy[endIndex + 1].member.Name) == 0 - && string.CompareOrdinal(membersInHierarchy[endIndex].ns, membersInHierarchy[endIndex + 1].ns) == 0) + && string.CompareOrdinal(membersInHierarchy[endIndex]._member.Name, membersInHierarchy[endIndex + 1]._member.Name) == 0 + && string.CompareOrdinal(membersInHierarchy[endIndex]._ns, membersInHierarchy[endIndex + 1]._ns) == 0) { - membersInHierarchy[endIndex].member.ConflictingMember = membersInHierarchy[endIndex + 1].member; + membersInHierarchy[endIndex]._member.ConflictingMember = membersInHierarchy[endIndex + 1]._member; if (!hasConflictingType) { - if (membersInHierarchy[endIndex + 1].member.HasConflictingNameAndType) + if (membersInHierarchy[endIndex + 1]._member.HasConflictingNameAndType) { hasConflictingType = true; } else { - hasConflictingType = (membersInHierarchy[endIndex].member.MemberType != membersInHierarchy[endIndex + 1].member.MemberType); + hasConflictingType = (membersInHierarchy[endIndex]._member.MemberType != membersInHierarchy[endIndex + 1]._member.MemberType); } } endIndex++; @@ -1158,7 +1008,7 @@ private void SetIfMembersHaveConflict(List members) { for (int j = startIndex; j <= endIndex; j++) { - membersInHierarchy[j].member.HasConflictingNameAndType = true; + membersInHierarchy[j]._member.HasConflictingNameAndType = true; } } @@ -1167,15 +1017,15 @@ private void SetIfMembersHaveConflict(List members) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private XmlQualifiedName GetStableNameAndSetHasDataContract(Type type) + private XmlQualifiedName GetXmlNameAndSetHasDataContract(Type type) { - return DataContract.GetStableName(type, out _hasDataContract); + return DataContract.GetXmlName(type, out _hasDataContract); } /// /// RequiresReview - marked SRR because callers may need to depend on isNonAttributedType for a security decision /// isNonAttributedType must be calculated correctly - /// SetIsNonAttributedType should not be called before GetStableNameAndSetHasDataContract since it + /// SetIsNonAttributedType should not be called before GetXmlNameAndSetHasDataContract since it /// is dependent on the correct calculation of hasDataContract /// Safe - does not let caller influence isNonAttributedType calculation; no harm in leaking value /// @@ -1199,7 +1049,7 @@ internal void EnsureMethodsImported() { if (!_isMethodChecked) { - Type type = this.UnderlyingType; + Type type = UnderlyingType; MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < methods.Length; i++) { @@ -1272,20 +1122,21 @@ private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameter return false; } - internal ClassDataContract? BaseContract + internal ClassDataContract? BaseClassContract { - get { return _baseContract; } + get => _baseContract; set { _baseContract = value; if (_baseContract != null && IsValueType) - ThrowInvalidDataContractException(SR.Format(SR.ValueTypeCannotHaveBaseType, StableName!.Name, StableName.Namespace, _baseContract.StableName!.Name, _baseContract.StableName.Namespace)); + ThrowInvalidDataContractException(SR.Format(SR.ValueTypeCannotHaveBaseType, XmlName!.Name, XmlName.Namespace, _baseContract.XmlName!.Name, _baseContract.XmlName.Namespace)); } } internal List? Members { - get { return _members; } + get => _members; + set => _members = value; } internal MethodInfo? OnSerializing @@ -1335,99 +1186,39 @@ internal MethodInfo? ExtensionDataSetMethod internal override DataContractDictionary? KnownDataContracts { - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { - if (_knownDataContracts != null) - { - return _knownDataContracts; - } - if (!_isKnownTypeAttributeChecked && UnderlyingType != null) { lock (this) { if (!_isKnownTypeAttributeChecked) { - _knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType); + _knownDataContracts = DataContract.ImportKnownTypeAttributes(UnderlyingType); Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } + _knownDataContracts ??= new DataContractDictionary(); } } return _knownDataContracts; } - set - { _knownDataContracts = value; } - } - - internal override bool IsISerializable - { - get { return _isISerializable; } - set { _isISerializable = value; } - } - - internal bool HasDataContract - { - get { return _hasDataContract; } - } - - internal bool HasExtensionData - { - get { return _hasExtensionData; } - set { _hasExtensionData = value; } - } - - internal bool IsNonAttributedType - { - get { return _isNonAttributedType; } + set { _knownDataContracts = value; } } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private void SetKeyValuePairAdapterFlags( - [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)] - Type type) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePairAdapter) - { - _isKeyValuePairAdapter = true; - _keyValuePairGenericArguments = type.GetGenericArguments(); - _keyValuePairCtorInfo = (ConstructorInfo)type.GetMemberWithSameMetadataDefinitionAs(s_ctorGenericMethod); - _getKeyValuePairMethodInfo = (MethodInfo)type.GetMemberWithSameMetadataDefinitionAs(s_getKeyValuePairMethod); - } - } + internal string? SerializationExceptionMessage => _serializationExceptionMessage; - private bool _isKeyValuePairAdapter; - private Type[]? _keyValuePairGenericArguments; - private ConstructorInfo? _keyValuePairCtorInfo; - private MethodInfo? _getKeyValuePairMethodInfo; + internal string? DeserializationExceptionMessage => (_serializationExceptionMessage == null) ? null : SR.Format(SR.ReadOnlyClassDeserialization, _serializationExceptionMessage); - internal bool IsKeyValuePairAdapter - { - get { return _isKeyValuePairAdapter; } - } + internal override bool IsISerializable { get; set; } - internal bool IsScriptObject - { - get { return _isScriptObject; } - } - - internal Type[]? KeyValuePairGenericArguments - { - get { return _keyValuePairGenericArguments; } - } + internal bool HasDataContract => _hasDataContract; - internal ConstructorInfo? KeyValuePairAdapterConstructorInfo - { - get { return _keyValuePairCtorInfo; } - } + internal bool HasExtensionData => _hasExtensionData; - internal MethodInfo? GetKeyValuePairMethodInfo - { - get { return _getKeyValuePairMethodInfo; } - } + internal bool IsNonAttributedType => _isNonAttributedType; internal ConstructorInfo? GetISerializableConstructor() { @@ -1443,7 +1234,7 @@ internal MethodInfo? GetKeyValuePairMethodInfo internal ConstructorInfo? GetNonAttributedTypeConstructor() { - if (!this.IsNonAttributedType) + if (!IsNonAttributedType) return null; Type type = UnderlyingType; @@ -1458,23 +1249,11 @@ internal MethodInfo? GetKeyValuePairMethodInfo return ctor; } - internal XmlFormatClassWriterDelegate? XmlFormatWriterDelegate - { - get { return _xmlFormatWriterDelegate; } - set { _xmlFormatWriterDelegate = value; } - } + internal XmlFormatClassWriterDelegate? XmlFormatWriterDelegate { get; set; } - internal XmlFormatClassReaderDelegate? XmlFormatReaderDelegate - { - get { return _xmlFormatReaderDelegate; } - set { _xmlFormatReaderDelegate = value; } - } + internal XmlFormatClassReaderDelegate? XmlFormatReaderDelegate { get; set; } - public XmlDictionaryString?[]? ChildElementNamespaces - { - get { return _childElementNamespaces; } - set { _childElementNamespaces = value; } - } + internal XmlDictionaryString?[]? ChildElementNamespaces { get; set; } private static Type[] SerInfoCtorArgs => s_serInfoCtorArgs ??= new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }; @@ -1482,70 +1261,207 @@ internal struct Member { internal Member(DataMember member, string ns, int baseTypeIndex) { - this.member = member; - this.ns = ns; - this.baseTypeIndex = baseTypeIndex; + _member = member; + _ns = ns; + _baseTypeIndex = baseTypeIndex; } - internal DataMember member; - internal string ns; - internal int baseTypeIndex; + internal DataMember _member; + internal string _ns; + internal int _baseTypeIndex; } internal sealed class DataMemberConflictComparer : IComparer { public int Compare(Member x, Member y) { - int nsCompare = string.CompareOrdinal(x.ns, y.ns); + int nsCompare = string.CompareOrdinal(x._ns, y._ns); if (nsCompare != 0) return nsCompare; - int nameCompare = string.CompareOrdinal(x.member.Name, y.member.Name); + int nameCompare = string.CompareOrdinal(x._member.Name, y._member.Name); if (nameCompare != 0) return nameCompare; - return x.baseTypeIndex - y.baseTypeIndex; + return x._baseTypeIndex - y._baseTypeIndex; } internal static DataMemberConflictComparer Singleton = new DataMemberConflictComparer(); } + } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal ClassDataContractCriticalHelper Clone() + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) + { + Type type = UnderlyingType; + if (!type.IsGenericType || !type.ContainsGenericParameters) + return this; + + lock (this) + { + if (boundContracts != null && boundContracts.TryGetValue(this, out DataContract? boundContract)) + return boundContract; + + XmlQualifiedName xmlName; + object[] genericParams; + Type boundType; + if (type.IsGenericTypeDefinition) + { + xmlName = XmlName; + genericParams = paramContracts; + + // This type-binding ('boundType') stuff is new. We did not do this in NetFx. We used to use default contract constructors and let the + // underlying type get filled in later. But default constructors for DataContracts runs afoul of requiring an underlying type. Our web of nullable + // notations make it hard to get around. But it also allows us to feel good about using .UnderlyingType from matching parameter contracts. + Type[] underlyingParamTypes = new Type[paramContracts.Length]; + for (int i = 0; i < paramContracts.Length; i++) + underlyingParamTypes[i] = paramContracts[i].UnderlyingType; + boundType = type.MakeGenericType(underlyingParamTypes); + } + else + { + //partial Generic: Construct xml name from its open generic type definition + xmlName = DataContract.GetXmlName(type.GetGenericTypeDefinition()); + Type[] paramTypes = type.GetGenericArguments(); + genericParams = new object[paramTypes.Length]; + for (int i = 0; i < paramTypes.Length; i++) + { + Type paramType = paramTypes[i]; + if (paramType.IsGenericParameter) + { + genericParams[i] = paramContracts[paramType.GenericParameterPosition]; + paramTypes[i] = paramContracts[paramType.GenericParameterPosition].UnderlyingType; + } + else + { + genericParams[i] = paramType; + } + } + boundType = type.MakeGenericType(paramTypes); + } + ClassDataContract boundClassContract = new ClassDataContract(boundType); + boundContracts ??= new Dictionary(); + boundContracts.Add(this, boundClassContract); + boundClassContract.XmlName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(xmlName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(UnderlyingType), genericParams)), xmlName.Namespace); + if (BaseClassContract != null) + boundClassContract.BaseClassContract = (ClassDataContract)BaseClassContract.BindGenericParameters(paramContracts, boundContracts); + boundClassContract.IsISerializable = IsISerializable; + boundClassContract.IsValueType = IsValueType; + boundClassContract.IsReference = IsReference; + if (Members != null) + { + boundClassContract.Members = new List(Members.Count); + foreach (DataMember member in Members) + boundClassContract.Members.Add(member.BindGenericParameters(paramContracts, boundContracts)); + } + return boundClassContract; + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All ctor's required to create an instance of this type are marked with RequiresUnreferencedCode.")] + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (IsEqualOrChecked(other, checkedContracts)) + return true; + + if (base.Equals(other, checkedContracts)) { - ClassDataContractCriticalHelper clonedHelper = new ClassDataContractCriticalHelper(this.UnderlyingType); - - clonedHelper._baseContract = this._baseContract; - clonedHelper._childElementNamespaces = this._childElementNamespaces; - clonedHelper.ContractNamespaces = this.ContractNamespaces; - clonedHelper._hasDataContract = this._hasDataContract; - clonedHelper._isMethodChecked = this._isMethodChecked; - clonedHelper._isNonAttributedType = this._isNonAttributedType; - clonedHelper.IsReference = this.IsReference; - clonedHelper.IsValueType = this.IsValueType; - clonedHelper.MemberNames = this.MemberNames; - clonedHelper.MemberNamespaces = this.MemberNamespaces; - clonedHelper._members = this._members; - clonedHelper.Name = this.Name; - clonedHelper.Namespace = this.Namespace; - clonedHelper._onDeserialized = this._onDeserialized; - clonedHelper._onDeserializing = this._onDeserializing; - clonedHelper._onSerialized = this._onSerialized; - clonedHelper._onSerializing = this._onSerializing; - clonedHelper.StableName = this.StableName; - clonedHelper.TopLevelElementName = this.TopLevelElementName; - clonedHelper.TopLevelElementNamespace = this.TopLevelElementNamespace; - clonedHelper._xmlFormatReaderDelegate = this._xmlFormatReaderDelegate; - clonedHelper._xmlFormatWriterDelegate = this._xmlFormatWriterDelegate; - - return clonedHelper; + if (other is ClassDataContract dataContract) + { + if (IsISerializable) + { + if (!dataContract.IsISerializable) + return false; + } + else + { + if (dataContract.IsISerializable) + return false; + + if (Members == null) + { + if (dataContract.Members != null) + { + // check that all the datamembers in dataContract.Members are optional + if (!IsEveryDataMemberOptional(dataContract.Members)) + return false; + } + } + else if (dataContract.Members == null) + { + // check that all the datamembers in Members are optional + if (!IsEveryDataMemberOptional(Members)) + return false; + } + else + { + Dictionary membersDictionary = new Dictionary(Members.Count); + List dataContractMembersList = new List(); + for (int i = 0; i < Members.Count; i++) + { + membersDictionary.Add(Members[i].Name, Members[i]); + } + + for (int i = 0; i < dataContract.Members.Count; i++) + { + // check that all datamembers common to both datacontracts match + if (membersDictionary.TryGetValue(dataContract.Members[i].Name, out DataMember? dataMember)) + { + if (dataMember.Equals(dataContract.Members[i], checkedContracts)) + { + membersDictionary.Remove(dataMember.Name); + } + else + { + return false; + } + } + // otherwise save the non-matching datamembers for later verification + else + { + dataContractMembersList.Add(dataContract.Members[i]); + } + } + + // check that datamembers left over from either datacontract are optional + if (!IsEveryDataMemberOptional(membersDictionary.Values)) + return false; + if (!IsEveryDataMemberOptional(dataContractMembersList)) + return false; + } + } + + if (BaseClassContract == null) + return (dataContract.BaseClassContract == null); + else if (dataContract.BaseClassContract == null) + return false; + else + return BaseClassContract.Equals(dataContract.BaseClassContract, checkedContracts); + } } + return false; + } + + private static bool IsEveryDataMemberOptional(IEnumerable dataMembers) + { + return !dataMembers.Any(dm => dm.IsRequired); + } + + public override int GetHashCode() + { + return base.GetHashCode(); } internal sealed class DataMemberComparer : IComparer { public int Compare(DataMember? x, DataMember? y) { - int orderCompare = x!.Order - y!.Order; + if (x == null && y == null) + return 0; + if (x == null || y == null) + return -1; + + int orderCompare = (int)(x.Order - y.Order); if (orderCompare != 0) return orderCompare; @@ -1556,7 +1472,7 @@ public int Compare(DataMember? x, DataMember? y) } /// - /// Get object type for Xml/JsonFormmatReaderGenerator + /// Get object type for Xml/JsonFormatReaderGenerator /// internal Type ObjectType { @@ -1570,37 +1486,5 @@ internal Type ObjectType return type; } } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal ClassDataContract Clone() - { - ClassDataContract clonedDc = new ClassDataContract(this.UnderlyingType); - clonedDc._helper = _helper.Clone(); - clonedDc.ContractNamespaces = this.ContractNamespaces; - clonedDc.ChildElementNamespaces = this.ChildElementNamespaces; - clonedDc.MemberNames = this.MemberNames; - clonedDc.MemberNamespaces = this.MemberNamespaces; - clonedDc.XmlFormatWriterDelegate = this.XmlFormatWriterDelegate; - clonedDc.XmlFormatReaderDelegate = this.XmlFormatReaderDelegate; - return clonedDc; - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void UpdateNamespaceAndMembers(Type type, XmlDictionaryString ns, string[] memberNames) - { - this.StableName = new XmlQualifiedName(GetStableName(type).Name, ns.Value); - this.Namespace = ns; - XmlDictionary dictionary = new XmlDictionary(1 + memberNames.Length); - this.Name = dictionary.Add(StableName.Name); - this.Namespace = ns; - this.ContractNamespaces = new XmlDictionaryString[] { ns }; - this.MemberNames = new XmlDictionaryString[memberNames.Length]; - this.MemberNamespaces = new XmlDictionaryString[memberNames.Length]; - for (int i = 0; i < memberNames.Length; i++) - { - this.MemberNames[i] = dictionary.Add(memberNames[i]); - this.MemberNamespaces[i] = ns; - } - } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs index b39d1af3d439f..201bdc3e93e83 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs @@ -2,17 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Xml; using System.Reflection; using System.Reflection.Emit; -using System.IO; -using System.Security; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization.Json; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization { @@ -88,6 +84,14 @@ private static MethodInfo StringFormat } } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "The trimmer will never remove the Invoke method from delegates.")] + internal static MethodInfo GetInvokeMethod(Type delegateType) + { + Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType)); + return delegateType.GetMethod("Invoke")!; + } + private Type _delegateType = null!; // initialized in BeginMethod private static Module? s_serializationModule; @@ -100,17 +104,9 @@ private static MethodInfo StringFormat private Stack _blockStack = null!; // initialized in BeginMethod private Label _methodEndLabel; - private readonly Dictionary _localNames = new Dictionary(); - - private enum CodeGenTrace { None, Save, Tron }; - private readonly CodeGenTrace _codeGenTrace; private LocalBuilder? _stringFormatArray; - internal CodeGenerator() - { - //Defaulting to None as thats the default value in WCF - _codeGenTrace = CodeGenTrace.None; - } + internal CodeGenerator() { } internal void BeginMethod(DynamicMethod dynamicMethod, Type delegateType, string methodName, Type[] argTypes, bool allowPrivateMemberAccess) { @@ -123,7 +119,7 @@ internal void BeginMethod(DynamicMethod dynamicMethod, Type delegateType, string internal void BeginMethod(string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); + MethodInfo signature = GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -148,15 +144,11 @@ private void InitILGeneration(string methodName, Type[] argTypes) _argList = new List(); for (int i = 0; i < argTypes.Length; i++) _argList.Add(new ArgBuilder(i, argTypes[i])); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceLabel("Begin method " + methodName + " {"); } internal Delegate EndMethod() { MarkLabel(_methodEndLabel); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceLabel("} End method"); Ret(); Delegate? retVal; @@ -180,15 +172,15 @@ internal MethodInfo CurrentMethod internal ArgBuilder GetArg(int index) { - return (ArgBuilder)_argList[index]; + return _argList[index]; } internal static Type GetVariableType(object var) { - if (var is ArgBuilder) - return ((ArgBuilder)var).ArgType; - else if (var is LocalBuilder) - return ((LocalBuilder)var).LocalType; + if (var is ArgBuilder argBuilder) + return argBuilder.ArgType; + else if (var is LocalBuilder localBuilder) + return localBuilder.LocalType; else return var.GetType(); } @@ -203,18 +195,12 @@ internal LocalBuilder DeclareLocal(Type type, string name, object initialValue) internal LocalBuilder DeclareLocal(Type type, string name) { - return DeclareLocal(type, name, false); + return DeclareLocal(type, false); } - internal LocalBuilder DeclareLocal(Type type, string name, bool isPinned) + internal LocalBuilder DeclareLocal(Type type, bool isPinned) { - LocalBuilder local = _ilGen.DeclareLocal(type, isPinned); - if (_codeGenTrace != CodeGenTrace.None) - { - _localNames[local] = name; - EmitSourceComment("Declare local '" + name + "' of type " + type); - } - return local; + return _ilGen.DeclareLocal(type, isPinned); } internal void Set(LocalBuilder local, object value) @@ -277,16 +263,14 @@ internal void InternalBreakFor(object userForState, OpCode branchInstruction) { foreach (object block in _blockStack) { - ForState? forState = block as ForState; - if (forState != null && (object)forState == userForState) + if (block == userForState && block is ForState forState) { if (!forState.RequiresEndLabel) { forState.EndLabel = DefineLabel(); forState.RequiresEndLabel = true; } - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(branchInstruction + " " + forState.EndLabel.GetHashCode()); + _ilGen.Emit(branchInstruction, forState.EndLabel); break; } @@ -329,7 +313,7 @@ internal void EndForEach(MethodInfo moveNextMethod) internal void IfNotDefaultValue(object value) { Type type = GetVariableType(value); - TypeCode typeCode = type.GetTypeCode(); + TypeCode typeCode = Type.GetTypeCode(type); if ((typeCode == TypeCode.Object && type.IsValueType) || typeCode == TypeCode.DateTime || typeCode == TypeCode.Decimal) { @@ -507,51 +491,36 @@ internal void Call(MethodInfo methodInfo) { if (methodInfo.IsVirtual && !methodInfo.DeclaringType!.IsValueType) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Callvirt " + methodInfo.ToString() + " on type " + methodInfo.DeclaringType.ToString()); _ilGen.Emit(OpCodes.Callvirt, methodInfo); } else if (methodInfo.IsStatic) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Static Call " + methodInfo.ToString() + " on type " + methodInfo.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Call, methodInfo); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Call " + methodInfo.ToString() + " on type " + methodInfo.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Call, methodInfo); } } internal void Call(ConstructorInfo ctor) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Call " + ctor.ToString() + " on type " + ctor.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Call, ctor); } internal void New(ConstructorInfo constructorInfo) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Newobj " + constructorInfo.ToString() + " on type " + constructorInfo.DeclaringType!.ToString()); _ilGen.Emit(OpCodes.Newobj, constructorInfo); } - internal void InitObj(Type valueType) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Initobj " + valueType); _ilGen.Emit(OpCodes.Initobj, valueType); } internal void NewArray(Type elementType, object len) { Load(len); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Newarr " + elementType); _ilGen.Emit(OpCodes.Newarr, elementType); } @@ -605,27 +574,20 @@ internal Type LoadMember(MemberInfo memberInfo) memberType = fieldInfo.FieldType; if (fieldInfo.IsStatic) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldsfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Ldsfld, fieldInfo); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Ldfld, fieldInfo); } } else if (memberInfo is PropertyInfo property) { memberType = property.PropertyType; - if (property != null) - { - MethodInfo? getMethod = property.GetMethod; - if (getMethod == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoGetMethodForProperty, property.DeclaringType, property))); - Call(getMethod); - } + MethodInfo? getMethod = property.GetMethod; + if (getMethod == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoGetMethodForProperty, property.DeclaringType, property))); + Call(getMethod); } else if (memberInfo is MethodInfo method) { @@ -635,7 +597,6 @@ internal Type LoadMember(MemberInfo memberInfo) else throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.CannotLoadMemberType, "Unknown", memberInfo.DeclaringType, memberInfo.Name))); - EmitStackTop(memberType); return memberType; } @@ -645,30 +606,22 @@ internal void StoreMember(MemberInfo memberInfo) { if (fieldInfo.IsStatic) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stsfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Stsfld, fieldInfo); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stfld " + fieldInfo + " on type " + fieldInfo.DeclaringType); _ilGen.Emit(OpCodes.Stfld, fieldInfo); } } - else if (memberInfo is PropertyInfo) + else if (memberInfo is PropertyInfo property) { - PropertyInfo? property = memberInfo as PropertyInfo; - if (property != null) - { - MethodInfo? setMethod = property.SetMethod; - if (setMethod == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property))); - Call(setMethod); - } + MethodInfo? setMethod = property.SetMethod; + if (setMethod == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property))); + Call(setMethod); } - else if (memberInfo is MethodInfo) - Call((MethodInfo)memberInfo); + else if (memberInfo is MethodInfo method) + Call(method); else throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.CannotLoadMemberType, "Unknown"))); } @@ -677,7 +630,7 @@ internal void LoadDefaultValue(Type type) { if (type.IsValueType) { - switch (type.GetTypeCode()) + switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: Ldc(false); @@ -719,24 +672,22 @@ internal void Load(object? obj) { if (obj == null) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldnull"); _ilGen.Emit(OpCodes.Ldnull); } - else if (obj is ArgBuilder) - Ldarg((ArgBuilder)obj); - else if (obj is LocalBuilder) - Ldloc((LocalBuilder)obj); + else if (obj is ArgBuilder argBuilder) + Ldarg(argBuilder); + else if (obj is LocalBuilder localBuilder) + Ldloc(localBuilder); else Ldc(obj); } internal void Store(object var) { - if (var is ArgBuilder) - Starg((ArgBuilder)var); - else if (var is LocalBuilder) - Stloc((LocalBuilder)var); + if (var is ArgBuilder argBuilder) + Starg(argBuilder); + else if (var is LocalBuilder localBuilder) + Stloc(localBuilder); else { DiagnosticUtility.DebugAssert("Data can only be stored into ArgBuilder or LocalBuilder."); @@ -754,10 +705,10 @@ internal void Dec(object var) internal void LoadAddress(object obj) { - if (obj is ArgBuilder) - LdargAddress((ArgBuilder)obj); - else if (obj is LocalBuilder) - LdlocAddress((LocalBuilder)obj); + if (obj is ArgBuilder argBuilder) + LdargAddress(argBuilder); + else if (obj is LocalBuilder localBuilder) + LdlocAddress(localBuilder); else Load(obj); } @@ -776,22 +727,16 @@ internal void ConvertValue(Type source, Type target) internal void Castclass(Type target) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Castclass " + target); _ilGen.Emit(OpCodes.Castclass, target); } internal void Box(Type type) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Box " + type); _ilGen.Emit(OpCodes.Box, type); } internal void Unbox(Type type) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Unbox " + type); _ilGen.Emit(OpCodes.Unbox, type); } @@ -816,67 +761,53 @@ private static OpCode GetLdindOpCode(TypeCode typeCode) => internal void Ldobj(Type type) { - OpCode opCode = GetLdindOpCode(type.GetTypeCode()); + OpCode opCode = GetLdindOpCode(Type.GetTypeCode(type)); if (!opCode.Equals(OpCodes.Nop)) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldobj " + type); _ilGen.Emit(OpCodes.Ldobj, type); } } internal void Stobj(Type type) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stobj " + type); _ilGen.Emit(OpCodes.Stobj, type); } internal void Ceq() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ceq"); _ilGen.Emit(OpCodes.Ceq); } internal void Throw() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Throw"); _ilGen.Emit(OpCodes.Throw); } internal void Ldtoken(Type t) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldtoken " + t); _ilGen.Emit(OpCodes.Ldtoken, t); } internal void Ldc(object o) { Type valueType = o.GetType(); - if (o is Type) + if (o is Type t) { - Ldtoken((Type)o); + Ldtoken(t); Call(GetTypeFromHandle); } else if (valueType.IsEnum) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceComment("Ldc " + o.GetType() + "." + o); Ldc(Convert.ChangeType(o, Enum.GetUnderlyingType(valueType), null)); } else { - switch (valueType.GetTypeCode()) + switch (Type.GetTypeCode(valueType)) { case TypeCode.Boolean: Ldc((bool)o); @@ -915,6 +846,7 @@ internal void Ldc(object o) case TypeCode.Decimal: case TypeCode.DateTime: case TypeCode.Empty: + case TypeCode.DBNull: default: throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.UnknownConstantType, DataContract.GetClrTypeFullName(valueType)))); } @@ -925,50 +857,36 @@ internal void Ldc(bool boolVar) { if (boolVar) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i4 1"); _ilGen.Emit(OpCodes.Ldc_I4_1); } else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i4 0"); _ilGen.Emit(OpCodes.Ldc_I4_0); } } internal void Ldc(int intVar) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i4 " + intVar); _ilGen.Emit(OpCodes.Ldc_I4, intVar); } internal void Ldc(long l) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.i8 " + l); _ilGen.Emit(OpCodes.Ldc_I8, l); } internal void Ldc(float f) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.r4 " + f); _ilGen.Emit(OpCodes.Ldc_R4, f); } internal void Ldc(double d) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldc.r8 " + d); _ilGen.Emit(OpCodes.Ldc_R8, d); } internal void Ldstr(string strVar) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldstr " + strVar); _ilGen.Emit(OpCodes.Ldstr, strVar); } @@ -982,27 +900,17 @@ internal void LdlocAddress(LocalBuilder localBuilder) internal void Ldloc(LocalBuilder localBuilder) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldloc " + _localNames[localBuilder]); _ilGen.Emit(OpCodes.Ldloc, localBuilder); - EmitStackTop(localBuilder.LocalType); } internal void Stloc(LocalBuilder local) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Stloc " + _localNames[local]); - EmitStackTop(local.LocalType); _ilGen.Emit(OpCodes.Stloc, local); } - internal void Ldloca(LocalBuilder localBuilder) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldloca " + _localNames[localBuilder]); _ilGen.Emit(OpCodes.Ldloca, localBuilder); - EmitStackTop(localBuilder.LocalType); } internal void LdargAddress(ArgBuilder argBuilder) @@ -1025,17 +933,11 @@ internal void Starg(ArgBuilder arg) internal void Ldarg(int slot) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldarg " + slot); - _ilGen.Emit(OpCodes.Ldarg, slot); } internal void Starg(int slot) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Starg " + slot); - _ilGen.Emit(OpCodes.Starg, slot); } @@ -1046,26 +948,19 @@ internal void Ldarga(ArgBuilder argBuilder) internal void Ldarga(int slot) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldarga " + slot); - _ilGen.Emit(OpCodes.Ldarga, slot); } internal void Ldlen() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ldlen"); _ilGen.Emit(OpCodes.Ldlen); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Conv.i4"); _ilGen.Emit(OpCodes.Conv_I4); } private static OpCode GetLdelemOpCode(TypeCode typeCode) => typeCode switch { - TypeCode.Object => OpCodes.Ldelem_Ref, // TypeCode.Object: + TypeCode.Object or TypeCode.DBNull => OpCodes.Ldelem_Ref, // TypeCode.Object: TypeCode.Boolean => OpCodes.Ldelem_I1, // TypeCode.Boolean: TypeCode.Char => OpCodes.Ldelem_I2, // TypeCode.Char: TypeCode.SByte => OpCodes.Ldelem_I1, // TypeCode.SByte: @@ -1090,29 +985,22 @@ internal void Ldelem(Type arrayElementType) } else { - OpCode opCode = GetLdelemOpCode(arrayElementType.GetTypeCode()); + OpCode opCode = GetLdelemOpCode(Type.GetTypeCode(arrayElementType)); if (opCode.Equals(OpCodes.Nop)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArrayTypeIsNotSupported_GeneratingCode, DataContract.GetClrTypeFullName(arrayElementType)))); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode); - EmitStackTop(arrayElementType); } } internal void Ldelema(Type arrayElementType) { OpCode opCode = OpCodes.Ldelema; - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode, arrayElementType); - - EmitStackTop(arrayElementType); } private static OpCode GetStelemOpCode(TypeCode typeCode) => typeCode switch { - TypeCode.Object => OpCodes.Stelem_Ref, // TypeCode.Object: + TypeCode.Object or TypeCode.DBNull => OpCodes.Stelem_Ref, // TypeCode.Object: TypeCode.Boolean => OpCodes.Stelem_I1, // TypeCode.Boolean: TypeCode.Char => OpCodes.Stelem_I2, // TypeCode.Char: TypeCode.SByte => OpCodes.Stelem_I1, // TypeCode.SByte: @@ -1135,12 +1023,10 @@ internal void Stelem(Type arrayElementType) Stelem(Enum.GetUnderlyingType(arrayElementType)); else { - OpCode opCode = GetStelemOpCode(arrayElementType.GetTypeCode()); + OpCode opCode = GetStelemOpCode(Type.GetTypeCode(arrayElementType)); if (opCode.Equals(OpCodes.Nop)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArrayTypeIsNotSupported_GeneratingCode, DataContract.GetClrTypeFullName(arrayElementType)))); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); - EmitStackTop(arrayElementType); + _ilGen.Emit(opCode); } } @@ -1153,92 +1039,64 @@ internal Label DefineLabel() internal void MarkLabel(Label label) { _ilGen.MarkLabel(label); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceLabel(label.GetHashCode() + ":"); } internal void Add() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Add"); _ilGen.Emit(OpCodes.Add); } internal void Subtract() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Sub"); _ilGen.Emit(OpCodes.Sub); } internal void And() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("And"); _ilGen.Emit(OpCodes.And); } internal void Or() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Or"); _ilGen.Emit(OpCodes.Or); } internal void Not() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Not"); _ilGen.Emit(OpCodes.Not); } internal void Ret() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Ret"); _ilGen.Emit(OpCodes.Ret); } internal void Br(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Br " + label.GetHashCode()); _ilGen.Emit(OpCodes.Br, label); } internal void Blt(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Blt " + label.GetHashCode()); _ilGen.Emit(OpCodes.Blt, label); } internal void Brfalse(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Brfalse " + label.GetHashCode()); _ilGen.Emit(OpCodes.Brfalse, label); } internal void Brtrue(Label label) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Brtrue " + label.GetHashCode()); _ilGen.Emit(OpCodes.Brtrue, label); } - - internal void Pop() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Pop"); _ilGen.Emit(OpCodes.Pop); } internal void Dup() { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("Dup"); _ilGen.Emit(OpCodes.Dup); } @@ -1296,13 +1154,11 @@ private void InternalConvert(Type source, Type target, bool isAddress) { if (source.IsValueType) { - OpCode opCode = GetConvOpCode(target.GetTypeCode()); + OpCode opCode = GetConvOpCode(Type.GetTypeCode(target)); if (opCode.Equals(OpCodes.Nop)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NoConversionPossibleTo, DataContract.GetClrTypeFullName(target)))); else { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction(opCode.ToString()!); _ilGen.Emit(opCode); } } @@ -1351,26 +1207,6 @@ private static void ThrowMismatchException(object expected) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ExpectingEnd, expected.ToString()))); } - - internal static void EmitSourceInstruction(string line) - { - } - - internal static void EmitSourceLabel(string line) - { - } - - internal static void EmitSourceComment(string comment) - { - } - - - internal void EmitStackTop(Type stackTopType) - { - if (_codeGenTrace != CodeGenTrace.Tron) - return; - } - internal Label[] Switch(int labelCount) { SwitchState switchState = new SwitchState(DefineLabel(), DefineLabel()); @@ -1385,8 +1221,6 @@ internal Label[] Switch(int labelCount) } internal void Case(Label caseLabel1, string caseLabelName) { - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("case " + caseLabelName + "{"); MarkLabel(caseLabel1); } @@ -1397,8 +1231,6 @@ internal void EndCase() if (switchState == null) ThrowMismatchException(stackTop); Br(switchState.EndOfSwitchLabel); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("} //end case "); } internal void EndSwitch() @@ -1407,8 +1239,6 @@ internal void EndSwitch() SwitchState? switchState = stackTop as SwitchState; if (switchState == null) ThrowMismatchException(stackTop); - if (_codeGenTrace != CodeGenTrace.None) - EmitSourceInstruction("} //end switch"); if (!switchState.DefaultDefined) MarkLabel(switchState.DefaultLabel); MarkLabel(switchState.EndOfSwitchLabel); @@ -1490,8 +1320,8 @@ internal sealed class ArgBuilder internal Type ArgType; internal ArgBuilder(int index, Type argType) { - this.Index = index; - this.ArgType = argType; + Index = index; + ArgType = argType; } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs index df57f68205cf2..948817e1d31a2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs @@ -8,62 +8,14 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using System.Security; using System.Threading; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { - // The interface is a perf optimization. - // Only KeyValuePairAdapter should implement the interface. - internal interface IKeyValuePairAdapter { } - - //Special Adapter class to serialize KeyValuePair as Dictionary needs it when polymorphism is involved - [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/System.Collections.Generic")] - internal sealed class KeyValuePairAdapter : IKeyValuePairAdapter - { - private K _kvpKey; - private T _kvpValue; - - public KeyValuePairAdapter(KeyValuePair kvPair) - { - _kvpKey = kvPair.Key; - _kvpValue = kvPair.Value; - } - - [DataMember(Name = "key")] - public K Key - { - get { return _kvpKey; } - set { _kvpKey = value; } - } - - [DataMember(Name = "value")] - public T Value - { - get - { - return _kvpValue; - } - set - { - _kvpValue = value; - } - } - - internal KeyValuePair GetKeyValuePair() - { - return new KeyValuePair(_kvpKey, _kvpValue); - } - - internal static KeyValuePairAdapter GetKeyValuePairAdapter(KeyValuePair kvPair) - { - return new KeyValuePairAdapter(kvPair); - } - } - internal interface IKeyValue { object? Key { get; set; } @@ -73,39 +25,28 @@ internal interface IKeyValue [DataContract(Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")] internal struct KeyValue : IKeyValue { - private K _key; - private V _value; - internal KeyValue(K key, V value) { - _key = key; - _value = value; + Key = key; + Value = value; } [DataMember(IsRequired = true)] - public K Key - { - get { return _key; } - set { _key = value; } - } + public K Key { get; set; } [DataMember(IsRequired = true)] - public V Value - { - get { return _value; } - set { _value = value; } - } + public V Value { get; set; } object? IKeyValue.Key { - get { return _key; } - set { _key = (K)value!; } + get => this.Key; + set => this.Key = (K)value!; } object? IKeyValue.Value { - get { return _value; } - set { _value = (V)value!; } + get => this.Value; + set => this.Value = (V)value!; } } @@ -122,9 +63,15 @@ internal enum CollectionKind : byte Enumerable, Array, } +} +namespace System.Runtime.Serialization.DataContracts +{ internal sealed class CollectionDataContract : DataContract { + internal const string ContractTypeString = nameof(CollectionDataContract); + public override string? ContractType => ContractTypeString; + private XmlDictionaryString _collectionItemName; private XmlDictionaryString? _childElementNamespace; @@ -140,8 +87,20 @@ internal CollectionDataContract(Type type) : base(new CollectionDataContractCrit } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? deserializationExceptionMessage) - : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage)) + internal CollectionDataContract(Type type, DataContract itemContract) : base(new CollectionDataContractCriticalHelper(type, itemContract)) + { + InitCollectionDataContract(this); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal CollectionDataContract(Type type, CollectionKind kind) : base(new CollectionDataContractCriticalHelper(type, kind)) + { + InitCollectionDataContract(this); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? serializationExceptionMessage, string? deserializationExceptionMessage) + : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage)) { InitCollectionDataContract(GetSharedTypeContract(type)); } @@ -180,32 +139,16 @@ private void InitCollectionDataContract(DataContract? sharedTypeContract) _helper.SharedTypeContract = sharedTypeContract; } - private static Type[] KnownInterfaces - { - get - { return CollectionDataContractCriticalHelper.KnownInterfaces; } - } + private static Type[] KnownInterfaces => CollectionDataContractCriticalHelper.KnownInterfaces; - internal CollectionKind Kind - { - get - { return _helper.Kind; } - } + internal CollectionKind Kind => _helper.Kind; - public Type ItemType - { - get - { return _helper.ItemType; } - set { _helper.ItemType = value; } - } + internal Type ItemType => _helper.ItemType; - public DataContract ItemContract + internal DataContract ItemContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { - return _itemContract ?? _helper.ItemContract; - } + get => _itemContract ?? _helper.ItemContract; set { @@ -214,51 +157,45 @@ public DataContract ItemContract } } - internal DataContract? SharedTypeContract - { - get - { return _helper.SharedTypeContract; } - } + internal DataContract? SharedTypeContract => _helper.SharedTypeContract; - public string ItemName + internal string ItemName { - get - { return _helper.ItemName; } - - set - { _helper.ItemName = value; } + get => _helper.ItemName; + set => _helper.ItemName = value; } - public XmlDictionaryString CollectionItemName + internal XmlDictionaryString CollectionItemName => _collectionItemName; + + internal string? KeyName { - get { return _collectionItemName; } - set { _collectionItemName = value; } + get => _helper.KeyName; + set => _helper.KeyName = value; } - public string? KeyName + internal string? ValueName { - get - { return _helper.KeyName; } - - set - { _helper.KeyName = value; } + get => _helper.ValueName; + set => _helper.ValueName = value; } - public string? ValueName + public override bool IsDictionaryLike([NotNullWhen(true)] out string? keyName, [NotNullWhen(true)] out string? valueName, [NotNullWhen(true)] out string? itemName) { - get - { return _helper.ValueName; } - - set - { _helper.ValueName = value; } + keyName = KeyName; + valueName = ValueName; + itemName = ItemName; + return IsDictionary; } - internal bool IsDictionary + public override DataContract BaseContract { - get { return KeyName != null; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => ItemContract; } - public XmlDictionaryString? ChildElementNamespace + internal bool IsDictionary => KeyName != null; + + internal XmlDictionaryString? ChildElementNamespace { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get @@ -285,62 +222,38 @@ public XmlDictionaryString? ChildElementNamespace internal bool IsItemTypeNullable { - get { return _helper.IsItemTypeNullable; } - set { _helper.IsItemTypeNullable = value; } + get => _helper.IsItemTypeNullable; + set => _helper.IsItemTypeNullable = value; } internal bool IsConstructorCheckRequired { - get - { return _helper.IsConstructorCheckRequired; } - - set - { _helper.IsConstructorCheckRequired = value; } + get => _helper.IsConstructorCheckRequired; + set => _helper.IsConstructorCheckRequired = value; } - internal MethodInfo? GetEnumeratorMethod - { - get - { return _helper.GetEnumeratorMethod; } - } + internal MethodInfo? GetEnumeratorMethod => _helper.GetEnumeratorMethod; - internal MethodInfo? AddMethod - { - get - { return _helper.AddMethod; } - } + internal MethodInfo? AddMethod => _helper.AddMethod; - internal ConstructorInfo? Constructor - { - get - { return _helper.Constructor; } - } + internal ConstructorInfo? Constructor => _helper.Constructor; public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } - - set - { _helper.KnownDataContracts = value; } + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } - internal string? InvalidCollectionInSharedContractMessage - { - get - { return _helper.InvalidCollectionInSharedContractMessage; } - } + internal string? InvalidCollectionInSharedContractMessage => _helper.InvalidCollectionInSharedContractMessage; - internal string? DeserializationExceptionMessage - { - get { return _helper.DeserializationExceptionMessage; } - } + internal string? SerializationExceptionMessage => _helper.SerializationExceptionMessage; - internal bool IsReadOnlyContract - { - get { return DeserializationExceptionMessage != null; } - } + internal string? DeserializationExceptionMessage => _helper.DeserializationExceptionMessage; + + internal bool IsReadOnlyContract => DeserializationExceptionMessage != null; + + private bool ItemNameSetExplicit => _helper.ItemNameSetExplicit; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private XmlFormatCollectionWriterDelegate CreateXmlFormatWriterDelegate() @@ -367,9 +280,6 @@ internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate } return _helper.XmlFormatWriterDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -402,9 +312,6 @@ internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate } return _helper.XmlFormatReaderDelegate; } - set - { - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -479,6 +386,7 @@ private sealed class CollectionDataContractCriticalHelper : DataContract.DataCon private readonly MethodInfo? _getEnumeratorMethod; private readonly MethodInfo? _addMethod; private readonly ConstructorInfo? _constructor; + private readonly string? _serializationExceptionMessage; private readonly string? _deserializationExceptionMessage; private DataContract? _itemContract; private DataContract? _sharedTypeContract; @@ -549,9 +457,9 @@ private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAtt } XmlDictionary dictionary = isDictionary ? new XmlDictionary(5) : new XmlDictionary(3); - this.Name = dictionary.Add(this.StableName.Name); - this.Namespace = dictionary.Add(this.StableName.Namespace); - _itemName = itemName ?? DataContract.GetStableName(DataContract.UnwrapNullableType(itemType)).Name; + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); + _itemName = itemName ?? DataContract.GetXmlName(DataContract.UnwrapNullableType(itemType)).Name; _collectionItemName = dictionary.Add(_itemName); if (isDictionary) { @@ -561,11 +469,10 @@ private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAtt } if (collectionContractAttribute != null) { - this.IsReference = collectionContractAttribute.IsReference; + IsReference = collectionContractAttribute.IsReference; } } - // array [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] @@ -575,7 +482,31 @@ internal CollectionDataContractCriticalHelper( type = Globals.TypeOfObjectArray; if (type.GetArrayRank() > 1) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SupportForMultidimensionalArraysNotPresent)); - this.StableName = DataContract.GetStableName(type); + XmlName = DataContract.GetXmlName(type); + Init(CollectionKind.Array, type.GetElementType(), null); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal CollectionDataContractCriticalHelper( + [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] + Type type, + CollectionKind kind) : base(type) + { + XmlName = DataContract.GetXmlName(type); + Init(kind, type.GetElementType(), null); + } + + // array + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal CollectionDataContractCriticalHelper( + [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] + Type type, + DataContract itemContract) : base(type) + { + if (type.GetArrayRank() > 1) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SupportForMultidimensionalArraysNotPresent)); + XmlName = CreateQualifiedName(Globals.ArrayPrefix + itemContract.XmlName.Name, itemContract.XmlName.Namespace); + _itemContract = itemContract; Init(CollectionKind.Array, type.GetElementType(), null); } @@ -584,7 +515,7 @@ internal CollectionDataContractCriticalHelper( internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, - CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? deserializationExceptionMessage) + CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? serializationExceptionMessage, string? deserializationExceptionMessage) : base(type) { if (getEnumeratorMethod == null) @@ -593,10 +524,11 @@ internal CollectionDataContractCriticalHelper( throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveItemType, GetClrTypeFullName(type)))); CollectionDataContractAttribute? collectionContractAttribute; - this.StableName = DataContract.GetCollectionStableName(type, itemType, out collectionContractAttribute); + XmlName = DataContract.GetCollectionXmlName(type, itemType, out collectionContractAttribute); Init(kind, itemType, collectionContractAttribute); _getEnumeratorMethod = getEnumeratorMethod; + _serializationExceptionMessage = serializationExceptionMessage; _deserializationExceptionMessage = deserializationExceptionMessage; } @@ -605,20 +537,12 @@ internal CollectionDataContractCriticalHelper( internal CollectionDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type, - CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor) : base(type) + CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor) + : this(type, kind, itemType, getEnumeratorMethod, (string?)null, (string?)null) { - if (getEnumeratorMethod == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveGetEnumeratorMethod, DataContract.GetClrTypeFullName(type)))); if (addMethod == null && !type.IsInterface) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveAddMethod, DataContract.GetClrTypeFullName(type)))); - if (itemType == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionMustHaveItemType, DataContract.GetClrTypeFullName(type)))); - - CollectionDataContractAttribute? collectionContractAttribute; - this.StableName = DataContract.GetCollectionStableName(type, itemType, out collectionContractAttribute); - Init(kind, itemType, collectionContractAttribute); - _getEnumeratorMethod = getEnumeratorMethod; _addMethod = addMethod; _constructor = constructor; } @@ -641,16 +565,9 @@ internal CollectionDataContractCriticalHelper( _invalidCollectionInSharedContractMessage = invalidCollectionInSharedContractMessage; } - internal CollectionKind Kind - { - get { return _kind; } - } + internal CollectionKind Kind => _kind; - internal Type ItemType - { - get { return _itemType; } - set { _itemType = value; } - } + internal Type ItemType => _itemType; internal DataContract ItemContract { @@ -675,9 +592,7 @@ internal DataContract ItemContract } else { - _itemContract = - DataContract.GetDataContractFromGeneratedAssembly(ItemType) ?? - DataContract.GetDataContract(ItemType); + _itemContract = DataContract.GetDataContract(ItemType); } } return _itemContract; @@ -690,53 +605,52 @@ internal DataContract ItemContract internal DataContract? SharedTypeContract { - get { return _sharedTypeContract; } - set { _sharedTypeContract = value; } + get => _sharedTypeContract; + set => _sharedTypeContract = value; } internal string ItemName { - get { return _itemName; } - set { _itemName = value; } + get => _itemName; + set => _itemName = value; } internal bool IsConstructorCheckRequired { - get { return _isConstructorCheckRequired; } - set { _isConstructorCheckRequired = value; } + get => _isConstructorCheckRequired; + set => _isConstructorCheckRequired = value; } - public XmlDictionaryString CollectionItemName - { - get { return _collectionItemName; } - } + internal XmlDictionaryString CollectionItemName => _collectionItemName; internal string? KeyName { - get { return _keyName; } - set { _keyName = value; } + get => _keyName; + set => _keyName = value; } internal string? ValueName { - get { return _valueName; } - set { _valueName = value; } + get => _valueName; + set => _valueName = value; } internal bool IsDictionary => KeyName != null; - public string? DeserializationExceptionMessage => _deserializationExceptionMessage; + internal string? SerializationExceptionMessage => _serializationExceptionMessage; + + internal string? DeserializationExceptionMessage => _deserializationExceptionMessage; - public XmlDictionaryString? ChildElementNamespace + internal XmlDictionaryString? ChildElementNamespace { - get { return _childElementNamespace; } - set { _childElementNamespace = value; } + get => _childElementNamespace; + set => _childElementNamespace = value; } internal bool IsItemTypeNullable { - get { return _isItemTypeNullable; } - set { _isItemTypeNullable = value; } + get => _isItemTypeNullable; + set => _isItemTypeNullable = value; } internal MethodInfo? GetEnumeratorMethod => _getEnumeratorMethod; @@ -756,10 +670,11 @@ internal override DataContractDictionary? KnownDataContracts { if (!_isKnownTypeAttributeChecked) { - _knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType); + _knownDataContracts = DataContract.ImportKnownTypeAttributes(UnderlyingType); Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } + _knownDataContracts ??= new DataContractDictionary(); } } return _knownDataContracts; @@ -775,20 +690,20 @@ internal override DataContractDictionary? KnownDataContracts internal XmlFormatCollectionWriterDelegate? XmlFormatWriterDelegate { - get { return _xmlFormatWriterDelegate; } - set { _xmlFormatWriterDelegate = value; } + get => _xmlFormatWriterDelegate; + set => _xmlFormatWriterDelegate = value; } internal XmlFormatCollectionReaderDelegate? XmlFormatReaderDelegate { - get { return _xmlFormatReaderDelegate; } - set { _xmlFormatReaderDelegate = value; } + get => _xmlFormatReaderDelegate; + set => _xmlFormatReaderDelegate = value; } internal XmlFormatGetOnlyCollectionReaderDelegate? XmlFormatGetOnlyCollectionReaderDelegate { - get { return _xmlFormatGetOnlyCollectionReaderDelegate; } - set { _xmlFormatGetOnlyCollectionReaderDelegate = value; } + get => _xmlFormatGetOnlyCollectionReaderDelegate; + set => _xmlFormatGetOnlyCollectionReaderDelegate = value; } private delegate void IncrementCollectionCountDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context); @@ -816,13 +731,13 @@ internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, case CollectionKind.GenericCollection: case CollectionKind.GenericList: { - var buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(ItemType); + MethodInfo? buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(ItemType); _incrementCollectionCountDelegate = (IncrementCollectionCountDelegate)buildIncrementCollectionCountDelegate.Invoke(null, Array.Empty())!; } break; case CollectionKind.GenericDictionary: { - var buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(typeof(KeyValuePair<,>).MakeGenericType(ItemType.GetGenericArguments())); + MethodInfo? buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(typeof(KeyValuePair<,>).MakeGenericType(ItemType.GetGenericArguments())); _incrementCollectionCountDelegate = (IncrementCollectionCountDelegate)buildIncrementCollectionCountDelegate.Invoke(null, Array.Empty())!; } break; @@ -868,7 +783,7 @@ internal IEnumerator GetEnumeratorForCollection(object obj) { if (_createGenericDictionaryEnumeratorDelegate == null) { - var keyValueTypes = ItemType.GetGenericArguments(); + Type[]? keyValueTypes = ItemType.GetGenericArguments(); MethodInfo buildCreateGenericDictionaryEnumerator = GetBuildCreateGenericDictionaryEnumeratorGenericMethod(keyValueTypes); _createGenericDictionaryEnumeratorDelegate = (CreateGenericDictionaryEnumeratorDelegate)buildCreateGenericDictionaryEnumerator.Invoke(null, Array.Empty())!; } @@ -962,7 +877,7 @@ private static CreateGenericDictionaryEnumeratorDelegate BuildCreateGenericDicti { return this; } - if (type.IsDefined(Globals.TypeOfDataContractAttribute, false)) + if (type.IsSerializable || type.IsDefined(Globals.TypeOfDataContractAttribute, false)) { return new ClassDataContract(type); } @@ -989,20 +904,20 @@ internal static bool IsCollection(Type type, [NotNullWhen(true)] out Type? itemT } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool IsCollection(Type type, bool constructorRequired) + internal static bool IsCollection(Type type, bool constructorRequired, bool skipIfReadOnlyContract) { - return IsCollectionHelper(type, out _, constructorRequired); + return IsCollectionHelper(type, out _, constructorRequired, skipIfReadOnlyContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? itemType, bool constructorRequired) + private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? itemType, bool constructorRequired, bool skipIfReadOnlyContract = false) { if (type.IsArray && DataContract.GetBuiltInDataContract(type) == null) { itemType = type.GetElementType()!; return true; } - return IsCollectionOrTryCreate(type, tryCreate: false, out _, out itemType, constructorRequired); + return IsCollectionOrTryCreate(type, tryCreate: false, out _, out itemType, constructorRequired, skipIfReadOnlyContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -1012,7 +927,7 @@ internal static bool TryCreate(Type type, [NotNullWhen(true)] out DataContract? } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) + internal static bool TryCreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsArray) { @@ -1025,36 +940,6 @@ internal static bool CreateGetOnlyCollectionDataContract(Type type, [NotNullWhen } } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool TryCreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) - { - dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); - if (dataContract == null) - { - if (type.IsArray) - { - dataContract = new CollectionDataContract(type); - return true; - } - else - { - return IsCollectionOrTryCreate(type, tryCreate: true, out dataContract!, out _, constructorRequired: false); - } - } - else - { - if (dataContract is CollectionDataContract) - { - return true; - } - else - { - dataContract = null; - return false; - } - } - } - // Once https://github.com/mono/linker/issues/1731 is fixed we can remove the suppression from here as it won't be needed any longer. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:GetMethod", Justification = "The DynamicallyAccessedMembers declarations will ensure the interface methods will be preserved.")] @@ -1074,7 +959,7 @@ private static bool IsArraySegment(Type t) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract? dataContract, out Type itemType, bool constructorRequired) + private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract? dataContract, out Type itemType, bool constructorRequired, bool skipIfReadOnlyContract = false) { dataContract = null; itemType = Globals.TypeOfObject; @@ -1088,6 +973,7 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC MethodInfo? addMethod, getEnumeratorMethod; bool hasCollectionDataContract = IsCollectionDataContract(type); bool isReadOnlyContract = false; + string? serializationExceptionMessage = null; string? deserializationExceptionMessage = null; Type? baseType = type.BaseType; bool isBaseTypeCollection = (baseType != null && baseType != Globals.TypeOfObject @@ -1184,7 +1070,7 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC else { isReadOnlyContract = true; - GetReadOnlyCollectionExceptionMessages(type, SR.CollectionTypeDoesNotHaveDefaultCtor, null, out deserializationExceptionMessage); + GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveDefaultCtor, null, out serializationExceptionMessage, out deserializationExceptionMessage); } } } @@ -1238,22 +1124,22 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC // All collection types could be considered read-only collections except collection types that are marked [Serializable]. // Collection types marked [Serializable] cannot be read-only collections for backward compatibility reasons. // DataContract types and POCO types cannot be collection types, so they don't need to be factored in. - if (type.IsSerializable) + if (type.IsSerializable || skipIfReadOnlyContract) { - return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException, + return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException && !skipIfReadOnlyContract, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), ref dataContract); } else { isReadOnlyContract = true; - GetReadOnlyCollectionExceptionMessages(type, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), out deserializationExceptionMessage); + GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), out serializationExceptionMessage, out deserializationExceptionMessage); } } if (tryCreate) { dataContract = isReadOnlyContract ? - new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage) : + new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) : new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired); } } @@ -1298,12 +1184,12 @@ private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataC Debug.Assert(getEnumeratorMethod != null); dataContract = isReadOnlyContract ? - new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, deserializationExceptionMessage) : + new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) : new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired); } } - return true; + return !(isReadOnlyContract && skipIfReadOnlyContract); } internal static bool IsCollectionDataContract(Type type) @@ -1331,8 +1217,9 @@ private static bool HandleIfInvalidCollection(Type type, bool tryCreate, bool ha return false; } - private static void GetReadOnlyCollectionExceptionMessages(Type type, string message, string? param, out string deserializationExceptionMessage) + private static void GetReadOnlyCollectionExceptionMessages(Type type, bool hasCollectionDataContract, string message, string? param, out string serializationExceptionMessage, out string deserializationExceptionMessage) { + serializationExceptionMessage = GetInvalidCollectionMessage(message, SR.Format(hasCollectionDataContract ? SR.InvalidCollectionDataContract : SR.InvalidCollectionType, GetClrTypeFullName(type)), param); deserializationExceptionMessage = GetInvalidCollectionMessage(message, SR.Format(SR.ReadOnlyCollectionDeserialization, GetClrTypeFullName(type)), param); } @@ -1435,36 +1322,64 @@ private static bool IsKnownInterface(Type type) return false; } - internal override DataContract GetValidContract(SerializationMode mode) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) { - if (InvalidCollectionInSharedContractMessage != null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(InvalidCollectionInSharedContractMessage)); + DataContract boundContract; + if (boundContracts != null && boundContracts.TryGetValue(this, out boundContract!)) + return boundContract; - return this; + // This type-binding ('boundType') stuff is new. We did not do this in NetFx. We used to use default contract constructors and let the + // underlying type get filled in later. But default constructors for DataContracts runs afoul of requiring an underlying type. Our web of nullable + // notations make it hard to get around. But it also allows us to feel good about using .UnderlyingType from matching parameter contracts. + Type type = UnderlyingType; + Type[] paramTypes = type.GetGenericArguments(); + for (int i = 0; i < paramTypes.Length; i++) + { + if (paramTypes[i].IsGenericParameter) + paramTypes[i] = paramContracts[paramTypes[i].GenericParameterPosition].UnderlyingType; + } + Type boundType = type.MakeGenericType(paramTypes); + + CollectionDataContract boundCollectionContract = new CollectionDataContract(boundType); + boundContracts ??= new Dictionary(); + boundContracts.Add(this, boundCollectionContract); + boundCollectionContract.ItemContract = ItemContract.BindGenericParameters(paramContracts, boundContracts); + boundCollectionContract.IsItemTypeNullable = !boundCollectionContract.ItemContract.IsValueType; + boundCollectionContract.ItemName = ItemNameSetExplicit ? ItemName : boundCollectionContract.ItemContract.XmlName.Name; + boundCollectionContract.KeyName = KeyName; + boundCollectionContract.ValueName = ValueName; + boundCollectionContract.XmlName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(XmlName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(UnderlyingType), paramContracts)), + IsCollectionDataContract(UnderlyingType) ? XmlName.Namespace : DataContract.GetCollectionNamespace(boundCollectionContract.ItemContract.XmlName.Namespace)); + return boundCollectionContract; } - internal override DataContract GetValidContract() + internal override DataContract GetValidContract(bool verifyConstructor = false) { - if (this.IsConstructorCheckRequired) + if (verifyConstructor && IsConstructorCheckRequired) { CheckConstructor(); + return this; } + + if (InvalidCollectionInSharedContractMessage != null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(InvalidCollectionInSharedContractMessage)); return this; } private void CheckConstructor() { - if (this.Constructor == null) + if (Constructor == null) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionTypeDoesNotHaveDefaultCtor, DataContract.GetClrTypeFullName(this.UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CollectionTypeDoesNotHaveDefaultCtor, DataContract.GetClrTypeFullName(UnderlyingType)))); } else { - this.IsConstructorCheckRequired = false; + IsConstructorCheckRequired = false; } } - internal override bool IsValidContract(SerializationMode mode) + internal override bool IsValidContract() { return (InvalidCollectionInSharedContractMessage == null); } @@ -1512,7 +1427,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) } return true; } - if (MethodRequiresMemberAccess(this.AddMethod)) + if (MethodRequiresMemberAccess(AddMethod)) { if (securityException != null) { @@ -1520,7 +1435,7 @@ internal bool RequiresMemberAccessForRead(SecurityException? securityException) new SecurityException(SR.Format( SR.PartialTrustCollectionContractAddMethodNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), - this.AddMethod!.Name), + AddMethod!.Name), securityException)); } return true; @@ -1564,8 +1479,31 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) return false; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All ctor's required to create an instance of this type are marked with RequiresUnreferencedCode.")] + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (IsEqualOrChecked(other, checkedContracts)) + return true; + + if (base.Equals(other, checkedContracts)) + { + if (other is CollectionDataContract dataContract) + { + bool thisItemTypeIsNullable = (ItemContract == null) ? false : !ItemContract.IsValueType; + bool otherItemTypeIsNullable = (dataContract.ItemContract == null) ? false : !dataContract.ItemContract.IsValueType; + return ItemName == dataContract.ItemName && + (IsItemTypeNullable || thisItemTypeIsNullable) == (dataContract.IsItemTypeNullable || otherItemTypeIsNullable) && + ItemContract != null && ItemContract.Equals(dataContract.ItemContract, checkedContracts); + } + } + return false; + } + + public override int GetHashCode() => base.GetHashCode(); + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -1575,7 +1513,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); @@ -1614,15 +1552,9 @@ public bool MoveNext() return _enumerator.MoveNext(); } - public KeyValue Current - { - get { return new KeyValue(_enumerator.Key, _enumerator.Value); } - } + public KeyValue Current => new KeyValue(_enumerator.Key, _enumerator.Value); - object System.Collections.IEnumerator.Current - { - get { return Current; } - } + object System.Collections.IEnumerator.Current => Current; public void Reset() { @@ -1658,10 +1590,7 @@ public KeyValue Current } } - object System.Collections.IEnumerator.Current - { - get { return Current; } - } + object System.Collections.IEnumerator.Current => Current; public void Reset() { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs index f859f2f518cef..d61002c23a123 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs @@ -3,39 +3,40 @@ using System; using System.Buffers.Binary; -using System.Collections.Concurrent; +using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; using System.Text; -using System.Text.RegularExpressions; using System.Xml; -using System.Xml.Schema; -using DataContractDictionary = System.Collections.Generic.Dictionary; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { - internal abstract class DataContract + public abstract class DataContract { - private XmlDictionaryString _name; - - private XmlDictionaryString _ns; - - // this the global dictionary for data contracts introduced for multi-file. - private static readonly Dictionary s_dataContracts = new Dictionary(); - internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the " + "required types are preserved."; - public static Dictionary GetDataContracts() - { - return s_dataContracts; - } + internal const DynamicallyAccessedMemberTypes DataContractPreserveMemberTypes = + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicConstructors | + DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicFields | + DynamicallyAccessedMemberTypes.PublicProperties; + + internal static ReadOnlyCollection s_emptyDataMemberList = new List().AsReadOnly(); + private XmlDictionaryString _name; + private XmlDictionaryString _ns; private readonly DataContractCriticalHelper _helper; internal DataContract(DataContractCriticalHelper helper) @@ -45,29 +46,9 @@ internal DataContract(DataContractCriticalHelper helper) _ns = helper.Namespace; } - private static DataContract? GetGeneratedDataContract(Type type) - { - // this method used to be rewritten by an IL transform - // with the restructuring for multi-file, it has become a regular method - DataContract? result; - return s_dataContracts.TryGetValue(type, out result) ? result : null; - } - - internal static bool TryGetDataContractFromGeneratedAssembly(Type type, out DataContract? dataContract) - { - dataContract = GetGeneratedDataContract(type); - return dataContract != null; - } + public virtual string? ContractType => null; - internal static DataContract? GetDataContractFromGeneratedAssembly(Type? type) - { - return null; - } - - internal MethodInfo? ParseMethod - { - get { return _helper.ParseMethod; } - } + internal MethodInfo? ParseMethod => _helper.ParseMethod; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal static DataContract GetDataContract(Type type) @@ -76,24 +57,18 @@ internal static DataContract GetDataContract(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type) - { - return GetDataContract(typeHandle, type, SerializationMode.SharedContract); - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) + internal static DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type? type) { int id = GetId(typeHandle); DataContract dataContract = GetDataContractSkipValidation(id, typeHandle, null); - return dataContract.GetValidContract(mode); + return dataContract.GetValidContract(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle, SerializationMode mode) + internal static DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle) { DataContract dataContract = GetDataContractSkipValidation(id, typeHandle, null); - return dataContract.GetValidContract(mode); + return dataContract.GetValidContract(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -103,10 +78,10 @@ internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHa } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type, SerializationMode mode) + internal static DataContract GetGetOnlyCollectionDataContract(int id, RuntimeTypeHandle typeHandle, Type? type) { DataContract dataContract = GetGetOnlyCollectionDataContractSkipValidation(id, typeHandle, type); - dataContract = dataContract.GetValidContract(mode); + dataContract = dataContract.GetValidContract(); if (dataContract is ClassDataContract) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.Format(SR.ErrorDeserializing, SR.Format(SR.ErrorTypeInfo, DataContract.GetClrTypeFullName(dataContract.UnderlyingType)), SR.Format(SR.NoSetMethodForProperty, string.Empty, string.Empty)))); @@ -136,7 +111,7 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(Type type) + internal static DataContract? GetBuiltInDataContract(Type type) { return DataContractCriticalHelper.GetBuiltInDataContract(type); } @@ -148,7 +123,7 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(string typeName) + internal static DataContract? GetBuiltInDataContract(string typeName) { return DataContractCriticalHelper.GetBuiltInDataContract(typeName); } @@ -169,157 +144,118 @@ internal static void ThrowInvalidDataContractException(string? message, Type? ty DataContractCriticalHelper.ThrowInvalidDataContractException(message, type); } - protected DataContractCriticalHelper Helper - { - get - { return _helper; } - } + internal DataContractCriticalHelper Helper => _helper; [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] - public Type UnderlyingType - { - get - { return _helper.UnderlyingType; } - set { _helper.UnderlyingType = value; } - } + public virtual Type UnderlyingType => _helper.UnderlyingType; - public Type OriginalUnderlyingType - { - get { return _helper.OriginalUnderlyingType; } - set { _helper.OriginalUnderlyingType = value; } - } + public virtual Type OriginalUnderlyingType => _helper.OriginalUnderlyingType; - public virtual bool IsBuiltInDataContract - { - get - { return _helper.IsBuiltInDataContract; } - set { } - } + public virtual bool IsBuiltInDataContract => _helper.IsBuiltInDataContract; - internal Type TypeForInitialization - { - get - { return _helper.TypeForInitialization; } - } + internal Type TypeForInitialization => _helper.TypeForInitialization; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public virtual void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal virtual void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public virtual object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal virtual object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public virtual void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal virtual void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } - public virtual object ReadXmlElement(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) + internal virtual object ReadXmlElement(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) { - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(this.GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, DataContract.GetClrTypeFullName(GetType()), DataContract.GetClrTypeFullName(UnderlyingType)))); } - public bool IsValueType + public virtual bool IsValueType { - get - { return _helper.IsValueType; } - - set - { _helper.IsValueType = value; } + get => _helper.IsValueType; + internal set => _helper.IsValueType = value; } - public bool IsReference + public virtual bool IsReference { - get - { return _helper.IsReference; } - - set - { _helper.IsReference = value; } + get => _helper.IsReference; + internal set => _helper.IsReference = value; } - public XmlQualifiedName StableName + public virtual XmlQualifiedName XmlName { - get - { return _helper.StableName; } - - set - { _helper.StableName = value; } + get => _helper.XmlName; + internal set => _helper.XmlName = value; } - public virtual DataContractDictionary? KnownDataContracts + public virtual DataContract? BaseContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } - - set - { _helper.KnownDataContracts = value; } + get => null; } - public virtual bool IsISerializable + internal GenericInfo? GenericInfo { - get { return _helper.IsISerializable; } - - set { _helper.IsISerializable = value; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => _helper.GenericInfo; + set => _helper.GenericInfo = value; } - public XmlDictionaryString Name + public virtual DataContractDictionary? KnownDataContracts { - get - { return _name; } - set { _name = value; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } - public virtual XmlDictionaryString Namespace + public virtual bool IsISerializable { - get - { return _ns; } - set { _ns = value; } + get => _helper.IsISerializable; + internal set => _helper.IsISerializable = value; } - public virtual bool HasRoot - { - get - { return true; } + internal XmlDictionaryString Name => _name; + + internal virtual XmlDictionaryString Namespace => _ns; - set - { } + internal virtual bool HasRoot + { + get => _helper.HasRoot; + set => _helper.HasRoot = value; } public virtual XmlDictionaryString? TopLevelElementName { - get - { return _helper.TopLevelElementName; } - - set - { _helper.TopLevelElementName = value; } + get => _helper.TopLevelElementName; + internal set => _helper.TopLevelElementName = value; } public virtual XmlDictionaryString? TopLevelElementNamespace { - get - { return _helper.TopLevelElementNamespace; } - - set - { _helper.TopLevelElementNamespace = value; } + get => _helper.TopLevelElementNamespace; + internal set => _helper.TopLevelElementNamespace = value; } - internal virtual bool CanContainReferences - { - get { return true; } - } + internal virtual bool CanContainReferences => true; - internal virtual bool IsPrimitive + internal virtual bool IsPrimitive => false; + + public virtual bool IsDictionaryLike([NotNullWhen(true)] out string? keyName, [NotNullWhen(true)] out string? valueName, [NotNullWhen(true)] out string? itemName) { - get { return false; } + keyName = valueName = itemName = null; + return false; } + public virtual ReadOnlyCollection DataMembers => s_emptyDataMemberList; + internal virtual void WriteRootElement(XmlWriterDelegator writer, XmlDictionaryString name, XmlDictionaryString? ns) { if (object.ReferenceEquals(ns, DictionaryGlobals.SerializationNamespace) && !IsPrimitive) @@ -328,32 +264,36 @@ internal virtual void WriteRootElement(XmlWriterDelegator writer, XmlDictionaryS writer.WriteStartElement(name, ns); } - internal virtual DataContract GetValidContract(SerializationMode mode) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal virtual DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) { return this; } - internal virtual DataContract GetValidContract() + internal virtual DataContract GetValidContract(bool verifyConstructor = false) { return this; } - internal virtual bool IsValidContract(SerializationMode mode) + internal virtual bool IsValidContract() { return true; } internal class DataContractCriticalHelper { - private static readonly Dictionary s_typeToIDCache = new Dictionary(new TypeHandleRefEqualityComparer()); + private static readonly Hashtable s_typeToIDCache = new Hashtable(new HashTableEqualityComparer()); private static DataContract[] s_dataContractCache = new DataContract[32]; private static int s_dataContractID; private static Dictionary? s_typeToBuiltInContract; private static Dictionary? s_nameToBuiltInContract; - private static Dictionary? s_namespaces; + private static Dictionary? s_typeNameToBuiltInContract; + private static Hashtable s_namespaces = new Hashtable(); private static Dictionary? s_clrTypeStrings; private static XmlDictionary? s_clrTypeStringsDictionary; - private static readonly TypeHandleRef s_typeHandleRef = new TypeHandleRef(); + + [ThreadStatic] + private static TypeHandleRef? s_typeHandleRef; private static readonly object s_cacheLock = new object(); private static readonly object s_createDataContractLock = new object(); @@ -362,11 +302,11 @@ internal class DataContractCriticalHelper private static readonly object s_clrTypeStringsLock = new object(); [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] - private Type _underlyingType; + private readonly Type _underlyingType; private Type? _originalUnderlyingType; - private bool _isReference; private bool _isValueType; - private XmlQualifiedName _stableName = null!; // StableName is always set in concrete ctors set except for the "invalid" CollectionDataContract + private GenericInfo? _genericInfo; + private XmlQualifiedName _xmlName = null!; // XmlName is always set in concrete ctors set except for the "invalid" CollectionDataContract private XmlDictionaryString _name = null!; // Name is always set in concrete ctors set except for the "invalid" CollectionDataContract private XmlDictionaryString _ns = null!; // Namespace is always set in concrete ctors set except for the "invalid" CollectionDataContract @@ -385,11 +325,10 @@ internal static DataContract GetDataContractSkipValidation(int id, RuntimeTypeHa if (dataContract == null) { dataContract = CreateDataContract(id, typeHandle, type); - AssignDataContractToId(dataContract, id); } else { - return dataContract.GetValidContract(); + return dataContract.GetValidContract(verifyConstructor: true); } return dataContract; } @@ -401,7 +340,6 @@ internal static DataContract GetGetOnlyCollectionDataContractSkipValidation(int if (dataContract == null) { dataContract = CreateGetOnlyCollectionDataContract(id, typeHandle, type); - s_dataContractCache[id] = dataContract; } return dataContract; } @@ -418,7 +356,7 @@ internal static DataContract GetDataContractForInitialization(int id) internal static int GetIdForInitialization(ClassDataContract classContract) { - int id = DataContract.GetId(classContract.TypeForInitialization!.TypeHandle); + int id = DataContract.GetId(classContract.TypeForInitialization.TypeHandle); if (id < s_dataContractCache.Length && ContractMatches(classContract, s_dataContractCache[id])) { return id; @@ -441,35 +379,45 @@ private static bool ContractMatches(DataContract contract, DataContract cachedCo internal static int GetId(RuntimeTypeHandle typeHandle) { - lock (s_cacheLock) + typeHandle = GetDataContractAdapterTypeHandle(typeHandle); + s_typeHandleRef ??= new TypeHandleRef(); + s_typeHandleRef.Value = typeHandle; + + object? value = s_typeToIDCache[s_typeHandleRef]; + if (value != null) + return ((IntRef)value).Value; + + try { - IntRef? id; - typeHandle = GetDataContractAdapterTypeHandle(typeHandle); - s_typeHandleRef.Value = typeHandle; - if (!s_typeToIDCache.TryGetValue(s_typeHandleRef, out id)) + lock (s_cacheLock) { - int value = s_dataContractID++; - if (value >= s_dataContractCache.Length) + value = s_typeToIDCache[s_typeHandleRef]; + if (value != null) + return ((IntRef)value).Value; + + int nextId = s_dataContractID++; + if (nextId >= s_dataContractCache.Length) { - int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue; - if (newSize <= value) + int newSize = (nextId < int.MaxValue / 2) ? nextId * 2 : int.MaxValue; + if (newSize <= nextId) { DiagnosticUtility.DebugAssert("DataContract cache overflow"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.DataContractCacheOverflow)); } Array.Resize(ref s_dataContractCache, newSize); } - id = new IntRef(value); - try - { - s_typeToIDCache.Add(new TypeHandleRef(typeHandle), id); - } - catch (Exception ex) - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); - } + IntRef id = new IntRef(nextId); + + s_typeToIDCache.Add(new TypeHandleRef(typeHandle), id); + return id.Value; } - return id.Value; + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } @@ -486,16 +434,8 @@ private static DataContract CreateDataContract(int id, RuntimeTypeHandle typeHan if (dataContract == null) { type ??= Type.GetTypeFromHandle(typeHandle)!; - type = UnwrapNullableType(type); - - dataContract = DataContract.GetDataContractFromGeneratedAssembly(type); - if (dataContract != null) - { - AssignDataContractToId(dataContract, id); - return dataContract; - } - dataContract = CreateDataContract(type); + AssignDataContractToId(dataContract, id); } } } @@ -517,18 +457,18 @@ private static DataContract CreateDataContract(Type type) dataContract = new CollectionDataContract(type); else if (type.IsEnum) dataContract = new EnumDataContract(type); + else if (type.IsGenericParameter) + dataContract = new GenericParameterDataContract(type); else if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type)) dataContract = new XmlDataContract(type); - else if (Globals.TypeOfScriptObject_IsAssignableFrom(type)) - dataContract = Globals.CreateScriptObjectClassDataContract(); else { - //if (type.ContainsGenericParameters) - // ThrowInvalidDataContractException(SR.Format(SR.TypeMustNotBeOpenGeneric, type), type); + if (type.IsPointer) + type = Globals.TypeOfReflectionPointer; if (!CollectionDataContract.TryCreate(type, out dataContract)) { - if (!type.IsSerializable && !type.IsDefined(Globals.TypeOfDataContractAttribute, false) && !ClassDataContract.IsNonAttributedTypeValidForSerialization(type) && !ClassDataContract.IsKnownSerializableType(type)) + if (!type.IsSerializable && !type.IsDefined(Globals.TypeOfDataContractAttribute, false) && !ClassDataContract.IsNonAttributedTypeValidForSerialization(type)) { ThrowInvalidDataContractException(SR.Format(SR.TypeNotSerializable, type), type); } @@ -536,10 +476,10 @@ private static DataContract CreateDataContract(Type type) if (type != originalType) { var originalDataContract = new ClassDataContract(originalType); - if (dataContract.StableName != originalDataContract.StableName) + if (dataContract.XmlName != originalDataContract.XmlName) { - // for non-DC types, type adapters will not have the same stable name (contract name). - dataContract.StableName = originalDataContract.StableName; + // for non-DC types, type adapters will not have the same xml name (contract name). + dataContract.XmlName = originalDataContract.XmlName; } } } @@ -574,8 +514,10 @@ private static DataContract CreateGetOnlyCollectionDataContract(int id, RuntimeT { ThrowInvalidDataContractException(SR.Format(SR.TypeNotSerializable, type), type); } + AssignDataContractToId(dataContract, id); } } + // !; // If null after the lookup and creation attempts above, the 'ThrowInvalidDataContractException' kicks in. return dataContract; } @@ -594,10 +536,6 @@ internal static Type GetDataContractAdapterType(Type type) { return Globals.TypeOfMemoryStreamAdapter; } - if (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePair) - { - return Globals.TypeOfKeyValuePairAdapter.MakeGenericType(type.GetGenericArguments()); - } return type; } @@ -629,7 +567,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(Type type) + internal static DataContract? GetBuiltInDataContract(Type type) { if (type.IsInterface && !CollectionDataContract.IsCollectionInterface(type)) type = Globals.TypeOfObject; @@ -638,8 +576,7 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan { s_typeToBuiltInContract ??= new Dictionary(); - DataContract? dataContract; - if (!s_typeToBuiltInContract.TryGetValue(type, out dataContract)) + if (!s_typeToBuiltInContract.TryGetValue(type, out DataContract? dataContract)) { TryCreateBuiltInDataContract(type, out dataContract); s_typeToBuiltInContract.Add(type, dataContract); @@ -649,36 +586,35 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(string name, string ns) + internal static DataContract? GetBuiltInDataContract(string name, string ns) { lock (s_initBuiltInContractsLock) { s_nameToBuiltInContract ??= new Dictionary(); - DataContract? dataContract; XmlQualifiedName qname = new XmlQualifiedName(name, ns); - if (!s_nameToBuiltInContract.TryGetValue(qname, out dataContract)) + if (!s_nameToBuiltInContract.TryGetValue(qname, out DataContract? dataContract)) { - TryCreateBuiltInDataContract(name, ns, out dataContract); - s_nameToBuiltInContract.Add(qname, dataContract); + if (TryCreateBuiltInDataContract(name, ns, out dataContract)) + { + s_nameToBuiltInContract.Add(qname, dataContract); + } } return dataContract; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static DataContract? GetBuiltInDataContract(string typeName) + internal static DataContract? GetBuiltInDataContract(string typeName) { if (!typeName.StartsWith("System.", StringComparison.Ordinal)) return null; lock (s_initBuiltInContractsLock) { - s_nameToBuiltInContract ??= new Dictionary(); + s_typeNameToBuiltInContract ??= new Dictionary(); - DataContract? dataContract; - XmlQualifiedName qname = new XmlQualifiedName(typeName); - if (!s_nameToBuiltInContract.TryGetValue(qname, out dataContract)) + if (!s_typeNameToBuiltInContract.TryGetValue(typeName, out DataContract? dataContract)) { Type? type = null; string name = typeName.Substring(7); @@ -738,14 +674,14 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan if (type != null) TryCreateBuiltInDataContract(type, out dataContract); - s_nameToBuiltInContract.Add(qname, dataContract); + s_typeNameToBuiltInContract.Add(typeName, dataContract); } return dataContract; } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) + internal static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract) { if (type.IsEnum) // Type.GetTypeCode will report Enums as TypeCode.IntXX { @@ -753,7 +689,7 @@ public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] o return false; } dataContract = null; - switch (type.GetTypeCode()) + switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: dataContract = new BooleanDataContract(); @@ -827,7 +763,7 @@ public static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)] o } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public static bool TryCreateBuiltInDataContract(string name, string ns, [NotNullWhen(true)] out DataContract? dataContract) + internal static bool TryCreateBuiltInDataContract(string name, string ns, [NotNullWhen(true)] out DataContract? dataContract) { dataContract = null; if (ns == DictionaryGlobals.SchemaNamespace.Value) @@ -951,23 +887,30 @@ public static bool TryCreateBuiltInDataContract(string name, string ns, [NotNull internal static string GetNamespace(string key) { - lock (s_namespacesLock) + object? value = s_namespaces[key]; + + if (value != null) + return (string)value; + + try { - s_namespaces ??= new Dictionary(); - if (s_namespaces.TryGetValue(key, out string? value)) + lock (s_namespacesLock) { - return value; - } + value = s_namespaces[key]; + + if (value != null) + return (string)value; - try - { s_namespaces.Add(key, key); + return key; } - catch (Exception ex) - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); - } - return key; + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } @@ -985,11 +928,13 @@ internal static XmlDictionaryString GetClrTypeString(string key) } catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } - XmlDictionaryString? value; - if (s_clrTypeStrings.TryGetValue(key, out value)) + if (s_clrTypeStrings.TryGetValue(key, out XmlDictionaryString? value)) return value; value = s_clrTypeStringsDictionary!.Add(key); try @@ -998,6 +943,9 @@ internal static XmlDictionaryString GetClrTypeString(string key) } catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } return value; @@ -1011,14 +959,15 @@ internal static void ThrowInvalidDataContractException(string? message, Type? ty { lock (s_cacheLock) { + s_typeHandleRef ??= new TypeHandleRef(); s_typeHandleRef.Value = GetDataContractAdapterTypeHandle(type.TypeHandle); - try - { - s_typeToIDCache.Remove(s_typeHandleRef); - } - catch (Exception ex) + + if (s_typeToIDCache.ContainsKey(s_typeHandleRef)) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); + lock (s_cacheLock) + { + s_typeToIDCache.Remove(s_typeHandleRef); + } } } } @@ -1036,95 +985,78 @@ internal DataContractCriticalHelper( } [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] - internal Type UnderlyingType - { - get { return _underlyingType; } - set { _underlyingType = value; } - } + internal Type UnderlyingType => _underlyingType; - internal Type OriginalUnderlyingType - { - get => _originalUnderlyingType ??= GetDataContractOriginalType(this._underlyingType); - set => _originalUnderlyingType = value; - } + internal Type OriginalUnderlyingType => _originalUnderlyingType ??= GetDataContractOriginalType(_underlyingType); - internal virtual bool IsBuiltInDataContract - { - get - { - return false; - } - } + internal virtual bool IsBuiltInDataContract => false; - internal Type TypeForInitialization - { - get { return _typeForInitialization; } - } + internal Type TypeForInitialization => _typeForInitialization; [MemberNotNull(nameof(_typeForInitialization))] private void SetTypeForInitialization(Type classType) { + // TODO - This 'if' was not commented out in 4.8. But 4.8 was not dealing with nullable notations, which we do have here in Core. + // With the absence of schema importing, it does not make sense to have a data contract without a valid serializable underlying type. (Even + // with schema importing it doesn't make sense, but there is a building period while we're still figuring out all the data types and contracts + // where the underlying type may be null.) Anyway... might it make sense to re-instate this if clause - but use it to throw an exception if + // we don't meet the criteria? That way we can maintain nullable semantics and not do anything silly trying to keep them simple. //if (classType.IsSerializable || classType.IsDefined(Globals.TypeOfDataContractAttribute, false)) { _typeForInitialization = classType; } } - internal bool IsReference + internal bool IsReference { get; set; } + + internal bool IsValueType { - get { return _isReference; } - set - { - _isReference = value; - } + get => _isValueType; + set => _isValueType = value; } - internal bool IsValueType + internal XmlQualifiedName XmlName { - get { return _isValueType; } - set { _isValueType = value; } + get => _xmlName; + set => _xmlName = value; } - internal XmlQualifiedName StableName + internal GenericInfo? GenericInfo { - get { return _stableName; } - set { _stableName = value; } + get => _genericInfo; + set => _genericInfo = value; } internal virtual DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get { return null; } + get => null; set { /* do nothing */ } } internal virtual bool IsISerializable { - get { return false; } - set { ThrowInvalidDataContractException(SR.RequiresClassDataContractToSetIsISerializable); } + get => false; + set => ThrowInvalidDataContractException(SR.RequiresClassDataContractToSetIsISerializable); } internal XmlDictionaryString Name { - get { return _name; } - set { _name = value; } + get => _name; + set => _name = value; } - public XmlDictionaryString Namespace + internal XmlDictionaryString Namespace { - get { return _ns; } - set { _ns = value; } + get => _ns; + set => _ns = value; } - internal virtual bool HasRoot - { - get { return true; } - set { } - } + internal virtual bool HasRoot { get; set; } = true; internal virtual XmlDictionaryString? TopLevelElementName { - get { return _name; } + get => _name; set { Debug.Assert(value != null); @@ -1134,7 +1066,7 @@ internal virtual XmlDictionaryString? TopLevelElementName internal virtual XmlDictionaryString? TopLevelElementNamespace { - get { return _ns; } + get => _ns; set { Debug.Assert(value != null); @@ -1142,15 +1074,9 @@ internal virtual XmlDictionaryString? TopLevelElementNamespace } } - internal virtual bool CanContainReferences - { - get { return true; } - } + internal virtual bool CanContainReferences => true; - internal virtual bool IsPrimitive - { - get { return false; } - } + internal virtual bool IsPrimitive => false; internal MethodInfo? ParseMethod { @@ -1171,19 +1097,19 @@ internal MethodInfo? ParseMethod } } - internal void SetDataContractName(XmlQualifiedName stableName) + internal void SetDataContractName(XmlQualifiedName xmlName) { XmlDictionary dictionary = new XmlDictionary(2); - this.Name = dictionary.Add(stableName.Name); - this.Namespace = dictionary.Add(stableName.Namespace); - this.StableName = stableName; + Name = dictionary.Add(xmlName.Name); + Namespace = dictionary.Add(xmlName.Namespace); + XmlName = xmlName; } internal void SetDataContractName(XmlDictionaryString name, XmlDictionaryString ns) { - this.Name = name; - this.Namespace = ns; - this.StableName = CreateQualifiedName(name.Value, ns.Value); + Name = name; + Namespace = ns; + XmlName = CreateQualifiedName(name.Value, ns.Value); } [DoesNotReturn] @@ -1194,10 +1120,14 @@ internal void ThrowInvalidDataContractException(string message) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static bool IsTypeSerializable(Type type, HashSet? previousCollectionTypes = null) + internal static bool IsTypeSerializable(Type type) { - Type? itemType; + return IsTypeSerializable(type, null); + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private static bool IsTypeSerializable(Type type, HashSet? previousCollectionTypes) + { if (type.IsSerializable || type.IsEnum || type.IsDefined(Globals.TypeOfDataContractAttribute, false) || @@ -1209,7 +1139,7 @@ internal static bool IsTypeSerializable(Type type, HashSet? previousCollec { return true; } - if (CollectionDataContract.IsCollection(type, out itemType)) + if (CollectionDataContract.IsCollection(type, out Type? itemType)) { previousCollectionTypes ??= new HashSet(); ValidatePreviousCollectionTypes(type, itemType, previousCollectionTypes); @@ -1229,9 +1159,34 @@ private static void ValidatePreviousCollectionTypes(Type collectionType, Type it { itemType = itemType.GetElementType()!; } - if (previousCollectionTypes.Contains(itemType)) + + // Do a breadth first traversal of the generic type tree to + // produce the closure of all generic argument types and + // check that none of these is in the previousCollectionTypes + List itemTypeClosure = new List(); + Queue itemTypeQueue = new Queue(); + + itemTypeQueue.Enqueue(itemType); + itemTypeClosure.Add(itemType); + + while (itemTypeQueue.Count > 0) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.RecursiveCollectionType, GetClrTypeFullName(itemType)))); + itemType = itemTypeQueue.Dequeue(); + if (previousCollectionTypes.Contains(itemType)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.RecursiveCollectionType, GetClrTypeFullName(itemType)))); + } + if (itemType.IsGenericType) + { + foreach (Type argType in itemType.GetGenericArguments()) + { + if (!itemTypeClosure.Contains(argType)) + { + itemTypeQueue.Enqueue(argType); + itemTypeClosure.Add(argType); + } + } + } } } @@ -1290,47 +1245,47 @@ internal static bool IsValidNCName(string name) { return false; } - catch (Exception) - { - return false; - } } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetStableName(Type type) + public static XmlQualifiedName GetXmlName(Type type) + { + return GetXmlName(type, out _); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static XmlQualifiedName GetXmlName(Type type, out bool hasDataContract) { - return GetStableName(type, out _); + return GetXmlName(type, new HashSet(), out hasDataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetStableName(Type type, out bool hasDataContract) + internal static XmlQualifiedName GetXmlName(Type type, HashSet previousCollectionTypes, out bool hasDataContract) { type = UnwrapRedundantNullableType(type); - XmlQualifiedName? stableName; - if (TryGetBuiltInXmlAndArrayTypeStableName(type, out stableName)) + if (TryGetBuiltInXmlAndArrayTypeXmlName(type, previousCollectionTypes, out XmlQualifiedName? xmlName)) { hasDataContract = false; } else { - DataContractAttribute? dataContractAttribute; - if (TryGetDCAttribute(type, out dataContractAttribute)) + if (TryGetDCAttribute(type, out DataContractAttribute? dataContractAttribute)) { - stableName = GetDCTypeStableName(type, dataContractAttribute); + xmlName = GetDCTypeXmlName(type, dataContractAttribute); hasDataContract = true; } else { - stableName = GetNonDCTypeStableName(type); + xmlName = GetNonDCTypeXmlName(type, previousCollectionTypes); hasDataContract = false; } } - return stableName; + return xmlName; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttribute dataContractAttribute) + private static XmlQualifiedName GetDCTypeXmlName(Type type, DataContractAttribute dataContractAttribute) { string? name, ns; if (dataContractAttribute.IsNameSetExplicitly) @@ -1343,7 +1298,7 @@ private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttri name = DataContract.EncodeLocalName(name); } else - name = GetDefaultStableLocalName(type); + name = GetDefaultXmlLocalName(type); if (dataContractAttribute.IsNamespaceSetExplicitly) { @@ -1359,14 +1314,16 @@ private static XmlQualifiedName GetDCTypeStableName(Type type, DataContractAttri } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static XmlQualifiedName GetNonDCTypeStableName(Type type) + private static XmlQualifiedName GetNonDCTypeXmlName(Type type, HashSet previousCollectionTypes) { string? name, ns; - Type? itemType; - if (CollectionDataContract.IsCollection(type, out itemType)) - return GetCollectionStableName(type, itemType, out _); - name = GetDefaultStableLocalName(type); + if (CollectionDataContract.IsCollection(type, out Type? itemType)) + { + ValidatePreviousCollectionTypes(type, itemType, previousCollectionTypes); + return GetCollectionXmlName(type, itemType, previousCollectionTypes, out _); + } + name = GetDefaultXmlLocalName(type); // ensures that ContractNamespaceAttribute is honored when used with non-attributed types if (ClassDataContract.IsNonAttributedTypeValidForSerialization(type)) @@ -1375,32 +1332,33 @@ private static XmlQualifiedName GetNonDCTypeStableName(Type type) } else { - ns = GetDefaultStableNamespace(type); + ns = GetDefaultXmlNamespace(type); } return CreateQualifiedName(name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool TryGetBuiltInXmlAndArrayTypeStableName(Type type, [NotNullWhen(true)] out XmlQualifiedName? stableName) + private static bool TryGetBuiltInXmlAndArrayTypeXmlName(Type type, HashSet previousCollectionTypes, [NotNullWhen(true)] out XmlQualifiedName? xmlName) { - stableName = null; + xmlName = null; DataContract? builtInContract = GetBuiltInDataContract(type); if (builtInContract != null) { - stableName = builtInContract.StableName; + xmlName = builtInContract.XmlName; } else if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type)) { - XmlQualifiedName xmlTypeStableName; - SchemaExporter.GetXmlTypeInfo(type, out xmlTypeStableName, out _, out _); - stableName = xmlTypeStableName; + SchemaExporter.GetXmlTypeInfo(type, out XmlQualifiedName xmlTypeName, out _, out _); + xmlName = xmlTypeName; } else if (type.IsArray) { - stableName = GetCollectionStableName(type, type.GetElementType()!, out _); + Type itemType = type.GetElementType()!; + ValidatePreviousCollectionTypes(type, itemType, previousCollectionTypes); + xmlName = GetCollectionXmlName(type, itemType, previousCollectionTypes, out _); } - return stableName != null; + return xmlName != null; } internal static bool TryGetDCAttribute(Type type, [NotNullWhen(true)] out DataContractAttribute? dataContractAttribute) @@ -1421,7 +1379,13 @@ internal static bool TryGetDCAttribute(Type type, [NotNullWhen(true)] out DataCo } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemType, out CollectionDataContractAttribute? collectionContractAttribute) + internal static XmlQualifiedName GetCollectionXmlName(Type type, Type itemType, out CollectionDataContractAttribute? collectionContractAttribute) + { + return GetCollectionXmlName(type, itemType, new HashSet(), out collectionContractAttribute); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static XmlQualifiedName GetCollectionXmlName(Type type, Type itemType, HashSet previousCollectionTypes, out CollectionDataContractAttribute? collectionContractAttribute) { string? name, ns; object[] collectionContractAttributes = type.GetCustomAttributes(Globals.TypeOfCollectionDataContractAttribute, false).ToArray(); @@ -1442,7 +1406,7 @@ internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemTyp name = DataContract.EncodeLocalName(name); } else - name = GetDefaultStableLocalName(type); + name = GetDefaultXmlLocalName(type); if (collectionContractAttribute.IsNamespaceSetExplicitly) { @@ -1458,9 +1422,9 @@ internal static XmlQualifiedName GetCollectionStableName(Type type, Type itemTyp { collectionContractAttribute = null; string arrayOfPrefix = Globals.ArrayPrefix + GetArrayPrefix(ref itemType); - XmlQualifiedName elementStableName = GetStableName(itemType); - name = arrayOfPrefix + elementStableName.Name; - ns = GetCollectionNamespace(elementStableName.Namespace); + XmlQualifiedName elementXmlName = GetXmlName(itemType, previousCollectionTypes, out _); + name = arrayOfPrefix + elementXmlName.Name; + ns = GetCollectionNamespace(elementXmlName.Namespace); } return CreateQualifiedName(name, ns); } @@ -1485,13 +1449,33 @@ internal static string GetCollectionNamespace(string elementNs) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static XmlQualifiedName GetDefaultStableName(Type type) + public virtual XmlQualifiedName GetArrayTypeName(bool isNullable) + { + XmlQualifiedName itemName; + if (IsValueType && isNullable) + { + GenericInfo genericInfo = new GenericInfo(DataContract.GetXmlName(Globals.TypeOfNullable), Globals.TypeOfNullable.FullName!); + genericInfo.Add(new GenericInfo(XmlName, null)); + genericInfo.AddToLevel(0, 1); + itemName = genericInfo.GetExpandedXmlName(); + } + else + { + itemName = XmlName; + } + string ns = GetCollectionNamespace(itemName.Namespace); + string name = Globals.ArrayPrefix + itemName.Name; + return new XmlQualifiedName(name, ns); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static XmlQualifiedName GetDefaultXmlName(Type type) { - return CreateQualifiedName(GetDefaultStableLocalName(type), GetDefaultStableNamespace(type)); + return CreateQualifiedName(GetDefaultXmlLocalName(type), GetDefaultXmlNamespace(type)); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static string GetDefaultStableLocalName(Type type) + private static string GetDefaultXmlLocalName(Type type) { if (type.IsGenericParameter) return "{" + type.GenericParameterPosition + "}"; @@ -1528,7 +1512,7 @@ private static string GetDefaultStableLocalName(Type type) localName.Append('{').Append(i).Append('}'); else { - XmlQualifiedName qname = DataContract.GetStableName(genParam); + XmlQualifiedName qname = DataContract.GetXmlName(genParam); localName.Append(qname.Name); namespaces.Append(' ').Append(qname.Namespace); if (parametersFromBuiltInNamespaces) @@ -1557,7 +1541,7 @@ private static string GetDefaultDataContractNamespace(Type type) if (ns == null) { - ns = GetDefaultStableNamespace(type); + ns = GetDefaultXmlNamespace(type); } else { @@ -1582,7 +1566,7 @@ internal static List GetDataContractNameForGenericName(string typeName, Str if (localName != null) { string tempLocalName = typeName.Substring(startIndex, endIndex - startIndex); - localName.Append((tempLocalName.Equals("KeyValuePairAdapter") ? "KeyValuePair" : tempLocalName)); + localName.Append(tempLocalName); } while ((startIndex = typeName.IndexOf('.', startIndex + 1, endIndex - startIndex - 1)) >= 0) nestedParamCounts.Add(0); @@ -1604,11 +1588,11 @@ internal static bool IsBuiltInNamespace(string ns) return (ns == Globals.SchemaNamespace || ns == Globals.SerializationNamespace); } - internal static string GetDefaultStableNamespace(Type type) + internal static string GetDefaultXmlNamespace(Type type) { if (type.IsGenericParameter) return "{ns}"; - return GetDefaultStableNamespace(type.Namespace); + return GetDefaultXmlNamespace(type.Namespace); } internal static XmlQualifiedName CreateQualifiedName(string localName, string ns) @@ -1616,27 +1600,27 @@ internal static XmlQualifiedName CreateQualifiedName(string localName, string ns return new XmlQualifiedName(localName, GetNamespace(ns)); } - internal static string GetDefaultStableNamespace(string? clrNs) + internal static string GetDefaultXmlNamespace(string? clrNs) { return new Uri(Globals.DataContractXsdBaseNamespaceUri, clrNs ?? string.Empty).AbsoluteUri; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static void GetDefaultStableName(string fullTypeName, out string localName, out string ns) + internal static void GetDefaultXmlName(string fullTypeName, out string localName, out string ns) { CodeTypeReference typeReference = new CodeTypeReference(fullTypeName); - GetDefaultStableName(typeReference, out localName, out ns); + GetDefaultName(typeReference, out localName, out ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static void GetDefaultStableName(CodeTypeReference typeReference, out string localName, out string ns) + private static void GetDefaultName(CodeTypeReference typeReference, out string localName, out string ns) { string fullTypeName = typeReference.BaseType; DataContract? dataContract = GetBuiltInDataContract(fullTypeName); if (dataContract != null) { - localName = dataContract.StableName.Name; - ns = dataContract.StableName.Namespace; + localName = dataContract.XmlName.Name; + ns = dataContract.XmlName.Namespace; return; } @@ -1649,8 +1633,7 @@ private static void GetDefaultStableName(CodeTypeReference typeReference, out st List nestedParamCounts = GetDataContractNameForGenericName(localName, localNameBuilder); foreach (CodeTypeReference typeArg in typeReference.TypeArguments) { - string typeArgName, typeArgNs; - GetDefaultStableName(typeArg, out typeArgName, out typeArgNs); + GetDefaultName(typeArg, out string typeArgName, out string typeArgNs); localNameBuilder.Append(typeArgName); argNamespacesBuilder.Append(' ').Append(typeArgNs); if (parametersFromBuiltInNamespaces) @@ -1663,7 +1646,7 @@ private static void GetDefaultStableName(CodeTypeReference typeReference, out st { foreach (int count in nestedParamCounts) { - argNamespacesBuilder.Insert(0, count).Insert(0, ' '); + argNamespacesBuilder.Insert(0, count.ToString(CultureInfo.InvariantCulture)).Insert(0, ' '); } localNameBuilder.Append(GetNamespacesDigest(argNamespacesBuilder.ToString())); @@ -1673,7 +1656,7 @@ private static void GetDefaultStableName(CodeTypeReference typeReference, out st } localName = DataContract.EncodeLocalName(localName); - ns = GetDefaultStableNamespace(ns); + ns = GetDefaultXmlNamespace(ns); } private static void CheckExplicitDataContractNamespaceUri(string dataContractNs, Type type) @@ -1686,8 +1669,7 @@ private static void CheckExplicitDataContractNamespaceUri(string dataContractNs, ThrowInvalidDataContractException(SR.Format(SR.DataContractNamespaceIsNotValid, dataContractNs), type); dataContractNs = trimmedNs; } - Uri? uri; - if (Uri.TryCreate(dataContractNs, UriKind.RelativeOrAbsolute, out uri)) + if (Uri.TryCreate(dataContractNs, UriKind.RelativeOrAbsolute, out Uri? uri)) { if (uri.ToString() == Globals.SerializationNamespace) ThrowInvalidDataContractException(SR.Format(SR.DataContractNamespaceReserved, Globals.SerializationNamespace), type); @@ -1933,8 +1915,7 @@ internal static string ExpandGenericParameters(string format, IGenericNameProvid } else { - int paramIndex; - if (!int.TryParse(format.AsSpan(start, i - start), out paramIndex) || paramIndex < 0 || paramIndex >= genericNameProvider.GetParameterCount()) + if (!int.TryParse(format.AsSpan(start, i - start), out int paramIndex) || paramIndex < 0 || paramIndex >= genericNameProvider.GetParameterCount()) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericParameterNotValid, format.Substring(start, i - start), genericNameProvider.GetGenericTypeName(), genericNameProvider.GetParameterCount() - 1))); typeName.Append(genericNameProvider.GetParameterName(paramIndex)); } @@ -2030,25 +2011,36 @@ private static void ImportKnownTypeAttributes(Type? type, Dictionary } } - //For Json we need to add KeyValuePair to KnownTypes if the UnderLyingType is a Dictionary - try + // After careful consideration, I don't think this code is necessary anymore. After trying to + // decipher the intent of the comments here, my best guess is that this was DCJS's way of working + // around a non-[Serializable] KVP in Silverlight/early .Net Core. The regular DCS went with a + // KVPAdapter approach. Neither is needed now. + // + // But this code does produce additional artifacts in schema handling. To be cautious, just in case + // somebody needs a KVP contract in addition to the KV contract in their schema, I've kept this + // here behind this AppContext switch. + AppContext.TryGetSwitch("Switch.System.Runtime.Serialization.DataContracts.Auto_Import_KVP", out bool autoImportKVP); + if (autoImportKVP) { - CollectionDataContract? collectionDataContract = DataContract.GetDataContract(type) as CollectionDataContract; - if (collectionDataContract != null && collectionDataContract.IsDictionary && - collectionDataContract.ItemType.GetGenericTypeDefinition() == Globals.TypeOfKeyValue) + //For Json we need to add KeyValuePair to KnownTypes if the UnderLyingType is a Dictionary + try { - DataContract itemDataContract = DataContract.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionDataContract.ItemType.GetGenericArguments())); - knownDataContracts ??= new DataContractDictionary(); + if (DataContract.GetDataContract(type) is CollectionDataContract collectionDataContract && collectionDataContract.IsDictionary && + collectionDataContract.ItemType.GetGenericTypeDefinition() == Globals.TypeOfKeyValue) + { + DataContract itemDataContract = DataContract.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionDataContract.ItemType.GetGenericArguments())); + knownDataContracts ??= new DataContractDictionary(); - knownDataContracts.TryAdd(itemDataContract.StableName, itemDataContract); + knownDataContracts.TryAdd(itemDataContract.XmlName, itemDataContract); + } + } + catch (InvalidDataContractException) + { + //Ignore any InvalidDataContractException as this phase is a workaround for lack of ISerializable. + //InvalidDataContractException may happen as we walk the type hierarchy back to Object and encounter + //types that may not be valid DC. This step is purely for KeyValuePair and shouldn't fail the (de)serialization. + //Any IDCE in this case fails the serialization/deserialization process which is not the optimal experience. } - } - catch (InvalidDataContractException) - { - //Ignore any InvalidDataContractException as this phase is a workaround for lack of ISerializable. - //InvalidDataContractException may happen as we walk the type hierarchy back to Object and encounter - //types that may not be valid DC. This step is purely for KeyValuePair and shouldn't fail the (de)serialization. - //Any IDCE in this case fails the serialization/deserialization process which is not the optimal experience. } type = type.BaseType; @@ -2060,23 +2052,62 @@ internal static void CheckAndAdd(Type type, Dictionary typesChecked, { type = DataContract.UnwrapNullableType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContract? alreadyExistingContract; if (nameToDataContractTable == null) { nameToDataContractTable = new DataContractDictionary(); } - else if (nameToDataContractTable.TryGetValue(dataContract.StableName, out alreadyExistingContract)) + else if (nameToDataContractTable.TryGetValue(dataContract.XmlName, out DataContract? alreadyExistingContract)) { + // The alreadyExistingContract type was used as-is in NetFx. The call to get the appropriate adapter type was added in CoreFx with https://github.com/dotnet/runtime/commit/50c0a70c52fa66fafa1227be552ccdab5e4cf8e4 // Don't throw duplicate if its a KeyValuePair as it could have been added by Dictionary - if (DataContractCriticalHelper.GetDataContractAdapterType(alreadyExistingContract.UnderlyingType) != DataContractCriticalHelper.GetDataContractAdapterType(type) && - !(alreadyExistingContract is ClassDataContract && ((ClassDataContract)alreadyExistingContract).IsKeyValuePairAdapter)) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInKnownTypes, type, alreadyExistingContract.UnderlyingType, dataContract.StableName.Namespace, dataContract.StableName.Name))); + if (DataContractCriticalHelper.GetDataContractAdapterType(alreadyExistingContract.UnderlyingType) != DataContractCriticalHelper.GetDataContractAdapterType(type)) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInKnownTypes, type, alreadyExistingContract.UnderlyingType, dataContract.XmlName.Namespace, dataContract.XmlName.Name))); return; } - nameToDataContractTable.Add(dataContract.StableName, dataContract); + nameToDataContractTable.Add(dataContract.XmlName, dataContract); ImportKnownTypeAttributes(type, typesChecked, ref nameToDataContractTable); } + public sealed override bool Equals(object? obj) + { + if ((object)this == obj) + return true; + return Equals(obj, new HashSet()); + } + + internal virtual bool Equals(object? other, HashSet? checkedContracts) + { + if (other is DataContract dataContract) + { + return (XmlName.Name == dataContract.XmlName.Name && XmlName.Namespace == dataContract.XmlName.Namespace && IsReference == dataContract.IsReference); + } + return false; + } + + internal bool IsEqualOrChecked(object? other, HashSet? checkedContracts) + { + if (other == null) + return false; + + if ((object)this == other) + return true; + + if (checkedContracts != null) + { + DataContractPairKey contractPairKey = new DataContractPairKey(this, other); + if (checkedContracts.Contains(contractPairKey)) + return true; + checkedContracts.Add(contractPairKey); + } + + return false; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + /// /// Review - checks type visibility to calculate if access to it requires MemberAccessPermission. /// since this information is used to determine whether to give the generated code access @@ -2084,18 +2115,46 @@ internal static void CheckAndAdd(Type type, Dictionary typesChecked, /// internal static bool IsTypeVisible(Type t) { - if (!t.IsVisible && !IsTypeVisibleInSerializationModule(t)) + // Generic parameters are always considered visible. + if (t.IsGenericParameter) + { + return true; + } + + // The normal Type.IsVisible check requires all nested types to be IsNestedPublic. + // This does not comply with our convention where they can also have InternalsVisibleTo + // with our assembly. The following method performs a recursive walk back the declaring + // type hierarchy to perform this enhanced IsVisible check. + if (!IsTypeAndDeclaringTypeVisible(t)) return false; foreach (Type genericType in t.GetGenericArguments()) { - if (!genericType.IsGenericParameter && !IsTypeVisible(genericType)) + if (!IsTypeVisible(genericType)) return false; } return true; } + internal static bool IsTypeAndDeclaringTypeVisible(Type t) + { + // Arrays, etc. must consider the underlying element type because the + // non-element type does not reflect the same type nesting. For example, + // MyClass[] would not show as a nested type, even when MyClass is nested. + if (t.HasElementType) + { + return IsTypeVisible(t.GetElementType()!); + } + + // Nested types are not visible unless their declaring type is visible. + // Additionally, they must be either IsNestedPublic or in an assembly with InternalsVisibleTo this current assembly. + // Non-nested types must be public or have this same InternalsVisibleTo relation. + return t.IsNested + ? (t.IsNestedPublic || IsTypeVisibleInSerializationModule(t)) && IsTypeVisible(t.DeclaringType!) + : t.IsPublic || IsTypeVisibleInSerializationModule(t); + } + /// /// Review - checks constructor visibility to calculate if access to it requires MemberAccessPermission. /// note: does local check for visibility, assuming that the declaring Type visibility has been checked. @@ -2200,7 +2259,7 @@ internal interface IGenericNameProvider string GetParameterName(int paramIndex); [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] string GetNamespaces(); - string GetGenericTypeName(); + string? GetGenericTypeName(); bool ParametersFromBuiltInNamespaces { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -2224,8 +2283,7 @@ internal GenericNameProvider(string genericTypeName, object[] genericParams) _genericParams = new object[genericParams.Length]; genericParams.CopyTo(_genericParams, 0); - string name; - DataContract.GetClrNameAndNamespace(genericTypeName, out name, out _); + DataContract.GetClrNameAndNamespace(genericTypeName, out string name, out _); _nestedParamCounts = DataContract.GetDataContractNameForGenericName(name, null); } @@ -2242,7 +2300,7 @@ public IList GetNestedParameterCounts() [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public string GetParameterName(int paramIndex) { - return GetStableName(paramIndex).Name; + return GetXmlName(paramIndex).Name; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -2250,11 +2308,11 @@ public string GetNamespaces() { StringBuilder namespaces = new StringBuilder(); for (int j = 0; j < GetParameterCount(); j++) - namespaces.Append(' ').Append(GetStableName(j).Namespace); + namespaces.Append(' ').Append(GetXmlName(j).Namespace); return namespaces.ToString(); } - public string GetGenericTypeName() + public string? GetGenericTypeName() { return _genericTypeName; } @@ -2268,7 +2326,7 @@ public bool ParametersFromBuiltInNamespaces for (int j = 0; j < GetParameterCount(); j++) { if (parametersFromBuiltInNamespaces) - parametersFromBuiltInNamespaces = DataContract.IsBuiltInNamespace(GetStableName(j).Namespace); + parametersFromBuiltInNamespaces = DataContract.IsBuiltInNamespace(GetXmlName(j).Namespace); else break; } @@ -2277,7 +2335,7 @@ public bool ParametersFromBuiltInNamespaces } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private XmlQualifiedName GetStableName(int i) + private XmlQualifiedName GetXmlName(int i) { object o = _genericParams[i]; XmlQualifiedName? qname = o as XmlQualifiedName; @@ -2285,15 +2343,158 @@ private XmlQualifiedName GetStableName(int i) { Type? paramType = o as Type; if (paramType != null) - _genericParams[i] = qname = DataContract.GetStableName(paramType); + _genericParams[i] = qname = DataContract.GetXmlName(paramType); else - _genericParams[i] = qname = ((DataContract)o).StableName; + _genericParams[i] = qname = ((DataContract)o).XmlName; } return qname; } } + internal sealed class GenericInfo : IGenericNameProvider + { + private string? _genericTypeName; + private XmlQualifiedName _xmlName; + private List? _paramGenericInfos; + private List _nestedParamCounts; + + internal GenericInfo(XmlQualifiedName xmlName, string? genericTypeName) + { + _xmlName = xmlName; + _genericTypeName = genericTypeName; + _nestedParamCounts = new List(); + _nestedParamCounts.Add(0); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public XmlQualifiedName GetExpandedXmlName() + { + if (_paramGenericInfos == null) + return _xmlName; + return new XmlQualifiedName(DataContract.EncodeLocalName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(_xmlName.Name), this)), _xmlName.Namespace); + } + + public XmlQualifiedName XmlName => _xmlName; + + public IList? Parameters => _paramGenericInfos; + + internal void Add(GenericInfo actualParamInfo) + { + if (_paramGenericInfos == null) + _paramGenericInfos = new List(); + _paramGenericInfos.Add(actualParamInfo); + } + + internal void AddToLevel(int level, int count) + { + if (level >= _nestedParamCounts.Count) + { + do + { + _nestedParamCounts.Add((level == _nestedParamCounts.Count) ? count : 0); + } while (level >= _nestedParamCounts.Count); + } + else + _nestedParamCounts[level] = _nestedParamCounts[level] + count; + } + + internal string GetXmlNamespace() + { + return _xmlName.Namespace; + } + + int IGenericNameProvider.GetParameterCount() + { + return _paramGenericInfos?.Count ?? 0; + } + + IList IGenericNameProvider.GetNestedParameterCounts() + { + return _nestedParamCounts; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + string IGenericNameProvider.GetParameterName(int paramIndex) + { + Debug.Assert(_paramGenericInfos != null); + return _paramGenericInfos[paramIndex].GetExpandedXmlName().Name; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + string IGenericNameProvider.GetNamespaces() + { + if (_paramGenericInfos == null || _paramGenericInfos.Count == 0) + return ""; + + StringBuilder namespaces = new StringBuilder(); + for (int j = 0; j < _paramGenericInfos.Count; j++) + namespaces.Append(' ').Append(_paramGenericInfos[j].GetXmlNamespace()); + return namespaces.ToString(); + } + + string? IGenericNameProvider.GetGenericTypeName() + { + return _genericTypeName; + } + + bool IGenericNameProvider.ParametersFromBuiltInNamespaces + { + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get + { + bool parametersFromBuiltInNamespaces = true; + + if (_paramGenericInfos == null || _paramGenericInfos.Count == 0) + return parametersFromBuiltInNamespaces; + + for (int j = 0; j < _paramGenericInfos.Count; j++) + { + if (parametersFromBuiltInNamespaces) + parametersFromBuiltInNamespaces = DataContract.IsBuiltInNamespace(_paramGenericInfos[j].GetXmlNamespace()); + else + break; + } + return parametersFromBuiltInNamespaces; + } + } + } + + internal sealed class DataContractPairKey + { + private object _object1; + private object _object2; + + internal DataContractPairKey(object object1, object object2) + { + _object1 = object1; + _object2 = object2; + } + + public override bool Equals(object? other) + { + if (other is not DataContractPairKey otherKey) + return false; + return ((otherKey._object1 == _object1 && otherKey._object2 == _object2) || (otherKey._object1 == _object2 && otherKey._object2 == _object1)); + } + + public override int GetHashCode() + { + return _object1.GetHashCode() ^ _object2.GetHashCode(); + } + } + + internal sealed class HashTableEqualityComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(object? x, object? y) + { + return ((TypeHandleRef)x!).Value.Equals(((TypeHandleRef)y!).Value); + } + public int GetHashCode(object obj) + { + return ((TypeHandleRef)obj).Value.GetHashCode(); + } + } internal sealed class TypeHandleRefEqualityComparer : IEqualityComparer { @@ -2323,14 +2524,8 @@ public TypeHandleRef(RuntimeTypeHandle value) public RuntimeTypeHandle Value { - get - { - return _value; - } - set - { - _value = value; - } + get => _value; + set => _value = value; } } @@ -2343,12 +2538,6 @@ public IntRef(int value) _value = value; } - public int Value - { - get - { - return _value; - } - } + public int Value => _value; } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs index 2b9345dc2c5f2..6983923059fb4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractResolver.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs index cd8b099284e33..97596f18babdf 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs @@ -2,18 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; +using System.Runtime.Serialization.DataContracts; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -28,25 +24,25 @@ public sealed class DataContractSerializer : XmlObjectSerializer private bool _ignoreExtensionDataObject; private bool _preserveObjectReferences; private ReadOnlyCollection? _knownTypeCollection; - internal IList? knownTypeList; - internal DataContractDictionary? knownDataContracts; + internal IList? _knownTypeList; + internal DataContractDictionary? _knownDataContracts; private DataContractResolver? _dataContractResolver; private ISerializationSurrogateProvider? _serializationSurrogateProvider; private bool _serializeReadOnlyTypes; - private static SerializationOption _option = IsReflectionBackupAllowed() ? SerializationOption.ReflectionAsBackup : SerializationOption.CodeGenOnly; - private static bool _optionAlreadySet; + private static SerializationOption s_option = IsReflectionBackupAllowed() ? SerializationOption.ReflectionAsBackup : SerializationOption.CodeGenOnly; + private static bool s_optionAlreadySet; internal static SerializationOption Option { - get { return RuntimeFeature.IsDynamicCodeSupported ? _option : SerializationOption.ReflectionOnly; } + get { return RuntimeFeature.IsDynamicCodeSupported ? s_option : SerializationOption.ReflectionOnly; } set { - if (_optionAlreadySet) + if (s_optionAlreadySet) { throw new InvalidOperationException(SR.CannotSetTwice); } - _optionAlreadySet = true; - _option = value; + s_optionAlreadySet = true; + s_option = value; } } @@ -61,22 +57,26 @@ public DataContractSerializer(Type type) } public DataContractSerializer(Type type, IEnumerable? knownTypes) + : this(type, knownTypes, int.MaxValue, false, false) { - Initialize(type, knownTypes, int.MaxValue, false, false, null, false); } - public DataContractSerializer(Type type, string rootName, string rootNamespace) : this(type, rootName, rootNamespace, null) { } public DataContractSerializer(Type type, string rootName, string rootNamespace, IEnumerable? knownTypes) + : this(type, rootName, rootNamespace, knownTypes, int.MaxValue, false, false) { - XmlDictionary dictionary = new XmlDictionary(2); - Initialize(type, dictionary.Add(rootName), dictionary.Add(DataContract.GetNamespace(rootNamespace)), knownTypes, int.MaxValue, false, false, null, false); } + internal DataContractSerializer(Type type, string rootName, string rootNamespace, IEnumerable? knownTypes, int maxItemsInObjectGraph, + bool ignoreExtensionDataObject, bool preserveObjectReferences) + { + XmlDictionary dictionary = new XmlDictionary(2); + Initialize(type, dictionary.Add(rootName), dictionary.Add(DataContract.GetNamespace(rootNamespace)), knownTypes, int.MaxValue, ignoreExtensionDataObject, preserveObjectReferences, null, false); + } public DataContractSerializer(Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace) : this(type, rootName, rootNamespace, null) @@ -88,7 +88,7 @@ public DataContractSerializer(Type type, XmlDictionaryString rootName, XmlDictio Initialize(type, rootName, rootNamespace, knownTypes, int.MaxValue, false, false, null, false); } - internal DataContractSerializer(Type type, IEnumerable knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences) + internal DataContractSerializer(Type type, IEnumerable? knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences) { Initialize(type, knownTypes, maxItemsInObjectGraph, ignoreExtensionDataObject, preserveObjectReferences, null, false); } @@ -96,7 +96,7 @@ internal DataContractSerializer(Type type, IEnumerable knownTypes, int max public DataContractSerializer(Type type, DataContractSerializerSettings? settings) { settings ??= new DataContractSerializerSettings(); - Initialize(type, settings.RootName, settings.RootNamespace, settings.KnownTypes, settings.MaxItemsInObjectGraph, false, + Initialize(type, settings.RootName, settings.RootNamespace, settings.KnownTypes, settings.MaxItemsInObjectGraph, settings.IgnoreExtensionDataObject, settings.PreserveObjectReferences, settings.DataContractResolver, settings.SerializeReadOnlyTypes); } @@ -115,10 +115,10 @@ private void Initialize(Type type, if (knownTypes != null) { - this.knownTypeList = new List(); + _knownTypeList = new List(); foreach (Type knownType in knownTypes) { - this.knownTypeList.Add(knownType); + _knownTypeList.Add(knownType); } } @@ -154,9 +154,9 @@ public ReadOnlyCollection KnownTypes { if (_knownTypeCollection == null) { - if (knownTypeList != null) + if (_knownTypeList != null) { - _knownTypeCollection = new ReadOnlyCollection(knownTypeList); + _knownTypeCollection = new ReadOnlyCollection(_knownTypeList); } else { @@ -172,15 +172,15 @@ internal override DataContractDictionary? KnownDataContracts [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { - if (this.knownDataContracts == null && this.knownTypeList != null) + if (_knownDataContracts == null && _knownTypeList != null) { // This assignment may be performed concurrently and thus is a race condition. // It's safe, however, because at worse a new (and identical) dictionary of // data contracts will be created and re-assigned to this field. Introduction // of a lock here could lead to deadlocks. - this.knownDataContracts = XmlObjectSerializerContext.GetDataContractsForKnownTypes(this.knownTypeList); + _knownDataContracts = XmlObjectSerializerContext.GetDataContractsForKnownTypes(_knownTypeList); } - return this.knownDataContracts; + return _knownDataContracts; } } @@ -354,7 +354,7 @@ internal void InternalWriteObjectContent(XmlWriterDelegator writer, object? grap graph = SurrogateToDataContractType(_serializationSurrogateProvider, graph, declaredType, ref graphType); } - dataContractResolver ??= this.DataContractResolver; + dataContractResolver ??= DataContractResolver; if (graph == null) { @@ -408,7 +408,7 @@ internal static DataContract GetDataContract(DataContract declaredTypeContract, } else { - return DataContract.GetDataContract(objectType.TypeHandle, objectType, SerializationMode.SharedContract); + return DataContract.GetDataContract(objectType.TypeHandle, objectType); } } @@ -433,7 +433,7 @@ internal override void InternalWriteEndObject(XmlWriterDelegator writer) if (MaxItemsInObjectGraph == 0) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ExceededMaxItemsQuota, MaxItemsInObjectGraph))); - dataContractResolver ??= this.DataContractResolver; + dataContractResolver ??= DataContractResolver; if (verifyObjectName) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs index 41bae413f816a..129540177a69d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs @@ -1,48 +1,48 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; using System.Collections; using System.Collections.Generic; -using System.Text; -using DataContractDictionary = System.Collections.Generic.Dictionary; using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Xml; +using System.Xml.Schema; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { - internal sealed class DataContractSet + public sealed class DataContractSet { - private Dictionary? _contracts; + private DataContractDictionary? _contracts; private Dictionary? _processedContracts; - private readonly ICollection _referencedTypes; - private readonly ICollection _referencedCollectionTypes; - -#if SUPPORT_SURROGATE - private IDataContractSurrogate _dataContractSurrogate; - private Hashtable _surrogateDataTable; - - internal DataContractSet(IDataContractSurrogate dataContractSurrogate) : this(dataContractSurrogate, null, null) { } - - internal DataContractSet(IDataContractSurrogate dataContractSurrogate, ICollection referencedTypes, ICollection referencedCollectionTypes) + private readonly ISerializationSurrogateProvider? _surrogateProvider; + private readonly ISerializationSurrogateProvider2? _extendedSurrogateProvider; + private Hashtable? _surrogateData; + private DataContractDictionary? _knownTypesForObject; + private readonly List? _referencedTypes; + private readonly List? _referencedCollectionTypes; + + public DataContractSet(ISerializationSurrogateProvider? dataContractSurrogate, IEnumerable? referencedTypes, IEnumerable? referencedCollectionTypes) { - _dataContractSurrogate = dataContractSurrogate; - _referencedTypes = referencedTypes; - _referencedCollectionTypes = referencedCollectionTypes; + _surrogateProvider = dataContractSurrogate; + _extendedSurrogateProvider = dataContractSurrogate as ISerializationSurrogateProvider2; + _referencedTypes = referencedTypes != null ? new List(referencedTypes) : null; + _referencedCollectionTypes = referencedCollectionTypes != null ? new List(referencedCollectionTypes) : null; } -#endif [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal DataContractSet(DataContractSet dataContractSet) + public DataContractSet(DataContractSet dataContractSet) { ArgumentNullException.ThrowIfNull(dataContractSet); - //this.dataContractSurrogate = dataContractSet.dataContractSurrogate; _referencedTypes = dataContractSet._referencedTypes; _referencedCollectionTypes = dataContractSet._referencedCollectionTypes; + _extendedSurrogateProvider = dataContractSet._extendedSurrogateProvider; - foreach (KeyValuePair pair in dataContractSet) + foreach (KeyValuePair pair in dataContractSet.Contracts) { - Add(pair.Key, pair.Value); + InternalAdd(pair.Key, pair.Value); } if (dataContractSet._processedContracts != null) @@ -54,22 +54,18 @@ internal DataContractSet(DataContractSet dataContractSet) } } - private Dictionary Contracts => - _contracts ??= new Dictionary(); + public DataContractDictionary Contracts => + _contracts ??= new DataContractDictionary(); - private Dictionary ProcessedContracts => + public Dictionary ProcessedContracts => _processedContracts ??= new Dictionary(); -#if SUPPORT_SURROGATE - private Hashtable SurrogateDataTable => _surrogateDataTable ??= new Hashtable(); -#endif + public Hashtable SurrogateData => _surrogateData ??= new Hashtable(); - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void Add(Type type) + public DataContractDictionary? KnownTypesForObject { - DataContract dataContract = GetDataContract(type); - EnsureTypeNotGeneric(dataContract.UnderlyingType); - Add(dataContract); + get => _knownTypesForObject; + internal set => _knownTypesForObject = value; } internal static void EnsureTypeNotGeneric(Type type) @@ -78,14 +74,22 @@ internal static void EnsureTypeNotGeneric(Type type) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericTypeNotExportable, type))); } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal void Add(Type type) + { + DataContract dataContract = GetDataContract(type); + EnsureTypeNotGeneric(dataContract.UnderlyingType); + Add(dataContract); + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void Add(DataContract dataContract) { - Add(dataContract.StableName, dataContract); + Add(dataContract.XmlName, dataContract); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public void Add(XmlQualifiedName name, DataContract dataContract) + internal void Add(XmlQualifiedName name, DataContract dataContract) { if (dataContract.IsBuiltInDataContract) return; @@ -95,17 +99,16 @@ public void Add(XmlQualifiedName name, DataContract dataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) { - DataContract? dataContractInSet; - if (Contracts.TryGetValue(name, out dataContractInSet)) + if (Contracts.TryGetValue(name, out DataContract? dataContractInSet)) { if (!dataContractInSet.Equals(dataContract)) { if (dataContract.UnderlyingType == null || dataContractInSet.UnderlyingType == null) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInDataContractSet, dataContract.StableName.Name, dataContract.StableName.Namespace))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupContractInDataContractSet, dataContract.XmlName.Name, dataContract.XmlName.Namespace))); else { bool typeNamesEqual = (DataContract.GetClrTypeFullName(dataContract.UnderlyingType) == DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)); - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupTypeContractInDataContractSet, (typeNamesEqual ? dataContract.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContract.UnderlyingType)), (typeNamesEqual ? dataContractInSet.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)), dataContract.StableName.Name, dataContract.StableName.Namespace))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DupTypeContractInDataContractSet, (typeNamesEqual ? dataContract.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContract.UnderlyingType)), (typeNamesEqual ? dataContractInSet.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)), dataContract.XmlName.Name, dataContract.XmlName.Namespace))); } } } @@ -113,17 +116,17 @@ internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) { Contracts.Add(name, dataContract); - if (dataContract is ClassDataContract) + if (dataContract is ClassDataContract classDC) { - AddClassDataContract((ClassDataContract)dataContract); + AddClassDataContract(classDC); } - else if (dataContract is CollectionDataContract) + else if (dataContract is CollectionDataContract collectionDC) { - AddCollectionDataContract((CollectionDataContract)dataContract); + AddCollectionDataContract(collectionDC); } - else if (dataContract is XmlDataContract) + else if (dataContract is XmlDataContract xmlDC) { - AddXmlDataContract((XmlDataContract)dataContract); + AddXmlDataContract(xmlDC); } } } @@ -131,9 +134,9 @@ internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void AddClassDataContract(ClassDataContract classDataContract) { - if (classDataContract.BaseContract != null) + if (classDataContract.BaseClassContract != null) { - Add(classDataContract.BaseContract.StableName, classDataContract.BaseContract); + Add(classDataContract.BaseClassContract.XmlName, classDataContract.BaseClassContract); } if (!classDataContract.IsISerializable) { @@ -143,18 +146,18 @@ private void AddClassDataContract(ClassDataContract classDataContract) { DataMember dataMember = classDataContract.Members[i]; DataContract memberDataContract = GetMemberTypeDataContract(dataMember); -#if SUPPORT_SURROGATE - if (_dataContractSurrogate != null && dataMember.MemberInfo != null) + + if (_extendedSurrogateProvider != null && dataMember.MemberInfo != null) { - object customData = DataContractSurrogateCaller.GetCustomDataToExport( - _dataContractSurrogate, - dataMember.MemberInfo, - memberDataContract.UnderlyingType); + object? customData = DataContractSurrogateCaller.GetCustomDataToExport( + _extendedSurrogateProvider, + dataMember.MemberInfo, + memberDataContract.UnderlyingType); if (customData != null) - SurrogateDataTable.Add(dataMember, customData); + SurrogateData.Add(dataMember, customData); } -#endif - Add(memberDataContract.StableName, memberDataContract); + + Add(memberDataContract.XmlName, memberDataContract); } } } @@ -164,16 +167,19 @@ private void AddClassDataContract(ClassDataContract classDataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void AddCollectionDataContract(CollectionDataContract collectionDataContract) { - if (collectionDataContract.IsDictionary) + if (collectionDataContract.UnderlyingType != Globals.TypeOfSchemaDefinedType) { - ClassDataContract keyValueContract = (collectionDataContract.ItemContract as ClassDataContract)!; - AddClassDataContract(keyValueContract); - } - else - { - DataContract itemContract = GetItemTypeDataContract(collectionDataContract); - if (itemContract != null) - Add(itemContract.StableName, itemContract); + if (collectionDataContract.IsDictionary) + { + ClassDataContract keyValueContract = (collectionDataContract.ItemContract as ClassDataContract)!; + AddClassDataContract(keyValueContract); + } + else + { + DataContract itemContract = GetItemTypeDataContract(collectionDataContract); + if (itemContract != null) + Add(itemContract.XmlName, itemContract); + } } AddKnownDataContracts(collectionDataContract.KnownDataContracts); } @@ -187,7 +193,7 @@ private void AddXmlDataContract(XmlDataContract xmlDataContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) { - if (knownDataContracts != null) + if (knownDataContracts?.Count > 0) { foreach (DataContract knownDataContract in knownDataContracts.Values) { @@ -197,88 +203,406 @@ private void AddKnownDataContracts(DataContractDictionary? knownDataContracts) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetDataContract(Type clrType) + internal XmlQualifiedName GetXmlName(Type clrType) + { + if (_surrogateProvider != null) + { + Type dcType = DataContractSurrogateCaller.GetDataContractType(_surrogateProvider, clrType); + return DataContract.GetXmlName(dcType); + } + return DataContract.GetXmlName(clrType); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public DataContract GetDataContract(Type type) { -#if SUPPORT_SURROGATE - if (_dataContractSurrogate == null) - return DataContract.GetDataContract(clrType); -#endif - DataContract? dataContract = DataContract.GetBuiltInDataContract(clrType); + if (_surrogateProvider == null) + return DataContract.GetDataContract(type); + + DataContract? dataContract = DataContract.GetBuiltInDataContract(type); if (dataContract != null) return dataContract; -#if SUPPORT_SURROGATE - Type dcType = DataContractSurrogateCaller.GetDataContractType(_dataContractSurrogate, clrType); - if (clrType.IsValueType != dcType.IsValueType) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.ValueTypeMismatchInSurrogatedType, dcType, clrType))); -#endif - Type dcType = clrType; + Type dcType = DataContractSurrogateCaller.GetDataContractType(_surrogateProvider, type); dataContract = DataContract.GetDataContract(dcType); -#if SUPPORT_SURROGATE - if (!SurrogateDataTable.Contains(dataContract)) + if (_extendedSurrogateProvider != null && !SurrogateData.Contains(dataContract)) { - object customData = DataContractSurrogateCaller.GetCustomDataToExport( - _dataContractSurrogate, clrType, dcType); + object? customData = DataContractSurrogateCaller.GetCustomDataToExport(_extendedSurrogateProvider, type, dcType); if (customData != null) - SurrogateDataTable.Add(dataContract, customData); + SurrogateData.Add(dataContract, customData); + } + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public DataContract? GetDataContract(XmlQualifiedName key) + { + DataContract? dataContract = DataContract.GetBuiltInDataContract(key.Name, key.Namespace); + if (dataContract == null) + { + Contracts.TryGetValue(key, out dataContract); } -#endif return dataContract; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetMemberTypeDataContract(DataMember dataMember) + internal DataContract GetMemberTypeDataContract(DataMember dataMember) { - Type dataMemberType = dataMember.MemberType; - if (dataMember.IsGetOnlyCollection) + if (dataMember.MemberInfo is not Type) { -#if SUPPORT_SURROGATE - if (_dataContractSurrogate != null) + Type dataMemberType = dataMember.MemberType; + if (dataMember.IsGetOnlyCollection) + { + if (_surrogateProvider != null) { - Type dcType = DataContractSurrogateCaller.GetDataContractType(_dataContractSurrogate, dataMemberType); + Type dcType = DataContractSurrogateCaller.GetDataContractType(_surrogateProvider, dataMemberType); if (dcType != dataMemberType) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupported, - DataContract.GetClrTypeFullName(dataMemberType), DataContract.GetClrTypeFullName(dataMember.MemberInfo.DeclaringType), dataMember.MemberInfo.Name))); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.SurrogatesWithGetOnlyCollectionsNotSupported, + DataContract.GetClrTypeFullName(dataMemberType), + (dataMember.MemberInfo.DeclaringType != null) ? DataContract.GetClrTypeFullName(dataMember.MemberInfo.DeclaringType) : dataMember.MemberInfo.DeclaringType, + dataMember.MemberInfo.Name))); } } -#endif - return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(dataMemberType.TypeHandle), dataMemberType.TypeHandle, dataMemberType, SerializationMode.SharedContract); - } - else - { - return GetDataContract(dataMemberType); + return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(dataMemberType.TypeHandle), dataMemberType.TypeHandle, dataMemberType); + } + else + { + return GetDataContract(dataMemberType); + } } + return dataMember.MemberTypeContract; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static DataContract GetItemTypeDataContract(CollectionDataContract collectionContract) + internal DataContract GetItemTypeDataContract(CollectionDataContract collectionContract) { if (collectionContract.ItemType != null) return GetDataContract(collectionContract.ItemType); return collectionContract.ItemContract; } -#if SUPPORT_SURROGATE - internal object GetSurrogateData(object key) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool Remove(XmlQualifiedName key) { - return SurrogateDataTable[key]; + if (DataContract.GetBuiltInDataContract(key.Name, key.Namespace) != null) + return false; + return Contracts.Remove(key); } - internal void SetSurrogateData(object key, object surrogateData) + private Dictionary? _referencedTypesDictionary; + private Dictionary? _referencedCollectionTypesDictionary; + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Dictionary GetReferencedTypes() { - SurrogateDataTable[key] = surrogateData; + if (_referencedTypesDictionary == null) + { + _referencedTypesDictionary = new Dictionary(); + //Always include Nullable as referenced type + //Do not allow surrogating Nullable + _referencedTypesDictionary.Add(DataContract.GetXmlName(Globals.TypeOfNullable), Globals.TypeOfNullable); + if (_referencedTypes != null) + { + foreach (Type type in _referencedTypes) + { + if (type == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypesCannotContainNull))); + + AddReferencedType(_referencedTypesDictionary, type); + } + } + } + return _referencedTypesDictionary; } - public IDataContractSurrogate DataContractSurrogate + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Dictionary GetReferencedCollectionTypes() { - get { return _dataContractSurrogate; } + if (_referencedCollectionTypesDictionary == null) + { + _referencedCollectionTypesDictionary = new Dictionary(); + if (_referencedCollectionTypes != null) + { + foreach (Type type in _referencedCollectionTypes) + { + if (type == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedCollectionTypesCannotContainNull))); + AddReferencedType(_referencedCollectionTypesDictionary, type); + } + } + XmlQualifiedName genericDictionaryName = DataContract.GetXmlName(Globals.TypeOfDictionaryGeneric); + if (!_referencedCollectionTypesDictionary.ContainsKey(genericDictionaryName) && GetReferencedTypes().ContainsKey(genericDictionaryName)) + AddReferencedType(_referencedCollectionTypesDictionary, Globals.TypeOfDictionaryGeneric); + } + return _referencedCollectionTypesDictionary; } -#endif - public IEnumerator> GetEnumerator() + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void AddReferencedType(Dictionary referencedTypes, Type type) { - return Contracts.GetEnumerator(); + if (IsTypeReferenceable(type)) + { + XmlQualifiedName xmlName; + try + { + xmlName = GetXmlName(type); + } + catch (InvalidDataContractException) + { + // Type not referenceable if we can't get a xml name. + return; + } + catch (InvalidOperationException) + { + // Type not referenceable if we can't get a xml name. + return; + } + + if (referencedTypes.TryGetValue(xmlName, out object? value)) + { + if (value is Type referencedType) + { + if (referencedType != type) + { + referencedTypes.Remove(xmlName); + List types = new List(); + types.Add(referencedType); + types.Add(type); + referencedTypes.Add(xmlName, types); + } + } + else + { + List types = (List)value; + if (!types.Contains(type)) + types.Add(type); + } + } + else + referencedTypes.Add(xmlName, type); + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private static bool IsTypeReferenceable(Type type) + { + try + { + return (type.IsSerializable || + type.IsDefined(Globals.TypeOfDataContractAttribute, false) || + (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) && !type.IsGenericTypeDefinition) || + CollectionDataContract.IsCollection(type, out _) || + ClassDataContract.IsNonAttributedTypeValidForSerialization(type)); + } + catch (Exception ex) + { + // An exception can be thrown in the designer when a project has a runtime binding redirection for a referenced assembly or a reference dependent assembly. + // Type.IsDefined is known to throw System.IO.FileLoadException. + // ClassDataContract.IsNonAttributedTypeValidForSerialization is known to throw System.IO.FileNotFoundException. + // We guard against all non-critical exceptions. + if (Fx.IsFatal(ex)) + throw; + } + + return false; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public Type? GetReferencedType(XmlQualifiedName xmlName, DataContract dataContract, out DataContract? referencedContract, out object[]? genericParameters, bool? supportGenericTypes = null) + { + Type? type = GetReferencedTypeInternal(xmlName, dataContract); + referencedContract = null; + genericParameters = null; + + if (supportGenericTypes == null) + return type; + + if (type != null && !type.IsGenericTypeDefinition && !type.ContainsGenericParameters) + return type; + + if (dataContract.GenericInfo == null) + return null; + + XmlQualifiedName genericXmlName = dataContract.GenericInfo.GetExpandedXmlName(); + if (genericXmlName != dataContract.XmlName) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericTypeNameMismatch, dataContract.XmlName.Name, dataContract.XmlName.Namespace, genericXmlName.Name, genericXmlName.Namespace))); + + // This check originally came "here" in the old code. Its tempting to move it up with the GenericInfo check. + if (!supportGenericTypes.Value) + return null; + + type = GetReferencedGenericTypeInternal(dataContract.GenericInfo, out referencedContract, out genericParameters); + return type; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Type? GetReferencedGenericTypeInternal(GenericInfo genInfo, out DataContract? referencedContract, out object[]? genericParameters) + { + genericParameters = null; + referencedContract = null; + + Type? type = GetReferencedTypeInternal(genInfo.XmlName, null); + + if (type == null) + { + if (genInfo.Parameters != null) + return null; + + referencedContract = GetDataContract(genInfo.XmlName); + if (referencedContract != null && referencedContract.GenericInfo != null) + referencedContract = null; + + return null; // No type, but maybe we found a suitable referenced contract? + } + + // We've got a type. But its generic. So we need some parameter contracts. + // referencedContract is still null, but will be set if we can verify all parameters. + if (genInfo.Parameters != null) + { + bool enableStructureCheck = (type != Globals.TypeOfNullable); + genericParameters = new object[genInfo.Parameters.Count]; + DataContract[] structureCheckContracts = new DataContract[genInfo.Parameters.Count]; + for (int i = 0; i < genInfo.Parameters.Count; i++) + { + GenericInfo paramInfo = genInfo.Parameters[i]; + XmlQualifiedName paramXmlName = paramInfo.GetExpandedXmlName(); + DataContract? paramContract = GetDataContract(paramXmlName); + + if (paramContract != null) + { + genericParameters[i] = paramContract; + } + else + { + Type? paramType = GetReferencedGenericTypeInternal(paramInfo, out paramContract, out object[]? paramParameters); + if (paramType != null) + { + genericParameters[i] = new Tuple(paramType, paramParameters); + } + else + { + genericParameters[i] = paramContract!; + } + } + + structureCheckContracts[i] = paramContract!; // This is ok. If it's null, we disable the use of this array in the next line. + if (paramContract == null) + enableStructureCheck = false; + } + if (enableStructureCheck) + referencedContract = DataContract.GetDataContract(type).BindGenericParameters(structureCheckContracts); + } + + return type; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Type? GetReferencedTypeInternal(XmlQualifiedName xmlName, DataContract? dataContract) + { + Type? type; + + if (dataContract == null) + { + if (TryGetReferencedCollectionType(xmlName, null, out type)) + return type; + if (TryGetReferencedType(xmlName, null, out type)) + { + // enforce that collection types only be specified via ReferencedCollectionTypes + if (CollectionDataContract.IsCollection(type)) + return null; + + return type; + } + } + else if (dataContract is CollectionDataContract) + { + if (TryGetReferencedCollectionType(xmlName, dataContract, out type)) + return type; + } + else + { + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) + xmlName = SchemaImporter.ImportActualType(xmlDataContract.XsdType?.Annotation, xmlName, dataContract.XmlName); + + if (TryGetReferencedType(xmlName, dataContract, out type)) + return type; + } + return null; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool TryGetReferencedType(XmlQualifiedName xmlName, DataContract? dataContract, [NotNullWhen(true)] out Type? type) + { + return TryGetReferencedType(xmlName, dataContract, false/*useReferencedCollectionTypes*/, out type); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool TryGetReferencedCollectionType(XmlQualifiedName xmlName, DataContract? dataContract, [NotNullWhen(true)] out Type? type) + { + return TryGetReferencedType(xmlName, dataContract, true/*useReferencedCollectionTypes*/, out type); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private bool TryGetReferencedType(XmlQualifiedName xmlName, DataContract? dataContract, bool useReferencedCollectionTypes, [NotNullWhen(true)] out Type? type) + { + Dictionary referencedTypes = useReferencedCollectionTypes ? GetReferencedCollectionTypes() : GetReferencedTypes(); + if (referencedTypes.TryGetValue(xmlName, out object? value)) + { + type = value as Type; + if (type != null) + { + return true; + } + else + { + // Throw ambiguous type match exception + List types = (List)value; + StringBuilder errorMessage = new StringBuilder(); + bool containsGenericType = false; + for (int i = 0; i < types.Count; i++) + { + Type conflictingType = types[i]; + if (!containsGenericType) + containsGenericType = conflictingType.IsGenericTypeDefinition; + errorMessage.AppendFormat("{0}\"{1}\" ", Environment.NewLine, conflictingType.AssemblyQualifiedName); + if (dataContract != null) + { + DataContract other = GetDataContract(conflictingType); + errorMessage.Append(SR.Format(((other != null && other.Equals(dataContract)) ? SR.ReferencedTypeMatchingMessage : SR.ReferencedTypeNotMatchingMessage))); + } + } + if (containsGenericType) + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes1 : SR.AmbiguousReferencedTypes1), + errorMessage.ToString()))); + } + else + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes3 : SR.AmbiguousReferencedTypes3), + XmlConvert.DecodeName(xmlName.Name), + xmlName.Namespace, + errorMessage.ToString()))); + } + } + } + type = null; + return false; + } + + internal ISerializationSurrogateProvider2? SerializationExtendedSurrogateProvider => _extendedSurrogateProvider; + + internal object? GetSurrogateData(object key) + { + return SurrogateData[key]; + } + + internal void SetSurrogateData(object key, object? surrogateData) + { + SurrogateData[key] = surrogateData; } internal bool IsContractProcessed(DataContract dataContract) @@ -291,19 +615,24 @@ internal void SetContractProcessed(DataContract dataContract) ProcessedContracts.Add(dataContract, dataContract); } -#if SUPPORT_SURROGATE - internal ContractCodeDomInfo GetContractCodeDomInfo(DataContract dataContract) + internal IEnumerator> GetEnumerator() { - object info; - if (ProcessedContracts.TryGetValue(dataContract, out info)) - return (ContractCodeDomInfo)info; - return null; + return Contracts.GetEnumerator(); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public void ImportSchemaSet(XmlSchemaSet schemaSet, IEnumerable? typeNames, bool importXmlDataType) + { + SchemaImporter importer = new SchemaImporter(schemaSet, typeNames, null, this, importXmlDataType); + importer.Import(out List _); } - internal void SetContractCodeDomInfo(DataContract dataContract, ContractCodeDomInfo info) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + public List ImportSchemaSet(XmlSchemaSet schemaSet, IEnumerable elements, bool importXmlDataType) { - ProcessedContracts.Add(dataContract, info); + SchemaImporter importer = new SchemaImporter(schemaSet, Array.Empty() /* Needs to be empty, not null for 'elements' to be used. */, elements, this, importXmlDataType); + importer.Import(out List? elementNames); + return elementNames!; // Not null when we have provided non-null 'typeNames' and 'elements' } -#endif } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs index 03d145ba53b88..a0554e10fd628 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSurrogateCaller.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.CodeDom; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization { @@ -40,5 +40,31 @@ internal static Type GetDataContractType(ISerializationSurrogateProvider surroga return obj; return surrogateProvider.GetDeserializedObject(obj, memberType); } + + internal static object? GetCustomDataToExport(ISerializationSurrogateProvider2 surrogateProvider, MemberInfo memberInfo, Type dataContractType) + { + return surrogateProvider.GetCustomDataToExport(memberInfo, dataContractType); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static object? GetCustomDataToExport(ISerializationSurrogateProvider2 surrogateProvider, Type clrType, Type dataContractType) + { + if (DataContract.GetBuiltInDataContract(clrType) != null) + return null; + return surrogateProvider.GetCustomDataToExport(clrType, dataContractType); + } + + internal static void GetKnownCustomDataTypes(ISerializationSurrogateProvider2 surrogateProvider, Collection customDataTypes) + { + surrogateProvider.GetKnownCustomDataTypes(customDataTypes); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static Type? GetReferencedTypeOnImport(ISerializationSurrogateProvider2 surrogateProvider, string typeName, string typeNamespace, object? customData) + { + if (DataContract.GetBuiltInDataContract(typeName, typeNamespace) != null) + return null; + return surrogateProvider.GetReferencedTypeOnImport(typeName, typeNamespace, customData); + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs index 251e896e86488..2f1ed959e98dc 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs @@ -2,17 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; -using System.Globalization; -using System.Reflection; -using System.Xml; -using System.Security; using System.Diagnostics.CodeAnalysis; +using System.Reflection; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { - internal sealed class DataMember + public sealed class DataMember { private readonly CriticalHelper _helper; @@ -21,104 +17,73 @@ internal DataMember(MemberInfo memberInfo) _helper = new CriticalHelper(memberInfo); } - internal MemberInfo MemberInfo + internal DataMember(DataContract memberTypeContract, string name, bool isNullable, bool isRequired, bool emitDefaultValue, long order) { - get - { return _helper.MemberInfo; } + _helper = new CriticalHelper(memberTypeContract, name, isNullable, isRequired, emitDefaultValue, order); } + internal MemberInfo MemberInfo => _helper.MemberInfo; + public string Name { - get - { return _helper.Name; } - - set - { _helper.Name = value; } + get => _helper.Name; + internal set => _helper.Name = value; } - public int Order + public long Order { - get - { return _helper.Order; } - - set - { _helper.Order = value; } + get => _helper.Order; + internal set => _helper.Order = value; } public bool IsRequired { - get - { return _helper.IsRequired; } - - set - { _helper.IsRequired = value; } + get => _helper.IsRequired; + internal set => _helper.IsRequired = value; } public bool EmitDefaultValue { - get - { return _helper.EmitDefaultValue; } - - set - { _helper.EmitDefaultValue = value; } + get => _helper.EmitDefaultValue; + internal set => _helper.EmitDefaultValue = value; } public bool IsNullable { - get - { return _helper.IsNullable; } - - set - { _helper.IsNullable = value; } + get => _helper.IsNullable; + internal set => _helper.IsNullable = value; } - public bool IsGetOnlyCollection + internal bool IsGetOnlyCollection { - get - { return _helper.IsGetOnlyCollection; } - - set - { _helper.IsGetOnlyCollection = value; } + get => _helper.IsGetOnlyCollection; + set => _helper.IsGetOnlyCollection = value; } - internal Type MemberType - { - get - { return _helper.MemberType; } - } + internal Type MemberType => _helper.MemberType; - internal DataContract MemberTypeContract + public DataContract MemberTypeContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.MemberTypeContract; } + get => _helper.MemberTypeContract; } internal PrimitiveDataContract? MemberPrimitiveContract { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { - return _helper.MemberPrimitiveContract; - } + get => _helper.MemberPrimitiveContract; } - public bool HasConflictingNameAndType + internal bool HasConflictingNameAndType { - get - { return _helper.HasConflictingNameAndType; } - - set - { _helper.HasConflictingNameAndType = value; } + get => _helper.HasConflictingNameAndType; + set => _helper.HasConflictingNameAndType = value; } internal DataMember? ConflictingMember { - get - { return _helper.ConflictingMember; } - - set - { _helper.ConflictingMember = value; } + get => _helper.ConflictingMember; + set => _helper.ConflictingMember = value; } private FastInvokerBuilder.Getter? _getter; @@ -131,12 +96,13 @@ private sealed class CriticalHelper { private DataContract? _memberTypeContract; private string _name = null!; // Name is always initialized right after construction - private int _order; + private long _order; private bool _isRequired; private bool _emitDefaultValue; private bool _isNullable; private bool _isGetOnlyCollection; private readonly MemberInfo _memberInfo; + private Type? _memberType; private bool _hasConflictingNameAndType; private DataMember? _conflictingMember; @@ -147,60 +113,67 @@ internal CriticalHelper(MemberInfo memberInfo) _memberPrimitiveContract = PrimitiveDataContract.NullContract; } - internal MemberInfo MemberInfo + internal CriticalHelper(DataContract memberTypeContract, string name, bool isNullable, bool isRequired, bool emitDefaultValue, long order) { - get { return _memberInfo; } + _memberTypeContract = memberTypeContract; + _name = name; + _isNullable = isNullable; + _isRequired = isRequired; + _emitDefaultValue = emitDefaultValue; + _order = order; + _memberInfo = memberTypeContract.UnderlyingType; } + internal MemberInfo MemberInfo => _memberInfo; + internal string Name { - get { return _name; } - set { _name = value; } + get => _name; + set => _name = value; } - internal int Order + internal long Order { - get { return _order; } - set { _order = value; } + get => _order; + set => _order = value; } internal bool IsRequired { - get { return _isRequired; } - set { _isRequired = value; } + get => _isRequired; + set => _isRequired = value; } internal bool EmitDefaultValue { - get { return _emitDefaultValue; } - set { _emitDefaultValue = value; } + get => _emitDefaultValue; + set => _emitDefaultValue = value; } internal bool IsNullable { - get { return _isNullable; } - set { _isNullable = value; } + get => _isNullable; + set => _isNullable = value; } internal bool IsGetOnlyCollection { - get { return _isGetOnlyCollection; } - set { _isGetOnlyCollection = value; } + get => _isGetOnlyCollection; + set => _isGetOnlyCollection = value; } - private Type? _memberType; - internal Type MemberType { get { if (_memberType == null) { - FieldInfo? field = MemberInfo as FieldInfo; - if (field != null) + if (MemberInfo is FieldInfo field) _memberType = field.FieldType; + else if (MemberInfo is PropertyInfo prop) + _memberType = prop.PropertyType; else - _memberType = ((PropertyInfo)MemberInfo).PropertyType; + _memberType = (Type)MemberInfo!; } return _memberType; @@ -214,9 +187,9 @@ internal DataContract MemberTypeContract { if (_memberTypeContract == null) { - if (this.IsGetOnlyCollection) + if (IsGetOnlyCollection) { - _memberTypeContract = DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(MemberType.TypeHandle), MemberType.TypeHandle, MemberType, SerializationMode.SharedContract); + _memberTypeContract = DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(MemberType.TypeHandle), MemberType.TypeHandle, MemberType); } else { @@ -234,14 +207,14 @@ internal DataContract MemberTypeContract internal bool HasConflictingNameAndType { - get { return _hasConflictingNameAndType; } - set { _hasConflictingNameAndType = value; } + get => _hasConflictingNameAndType; + set => _hasConflictingNameAndType = value; } internal DataMember? ConflictingMember { - get { return _conflictingMember; } - set { _conflictingMember = value; } + get => _conflictingMember; + set => _conflictingMember = value; } private PrimitiveDataContract? _memberPrimitiveContract; @@ -310,5 +283,38 @@ internal bool RequiresMemberAccessForSet() } return false; } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal DataMember BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) + { + DataContract memberTypeContract = MemberTypeContract.BindGenericParameters(paramContracts, boundContracts); + DataMember boundDataMember = new DataMember(memberTypeContract, + Name, + !memberTypeContract.IsValueType, + IsRequired, + EmitDefaultValue, + Order); + return boundDataMember; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal bool Equals(object? other, HashSet? checkedContracts) + { + if (this == other) + return true; + + if (other is DataMember dataMember) + { + // Note: comparison does not use Order hint since it influences element order but does not specify exact order + bool thisIsNullable = (MemberTypeContract == null) ? false : !MemberTypeContract.IsValueType; + bool dataMemberIsNullable = (dataMember.MemberTypeContract == null) ? false : !dataMember.MemberTypeContract.IsValueType; + return (Name == dataMember.Name + && (IsNullable || thisIsNullable) == (dataMember.IsNullable || dataMemberIsNullable) + && IsRequired == dataMember.IsRequired + && EmitDefaultValue == dataMember.EmitDefaultValue + && MemberTypeContract!.Equals(dataMember.MemberTypeContract, checkedContracts)); + } + return false; + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs index 20cb05471fe63..da469b0c337c6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DateTimeOffsetAdapter.cs @@ -66,7 +66,7 @@ public static DateTimeOffsetAdapter GetDateTimeOffsetAdapter(DateTimeOffset valu public string ToString(IFormatProvider provider) { - return "DateTime: " + this.UtcDateTime + ", Offset: " + this.OffsetMinutes; + return "DateTime: " + UtcDateTime + ", Offset: " + OffsetMinutes; } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs index 012b8ec54929b..a6217253340dc 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs @@ -3,6 +3,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Threading; namespace System.Runtime.Serialization { @@ -20,6 +22,50 @@ public static void Assert(string message) { Assert(false, message); } + + public static bool IsFatal(Exception exception) + { + while (exception != null) + { + // NetFx checked for FatalException and FatalInternalException as well, which were ServiceModel constructs. + if ((exception is OutOfMemoryException && !(exception is InsufficientMemoryException)) || + exception is ThreadAbortException) + { + return true; + } + + // These exceptions aren't themselves fatal, but since the CLR uses them to wrap other exceptions, + // we want to check to see whether they've been used to wrap a fatal exception. If so, then they + // count as fatal. + if (exception is TypeInitializationException || + exception is TargetInvocationException) + { + exception = exception.InnerException!; + } + else if (exception is AggregateException) + { + // AggregateExceptions have a collection of inner exceptions, which may themselves be other + // wrapping exceptions (including nested AggregateExceptions). Recursively walk this + // hierarchy. The (singular) InnerException is included in the collection. + var innerExceptions = ((AggregateException)exception).InnerExceptions; + foreach (Exception innerException in innerExceptions) + { + if (IsFatal(innerException)) + { + return true; + } + } + + break; + } + else + { + break; + } + } + + return false; + } } internal static class DiagnosticUtility diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs index 9014a71b47d40..ac13799e9e78f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs @@ -2,75 +2,80 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; -using System.Globalization; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Reflection; using System.Threading; -using System.Text; using System.Xml; -using System.Security; -using System.Linq; -using System.Diagnostics.CodeAnalysis; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class EnumDataContract : DataContract { - private readonly EnumDataContractCriticalHelper _helper; + internal const string ContractTypeString = nameof(EnumDataContract); + public override string? ContractType => ContractTypeString; - public XmlQualifiedName? BaseContractName { get; set; } + private readonly EnumDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal EnumDataContract(Type type) : base(new EnumDataContractCriticalHelper(type)) { _helper = (base.Helper as EnumDataContractCriticalHelper)!; } - public List Members + + internal static Type? GetBaseType(XmlQualifiedName baseContractName) { - get - { return _helper.Members; } - set { _helper.Members = value; } + return EnumDataContractCriticalHelper.GetBaseType(baseContractName); } - public List? Values + public override DataContract BaseContract { - get - { return _helper.Values; } - set { _helper.Values = value; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + get => _helper.BaseContract; } - public bool IsFlags + internal XmlQualifiedName BaseContractName { - get - { return _helper.IsFlags; } - set { _helper.IsFlags = value; } + get => _helper.BaseContractName; + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + set => _helper.BaseContractName = value; } - public bool IsULong + internal List Members { - get - { return _helper.IsULong; } - set { _helper.IsULong = value; } + get => _helper.Members; + set => _helper.Members = value; } - public XmlDictionaryString[]? ChildElementNames + public override ReadOnlyCollection DataMembers => (Members == null) ? DataContract.s_emptyDataMemberList : Members.AsReadOnly(); + + internal List? Values { - get - { return _helper.ChildElementNames; } - set { _helper.ChildElementNames = value; } + get => _helper.Values; + set => _helper.Values = value; } - internal override bool CanContainReferences + internal bool IsFlags { - get { return false; } + get => _helper.IsFlags; + set => _helper.IsFlags = value; } + internal bool IsULong => _helper.IsULong; + + internal XmlDictionaryString[]? ChildElementNames => _helper.ChildElementNames; + + internal override bool CanContainReferences => false; + private sealed class EnumDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static readonly Dictionary s_typeToName = new Dictionary(); private static readonly Dictionary s_nameToType = new Dictionary(); + private DataContract _baseContract; private List _members; private List? _values; private bool _isULong; @@ -80,21 +85,35 @@ private sealed class EnumDataContractCriticalHelper : DataContract.DataContractC static EnumDataContractCriticalHelper() { - Add(typeof(sbyte), "byte"); - Add(typeof(byte), "unsignedByte"); - Add(typeof(short), "short"); - Add(typeof(ushort), "unsignedShort"); - Add(typeof(int), "int"); - Add(typeof(uint), "unsignedInt"); - Add(typeof(long), "long"); - Add(typeof(ulong), "unsignedLong"); + Add(typeof(sbyte), DictionaryGlobals.SignedByteLocalName.Value); // "byte" + Add(typeof(byte), DictionaryGlobals.UnsignedByteLocalName.Value); // "unsignedByte" + Add(typeof(short), DictionaryGlobals.ShortLocalName.Value); // "short" + Add(typeof(ushort), DictionaryGlobals.UnsignedShortLocalName.Value); // "unsignedShort" + Add(typeof(int), DictionaryGlobals.IntLocalName.Value); // "int" + Add(typeof(uint), DictionaryGlobals.UnsignedIntLocalName.Value); // "unsignedInt" + Add(typeof(long), DictionaryGlobals.LongLocalName.Value); // "long" + Add(typeof(ulong), DictionaryGlobals.UnsignedLongLocalName.Value); // "unsignedLong" } internal static void Add(Type type, string localName) { - XmlQualifiedName stableName = CreateQualifiedName(localName, Globals.SchemaNamespace); - s_typeToName.Add(type, stableName); - s_nameToType.Add(stableName, type); + XmlQualifiedName xmlName = CreateQualifiedName(localName, Globals.SchemaNamespace); + s_typeToName.Add(type, xmlName); + s_nameToType.Add(xmlName, type); + } + + internal static XmlQualifiedName GetBaseContractName(Type type) + { + s_typeToName.TryGetValue(type, out XmlQualifiedName? retVal); + + Debug.Assert(retVal != null); // Enums can only have certain base types. We shouldn't come up empty here. + return retVal; + } + + internal static Type? GetBaseType(XmlQualifiedName baseContractName) + { + s_nameToType.TryGetValue(baseContractName, out Type? retVal); + return retVal; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -102,20 +121,23 @@ internal EnumDataContractCriticalHelper( [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)] Type type) : base(type) { - this.StableName = DataContract.GetStableName(type, out _hasDataContract); + XmlName = DataContract.GetXmlName(type, out _hasDataContract); Type baseType = Enum.GetUnderlyingType(type); + XmlQualifiedName baseTypeName = GetBaseContractName(baseType); + _baseContract = DataContract.GetBuiltInDataContract(baseTypeName.Name, baseTypeName.Namespace)!; + // Setting XmlName might be redundant. But I don't want to miss an edge case. + _baseContract.XmlName = baseTypeName; ImportBaseType(baseType); IsFlags = type.IsDefined(Globals.TypeOfFlagsAttribute, false); ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(2 + Members.Count); - Name = dictionary.Add(StableName.Name); - Namespace = dictionary.Add(StableName.Namespace); + Name = dictionary.Add(XmlName.Name); + Namespace = dictionary.Add(XmlName.Namespace); _childElementNames = new XmlDictionaryString[Members.Count]; for (int i = 0; i < Members.Count; i++) _childElementNames[i] = dictionary.Add(Members[i].Name); - DataContractAttribute? dataContractAttribute; - if (TryGetDCAttribute(type, out dataContractAttribute)) + if (TryGetDCAttribute(type, out DataContractAttribute? dataContractAttribute)) { if (dataContractAttribute.IsReference) { @@ -128,34 +150,55 @@ internal EnumDataContractCriticalHelper( } } } + + internal DataContract BaseContract => _baseContract; + + internal XmlQualifiedName BaseContractName + { + get => _baseContract.XmlName; + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + set + { + Type? baseType = GetBaseType(value); + if (baseType == null) + ThrowInvalidDataContractException( + SR.Format(SR.InvalidEnumBaseType, value.Name, value.Namespace, XmlName.Name, XmlName.Namespace)); + ImportBaseType(baseType); + _baseContract = DataContract.GetBuiltInDataContract(value.Name, value.Namespace)!; + // Setting XmlName might be redundant. But I don't want to miss an edge case. + _baseContract.XmlName = value; + } + } + internal List Members { - get { return _members; } - set { _members = value; } + get => _members; + set => _members = value; } internal List? Values { - get { return _values; } - set { _values = value; } + get => _values; + set => _values = value; } internal bool IsFlags { - get { return _isFlags; } - set { _isFlags = value; } + get => _isFlags; + set => _isFlags = value; } internal bool IsULong { - get { return _isULong; } - set { _isULong = value; } + get => _isULong; + set => _isULong = value; } internal XmlDictionaryString[]? ChildElementNames { - get { return _childElementNames; } - set { _childElementNames = value; } + get => _childElementNames; + set => _childElementNames = value; } private void ImportBaseType(Type baseType) @@ -166,7 +209,7 @@ private void ImportBaseType(Type baseType) [MemberNotNull(nameof(_members))] private void ImportDataMembers() { - Type type = this.UnderlyingType; + Type type = UnderlyingType; FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public); Dictionary memberValuesTable = new Dictionary(); List tempMembers = new List(fields.Length); @@ -194,6 +237,7 @@ private void ImportDataMembers() } else memberContract.Name = field.Name; + memberContract.Order = _isULong ? (long)Convert.ToUInt64(field.GetValue(null)) : Convert.ToInt64(field.GetValue(null)); ClassDataContract.CheckAndAddMember(tempMembers, memberContract, memberValuesTable); enumMemberValid = true; } @@ -206,8 +250,8 @@ private void ImportDataMembers() { if (!field.IsNotSerialized) { - DataMember memberContract = new DataMember(field); - memberContract.Name = field.Name; + DataMember memberContract = new DataMember(field) { Name = field.Name }; + memberContract.Order = _isULong ? (long)Convert.ToUInt64(field.GetValue(null)) : Convert.ToInt64(field.GetValue(null)); ClassDataContract.CheckAndAddMember(tempMembers, memberContract, memberValuesTable); enumMemberValid = true; } @@ -359,14 +403,45 @@ internal long GetEnumValueFromString(string value) } } + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (IsEqualOrChecked(other, checkedContracts)) + return true; + + if (base.Equals(other, null)) + { + if (other is EnumDataContract enumContract) + { + if (Members.Count != enumContract.Members.Count || Values?.Count != enumContract.Values?.Count) + return false; + string[] memberNames1 = new string[Members.Count], memberNames2 = new string[Members.Count]; + for (int i = 0; i < Members.Count; i++) + { + memberNames1[i] = Members[i].Name; + memberNames2[i] = enumContract.Members[i].Name; + } + Array.Sort(memberNames1); + Array.Sort(memberNames2); + for (int i = 0; i < Members.Count; i++) + { + if (memberNames1[i] != memberNames2[i]) + return false; + } + + return (IsFlags == enumContract.IsFlags); + } + } + return false; + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { WriteEnumValue(xmlWriter, obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object obj = ReadEnumValue(xmlReader); context?.AddNewObject(obj); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs index 435415d1ca565..8034f326ea09e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExportOptions.cs @@ -5,24 +5,26 @@ namespace System.Runtime.Serialization { + /// + /// Represents the options that can be set for an . + /// + /// + /// The is used to generate XSD schemas from a type or assembly. You can also use the XsdDataContractImporter to generate .NET Framework code from a schema document. + /// + /// The property is used by the to include types that can be read in an object graph. + /// public class ExportOptions { private Collection? _knownTypes; -#if SUPPORT_SURROGATE - private IDataContractSurrogate? _dataContractSurrogate; - public IDataContractSurrogate? DataContractSurrogate - { - get { return _dataContractSurrogate; } - set { _dataContractSurrogate = value; } - } - - internal IDataContractSurrogate? GetSurrogate() - { - return _dataContractSurrogate; - } -#endif + /// + /// Gets or sets a serialization surrogate provider. + /// + public ISerializationSurrogateProvider? DataContractSurrogate { get; set; } + /// + /// Gets the collection of types that may be encountered during serialization or deserialization. + /// public Collection KnownTypes => _knownTypes ??= new Collection(); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs index be5723fb3b09a..6004b41c84e3c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs @@ -36,7 +36,7 @@ public ExtensionDataMember(string name, string ns) public string Name { get; } - public string? Namespace { get; } + public string Namespace { get; } public IDataNode? Value { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs index df8b296a45713..d4309b86b3d4c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs @@ -1,9 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization { @@ -27,7 +31,7 @@ private enum ExtensionDataNodeType private ElementData? _nextElement; private ReadState _readState = ReadState.Initial; - private readonly ExtensionDataNodeType _internalNodeType; + private ExtensionDataNodeType _internalNodeType; private XmlNodeType _nodeType; private int _depth; private string? _localName; @@ -37,11 +41,12 @@ private enum ExtensionDataNodeType private int _attributeCount; private int _attributeIndex; + private Hashtable _cache = new Hashtable(); + private XmlNodeReader? _xmlNodeReader; + private Queue? _deserializedDataNodes; + private static readonly object s_prefixLock = new object(); -#pragma warning disable 0649 - private readonly XmlNodeReader? _xmlNodeReader; -#pragma warning restore 0649 private readonly XmlObjectSerializerReadContext _context; @@ -62,6 +67,16 @@ internal ExtensionDataReader(XmlObjectSerializerReadContext context) _context = context; } + internal void SetDeserializedValue(object? obj) + { + IDataNode? deserializedDataNode = (_deserializedDataNodes == null || _deserializedDataNodes.Count == 0) ? null : _deserializedDataNodes.Dequeue(); + if (deserializedDataNode != null && !(obj is IDataNode)) + { + deserializedDataNode.Value = obj; + deserializedDataNode.IsFinalValue = true; + } + } + internal IDataNode? GetCurrentNode() { IDataNode? retVal = _element!.dataNode; @@ -69,6 +84,14 @@ internal ExtensionDataReader(XmlObjectSerializerReadContext context) return retVal; } + internal void SetDataNode(IDataNode dataNode, string? name, string? ns) + { + SetNextElement(dataNode, name, ns, null); + _element = _nextElement; + _nextElement = null; + SetElement(); + } + internal void Reset() { _localName = null; @@ -83,11 +106,9 @@ internal void Reset() _elements = null; } -#pragma warning disable CS8775 // Member must have a non-null value when exiting in some condition. [MemberNotNullWhen(true, nameof(_xmlNodeReader))] [MemberNotNullWhen(false, nameof(_element))] private bool IsXmlDataNode { get { return (_internalNodeType == ExtensionDataNodeType.Xml); } } -#pragma warning restore CS8775 // Member must have a non-null value when exiting in some condition. public override XmlNodeType NodeType { get { return IsXmlDataNode ? _xmlNodeReader.NodeType : _nodeType; } } public override string LocalName { get { return IsXmlDataNode ? _xmlNodeReader.LocalName : _localName!; } } @@ -427,9 +448,294 @@ public override bool ReadAttributeValue() return false; } - private static void MoveNext(IDataNode? dataNode) + private void MoveNext(IDataNode? dataNode) + { + switch (_internalNodeType) + { + case ExtensionDataNodeType.Text: + case ExtensionDataNodeType.ReferencedElement: + case ExtensionDataNodeType.NullElement: + _internalNodeType = ExtensionDataNodeType.EndElement; + return; + default: + Type? dataNodeType = dataNode?.DataType; + if (dataNodeType == Globals.TypeOfClassDataNode) + MoveNextInClass((ClassDataNode)dataNode!); + else if (dataNodeType == Globals.TypeOfCollectionDataNode) + MoveNextInCollection((CollectionDataNode)dataNode!); + else if (dataNodeType == Globals.TypeOfISerializableDataNode) + MoveNextInISerializable((ISerializableDataNode)dataNode!); + else if (dataNodeType == Globals.TypeOfXmlDataNode) + MoveNextInXml((XmlDataNode)dataNode!); + else if (dataNode?.Value != null) + MoveToDeserializedObject(dataNode!); + else + { + Fx.Assert("Encountered invalid data node when deserializing unknown data"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.Format(SR.InvalidStateInExtensionDataReader))); + } + break; + } + } + + private void SetNextElement(IDataNode? node, string? name, string? ns, string? prefix) + { + _internalNodeType = ExtensionDataNodeType.Element; + _nextElement = GetNextElement(); + _nextElement.localName = name; + _nextElement.ns = ns; + _nextElement.prefix = prefix; + if (node == null) + { + _nextElement.attributeCount = 0; + _nextElement.AddAttribute(Globals.XsiPrefix, Globals.SchemaInstanceNamespace, Globals.XsiNilLocalName, Globals.True); + _internalNodeType = ExtensionDataNodeType.NullElement; + } + else if (!CheckIfNodeHandled(node)) + { + AddDeserializedDataNode(node); + node.GetData(_nextElement); + if (node is XmlDataNode xdn) + MoveNextInXml(xdn); + } + } + + private void AddDeserializedDataNode(IDataNode node) + { + if (node.Id != Globals.NewObjectId && (node.Value == null || !node.IsFinalValue)) + { + if (_deserializedDataNodes == null) + _deserializedDataNodes = new Queue(); + _deserializedDataNodes.Enqueue(node); + } + } + + private bool CheckIfNodeHandled(IDataNode node) + { + bool handled = false; + if (node.Id != Globals.NewObjectId) + { + handled = (_cache[node] != null); + if (handled) + { + if (_nextElement == null) + _nextElement = GetNextElement(); + _nextElement.attributeCount = 0; + _nextElement.AddAttribute(Globals.SerPrefix, Globals.SerializationNamespace, Globals.RefLocalName, node.Id.ToString(NumberFormatInfo.InvariantInfo)); + _nextElement.AddAttribute(Globals.XsiPrefix, Globals.SchemaInstanceNamespace, Globals.XsiNilLocalName, Globals.True); + _internalNodeType = ExtensionDataNodeType.ReferencedElement; + } + else + { + _cache.Add(node, node); + } + } + return handled; + } + + private void MoveNextInClass(ClassDataNode dataNode) + { + // Two frames above here in Read(), _element is asserted not null. + Fx.Assert(_element != null, ""); + if (dataNode.Members != null && _element.childElementIndex < dataNode.Members.Count) + { + if (_element.childElementIndex == 0) + _context.IncrementItemCount(-dataNode.Members.Count); + + ExtensionDataMember member = dataNode.Members[_element.childElementIndex++]; + SetNextElement(member.Value, member.Name, member.Namespace, GetPrefix(member.Namespace)); + } + else + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _element.childElementIndex = 0; + } + } + + private void MoveNextInCollection(CollectionDataNode dataNode) + { + // Two frames above here in Read(), _element is asserted not null. + Fx.Assert(_element != null, ""); + if (dataNode.Items != null && _element.childElementIndex < dataNode.Items.Count) + { + if (_element.childElementIndex == 0) + _context.IncrementItemCount(-dataNode.Items.Count); + + IDataNode? item = dataNode.Items[_element.childElementIndex++]; + SetNextElement(item, dataNode.ItemName, dataNode.ItemNamespace, GetPrefix(dataNode.ItemNamespace)); + } + else + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _element.childElementIndex = 0; + } + } + + private void MoveNextInISerializable(ISerializableDataNode dataNode) + { + // Two frames above here in Read(), _element is asserted not null. + Fx.Assert(_element != null, ""); + if (dataNode.Members != null && _element.childElementIndex < dataNode.Members.Count) + { + if (_element.childElementIndex == 0) + _context.IncrementItemCount(-dataNode.Members.Count); + + ISerializableDataMember member = dataNode.Members[_element.childElementIndex++]; + SetNextElement(member.Value, member.Name, string.Empty, string.Empty); + } + else + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _element.childElementIndex = 0; + } + } + + private void MoveNextInXml(XmlDataNode dataNode) { - throw NotImplemented.ByDesign; + if (IsXmlDataNode) + { + _xmlNodeReader.Read(); + if (_xmlNodeReader.Depth == 0) + { + _internalNodeType = ExtensionDataNodeType.EndElement; + _xmlNodeReader = null; + } + } + else + { + _internalNodeType = ExtensionDataNodeType.Xml; + if (_element == null) + _element = _nextElement; + else + PushElement(); + + Debug.Assert(dataNode.OwnerDocument != null); // OwnerDocument is always set on initialized dataNodes + + XmlElement wrapperElement = XmlObjectSerializerReadContext.CreateWrapperXmlElement(dataNode.OwnerDocument, + dataNode.XmlAttributes, dataNode.XmlChildNodes, _element?.prefix, _element?.localName, _element?.ns); + if (_element != null) + { + for (int i = 0; i < _element.attributeCount; i++) + { + AttributeData a = _element.attributes![i]; + XmlAttribute xmlAttr = dataNode.OwnerDocument.CreateAttribute(a.prefix, a.localName!, a.ns); + xmlAttr.Value = a.value; + wrapperElement.Attributes.Append(xmlAttr); + } + } + _xmlNodeReader = new XmlNodeReader(wrapperElement); + _xmlNodeReader.Read(); + } + } + + private void MoveToDeserializedObject(IDataNode dataNode) + { + Type type = dataNode.DataType; + bool isTypedNode = true; + if (type == Globals.TypeOfObject && dataNode.Value != null) + { + type = dataNode.Value.GetType(); + if (type == Globals.TypeOfObject) + { + _internalNodeType = ExtensionDataNodeType.EndElement; + return; + } + isTypedNode = false; + } + + if (!MoveToText(type, dataNode, isTypedNode)) + { + if (dataNode.IsFinalValue) + { + _internalNodeType = ExtensionDataNodeType.EndElement; + } + else + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.InvalidDataNode, DataContract.GetClrTypeFullName(type)))); + } + } + } + + private bool MoveToText(Type type, IDataNode dataNode, bool isTypedNode) + { + Fx.Assert(dataNode.Value != null, ""); + + bool handled = true; + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (bool)dataNode.Value); + break; + case TypeCode.Char: + _value = XmlConvert.ToString((int)(isTypedNode ? ((DataNode)dataNode).GetValue() : (char)dataNode.Value)); + break; + case TypeCode.Byte: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (byte)dataNode.Value); + break; + case TypeCode.Int16: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (short)dataNode.Value); + break; + case TypeCode.Int32: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (int)dataNode.Value); + break; + case TypeCode.Int64: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (long)dataNode.Value); + break; + case TypeCode.Single: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (float)dataNode.Value); + break; + case TypeCode.Double: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (double)dataNode.Value); + break; + case TypeCode.Decimal: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (decimal)dataNode.Value); + break; + case TypeCode.DateTime: + DateTime dateTime = isTypedNode ? ((DataNode)dataNode).GetValue() : (DateTime)dataNode.Value; + _value = dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", DateTimeFormatInfo.InvariantInfo); + break; + case TypeCode.String: + _value = isTypedNode ? ((DataNode)dataNode).GetValue() : (string?)dataNode.Value; + break; + case TypeCode.SByte: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (sbyte)dataNode.Value); + break; + case TypeCode.UInt16: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (ushort)dataNode.Value); + break; + case TypeCode.UInt32: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (uint)dataNode.Value); + break; + case TypeCode.UInt64: + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (ulong)dataNode.Value); + break; + case TypeCode.Object: + default: + if (type == Globals.TypeOfByteArray) + { + byte[]? bytes = isTypedNode ? ((DataNode)dataNode).GetValue() : (byte[])dataNode.Value; + _value = (bytes == null) ? string.Empty : Convert.ToBase64String(bytes); + } + else if (type == Globals.TypeOfTimeSpan) + _value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (TimeSpan)dataNode.Value); + else if (type == Globals.TypeOfGuid) + { + Guid guid = isTypedNode ? ((DataNode)dataNode).GetValue() : (Guid)dataNode.Value; + _value = guid.ToString(); + } + else if (type == Globals.TypeOfUri) + { + Uri uri = isTypedNode ? ((DataNode)dataNode).GetValue() : (Uri)dataNode.Value; + _value = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + } + else + handled = false; + break; + } + + if (handled) + _internalNodeType = ExtensionDataNodeType.Text; + return handled; } private void PushElement() @@ -477,11 +783,11 @@ private void GrowElementsIfNeeded() } } - private ElementData? GetNextElement() + private ElementData GetNextElement() { int nextDepth = _depth + 1; return (_elements == null || _elements.Length <= nextDepth || _elements[nextDepth] == null) - ? new ElementData() : _elements[nextDepth]; + ? new ElementData() : _elements[nextDepth]!; } internal static string GetPrefix(string? ns) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs index 8f5bd20805c9d..1d5eca774631c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class GenericParameterDataContract : DataContract { @@ -18,19 +18,9 @@ internal GenericParameterDataContract(Type type) _helper = (base.Helper as GenericParameterDataContractCriticalHelper)!; } - internal int ParameterPosition - { - get - { return _helper.ParameterPosition; } - } + internal int ParameterPosition => _helper.ParameterPosition; - public override bool IsBuiltInDataContract - { - get - { - return true; - } - } + public override bool IsBuiltInDataContract => true; private sealed class GenericParameterDataContractCriticalHelper : DataContract.DataContractCriticalHelper { @@ -42,17 +32,15 @@ internal GenericParameterDataContractCriticalHelper( Type type) : base(type) { - SetDataContractName(DataContract.GetStableName(type)); + SetDataContractName(DataContract.GetXmlName(type)); _parameterPosition = type.GenericParameterPosition; } - internal int ParameterPosition - { - get { return _parameterPosition; } - } + internal int ParameterPosition => _parameterPosition; } - internal DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary boundContracts) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary? boundContracts = null) { return paramContracts[ParameterPosition]; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs index 48167f42d04f6..3fa9493884c75 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.ComponentModel; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Schema; @@ -227,6 +227,10 @@ internal static partial class Globals internal static Type TypeOfNullable => s_typeOfNullable ??= typeof(Nullable<>); + private static Type? s_typeOfReflectionPointer; + internal static Type TypeOfReflectionPointer => + s_typeOfReflectionPointer ??= typeof(System.Reflection.Pointer); + private static Type? s_typeOfIDictionaryGeneric; internal static Type TypeOfIDictionaryGeneric => s_typeOfIDictionaryGeneric ??= typeof(IDictionary<,>); @@ -271,10 +275,6 @@ internal static partial class Globals internal static Type TypeOfKeyValuePair => s_typeOfKeyValuePair ??= typeof(KeyValuePair<,>); - private static Type? s_typeOfKeyValuePairAdapter; - internal static Type TypeOfKeyValuePairAdapter => - s_typeOfKeyValuePairAdapter ??= typeof(KeyValuePairAdapter<,>); - private static Type? s_typeOfKeyValue; internal static Type TypeOfKeyValue => s_typeOfKeyValue ??= typeof(KeyValue<,>); @@ -314,21 +314,25 @@ internal static Type TypeOfHashtable internal static Type TypeOfDBNull => s_typeOfDBNull ??= typeof(DBNull); - private static Uri? s_dataContractXsdBaseNamespaceUri; - internal static Uri DataContractXsdBaseNamespaceUri => - s_dataContractXsdBaseNamespaceUri ??= new Uri(DataContractXsdBaseNamespace); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + private static Type? s_typeOfSchemaDefinedType; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + internal static Type TypeOfSchemaDefinedType => + s_typeOfSchemaDefinedType ??= typeof(SchemaDefinedType); - private static readonly Type? s_typeOfScriptObject; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + private static Type? s_typeOfSchemaDefinedEnum; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] + internal static Type TypeOfSchemaDefinedEnum => + s_typeOfSchemaDefinedEnum ??= typeof(SchemaDefinedEnum); - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static ClassDataContract CreateScriptObjectClassDataContract() - { - Debug.Assert(s_typeOfScriptObject != null); - return new ClassDataContract(s_typeOfScriptObject); - } + private static MemberInfo? s_schemaMemberInfoPlaceholder; + internal static MemberInfo SchemaMemberInfoPlaceholder => + s_schemaMemberInfoPlaceholder ??= TypeOfSchemaDefinedType.GetField(nameof(SchemaDefinedType._xmlName), BindingFlags.NonPublic | BindingFlags.Instance)!; - internal static bool TypeOfScriptObject_IsAssignableFrom(Type type) => - s_typeOfScriptObject != null && s_typeOfScriptObject.IsAssignableFrom(type); + private static Uri? s_dataContractXsdBaseNamespaceUri; + internal static Uri DataContractXsdBaseNamespaceUri => + s_dataContractXsdBaseNamespaceUri ??= new Uri(DataContractXsdBaseNamespace); public const bool DefaultIsRequired = false; public const bool DefaultEmitDefaultValue = true; @@ -341,6 +345,10 @@ internal static bool TypeOfScriptObject_IsAssignableFrom(Type type) => public const string FullSRSInternalsVisiblePattern = @"^[\s]*System\.Runtime\.Serialization[\s]*,[\s]*PublicKey[\s]*=[\s]*(?i:00240000048000009400000006020000002400005253413100040000010001008d56c76f9e8649383049f383c44be0ec204181822a6c31cf5eb7ef486944d032188ea1d3920763712ccb12d75fb77e9811149e6148e5d32fbaab37611c1878ddc19e20ef135d0cb2cff2bfec3d115810c3d9069638fe4be215dbf795861920e5ab6f7db2e2ceef136ac23d5dd2bf031700aec232f6c6b1c785b4305c123b37ab)[\s]*$"; [GeneratedRegex(FullSRSInternalsVisiblePattern)] public static partial Regex FullSRSInternalsVisibleRegex(); + public const char SpaceChar = ' '; + public const char OpenBracketChar = '['; + public const char CloseBracketChar = ']'; + public const char CommaChar = ','; public const string Space = " "; public const string XsiPrefix = "i"; public const string XsdPrefix = "x"; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs index 4ab7ae4006ca9..208edd8125982 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs @@ -10,10 +10,12 @@ using System.IO; using System.Reflection; using System.Runtime.Serialization; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Text; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization.Json { @@ -32,7 +34,7 @@ public sealed class DataContractJsonSerializer : XmlObjectSerializer private XmlDictionaryString? _rootName; private bool _rootNameRequiresMapping; private Type _rootType; - + private ISerializationSurrogateProvider? _serializationSurrogateProvider; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type) @@ -92,6 +94,12 @@ internal DataContractJsonSerializer(Type type, Initialize(type, rootName, knownTypes, maxItemsInObjectGraph, ignoreExtensionDataObject, emitTypeInformation, false, null, false); } + internal ISerializationSurrogateProvider? SerializationSurrogateProvider + { + get { return _serializationSurrogateProvider; } + set { _serializationSurrogateProvider = value; } + } + public bool IgnoreExtensionDataObject { get { return _ignoreExtensionDataObject; } @@ -193,6 +201,19 @@ private XmlDictionaryString RootName } } + // These Get/Set methods mirror the extensions that were added to DCS in the early days of Core, which allowed + // using a slimmed-down surrogate on both NetFx and Core via type-forwarding mechanisms. That's why these are + // a pair of methods instead of making the property itself public. + public ISerializationSurrogateProvider? GetSerializationSurrogateProvider() + { + return SerializationSurrogateProvider; + } + + public void SetSerializationSurrogateProvider(ISerializationSurrogateProvider? provider) + { + SerializationSurrogateProvider = provider; + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public override bool IsStartObject(XmlReader reader) { @@ -451,6 +472,11 @@ internal override void InternalWriteObjectContent(XmlWriterDelegator writer, obj Type declaredType = contract.UnderlyingType; Type graphType = (graph == null) ? declaredType : graph.GetType(); + if (_serializationSurrogateProvider != null) + { + graph = DataContractSerializer.SurrogateToDataContractType(_serializationSurrogateProvider, graph, declaredType, ref graphType); + } + if (graph == null) { WriteJsonNull(writer); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializerExtensions.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializerExtensions.cs new file mode 100644 index 0000000000000..0c52dee3789a8 --- /dev/null +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializerExtensions.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Serialization.Json +{ + public static class DataContractJsonSerializerExtensions + { + public static ISerializationSurrogateProvider? GetSerializationSurrogateProvider(this DataContractJsonSerializer serializer) + { + return serializer.SerializationSurrogateProvider; + } + + public static void SetSerializationSurrogateProvider(this DataContractJsonSerializer serializer, ISerializationSurrogateProvider? provider) + { + serializer.SerializationSurrogateProvider = provider; + } + } +} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs index 5518442c8649d..8a6ca1fb0f4c4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs @@ -3,10 +3,11 @@ using System; using System.Collections.Generic; -using System.Text; using System.Diagnostics; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs index 6ff59d9eaa7f2..509b99f968217 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs @@ -1,12 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Threading; -using System.Xml; -using System.Diagnostics; using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Threading; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs index 9d0a40cc5d1c9..cbf92bf3e43e3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; using System.Threading; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs index 4330703dc3ab8..88258b3b397cd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs @@ -2,12 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Runtime; using System.Runtime.Serialization; -using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Xml; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization.Json { @@ -32,7 +35,7 @@ protected JsonDataContract(JsonDataContractCriticalHelper helper) protected DataContract TraditionalDataContract => _helper.TraditionalDataContract; - private Dictionary? KnownDataContracts => _helper.KnownDataContracts; + private DataContractDictionary? KnownDataContracts => _helper.KnownDataContracts; public static JsonReadWriteDelegates? GetGeneratedReadWriteDelegates(DataContract c) { @@ -144,7 +147,7 @@ internal class JsonDataContractCriticalHelper private static readonly TypeHandleRef s_typeHandleRef = new TypeHandleRef(); private static readonly Dictionary s_typeToIDCache = new Dictionary(new TypeHandleRefEqualityComparer()); - private Dictionary? _knownDataContracts; + private DataContractDictionary? _knownDataContracts; private readonly DataContract _traditionalDataContract; private readonly string _typeName; @@ -156,7 +159,7 @@ internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) _typeName = string.IsNullOrEmpty(traditionalDataContract.Namespace.Value) ? traditionalDataContract.Name.Value : string.Concat(traditionalDataContract.Name.Value, JsonGlobals.NameValueSeparatorString, XmlObjectSerializerWriteContextComplexJson.TruncateDefaultDataContractNamespace(traditionalDataContract.Namespace.Value)); } - internal Dictionary? KnownDataContracts => _knownDataContracts; + internal DataContractDictionary? KnownDataContracts => _knownDataContracts; internal DataContract TraditionalDataContract => _traditionalDataContract; @@ -201,6 +204,9 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex); } } @@ -283,15 +289,15 @@ private void AddCollectionItemContractsToKnownDataContracts() while (collectionDataContract != null) { DataContract itemContract = collectionDataContract.ItemContract; - _knownDataContracts ??= new Dictionary(); + _knownDataContracts ??= new DataContractDictionary(); - _knownDataContracts.TryAdd(itemContract.StableName, itemContract); + _knownDataContracts.TryAdd(itemContract.XmlName, itemContract); if (collectionDataContract.ItemType.IsGenericType && collectionDataContract.ItemType.GetGenericTypeDefinition() == typeof(KeyValue<,>)) { DataContract itemDataContract = DataContract.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionDataContract.ItemType.GenericTypeArguments)); - _knownDataContracts.TryAdd(itemDataContract.StableName, itemDataContract); + _knownDataContracts.TryAdd(itemDataContract.XmlName, itemDataContract); } if (!(itemContract is CollectionDataContract)) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs index 76f774cde91f0..e20b49ca92b4e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs index 0df641f90eea0..b094e6fcde688 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs @@ -7,6 +7,7 @@ using System.Xml; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; using System.Runtime.Serialization.Json; namespace System.Runtime.Serialization diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs index ceee570b7475e..9b7dd72c90c93 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Reflection.Emit; using System.Runtime; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Xml; @@ -62,7 +63,7 @@ public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract class bool memberAccessFlag = classContract.RequiresMemberAccessForRead(null); try { - BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(classContract.StableName!.Name) + "FromJson", typeof(JsonFormatClassReaderDelegate), memberAccessFlag); + BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(classContract.XmlName!.Name) + "FromJson", typeof(JsonFormatClassReaderDelegate), memberAccessFlag); } catch (SecurityException securityException) { @@ -109,12 +110,6 @@ public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract class _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamMethod); _ilg.ConvertValue(Globals.TypeOfMemoryStream, _ilg.CurrentMethod.ReturnType); } - //Copy the KeyValuePairAdapter to a KeyValuePair. - else if (classContract.IsKeyValuePairAdapter) - { - _ilg.Call(classContract.GetKeyValuePairMethodInfo); - _ilg.ConvertValue(Globals.TypeOfKeyValuePair.MakeGenericType(classContract.KeyValuePairGenericArguments), _ilg.CurrentMethod.ReturnType); - } else { _ilg.ConvertValue(_objectLocal.LocalType, _ilg.CurrentMethod.ReturnType); @@ -149,11 +144,11 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll { if (isGetOnlyCollection) { - BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.StableName.Name) + "FromJson" + "IsGetOnly", typeof(JsonFormatGetOnlyCollectionReaderDelegate), memberAccessFlag); + BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.XmlName.Name) + "FromJson" + "IsGetOnly", typeof(JsonFormatGetOnlyCollectionReaderDelegate), memberAccessFlag); } else { - BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.StableName.Name) + "FromJson", typeof(JsonFormatCollectionReaderDelegate), memberAccessFlag); + BeginMethod(_ilg, "Read" + DataContract.SanitizeTypeName(collectionContract.XmlName.Name) + "FromJson", typeof(JsonFormatCollectionReaderDelegate), memberAccessFlag); } } catch (SecurityException securityException) @@ -174,7 +169,7 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); + MethodInfo signature = CodeGenerator.GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -228,8 +223,8 @@ private void CreateObject(ClassDataContract classContract) private void InvokeOnDeserializing(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnDeserializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserializing(classContract.BaseClassContract); if (classContract.OnDeserializing != null) { _ilg.LoadAddress(_objectLocal); @@ -242,8 +237,8 @@ private void InvokeOnDeserializing(ClassDataContract classContract) private void InvokeOnDeserialized(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnDeserialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserialized(classContract.BaseClassContract); if (classContract.OnDeserialized != null) { _ilg.LoadAddress(_objectLocal); @@ -291,7 +286,7 @@ private void ReadClass(ClassDataContract classContract) MethodInfo? extensionDataSetMethod = currentContract.ExtensionDataSetMethod; if (extensionDataSetMethod != null) _ilg.Call(_objectLocal, extensionDataSetMethod, extensionDataLocal); - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } } else @@ -352,8 +347,8 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio private int ReadMembers(ClassDataContract classContract, BitFlagsGenerator expectedElements, Label[] memberLabels, Label throwDuplicateMemberLabel, LocalBuilder memberIndexLocal) { - int memberCount = (classContract.BaseContract == null) ? 0 : - ReadMembers(classContract.BaseContract, expectedElements, memberLabels, throwDuplicateMemberLabel, memberIndexLocal); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + ReadMembers(classContract.BaseClassContract, expectedElements, memberLabels, throwDuplicateMemberLabel, memberIndexLocal); for (int i = 0; i < classContract.Members!.Count; i++, memberCount++) { @@ -415,8 +410,8 @@ private void LoadArray(byte[] array, string name) private int SetRequiredElements(ClassDataContract contract, byte[] requiredElements) { - int memberCount = (contract.BaseContract == null) ? 0 : - SetRequiredElements(contract.BaseContract, requiredElements); + int memberCount = (contract.BaseClassContract == null) ? 0 : + SetRequiredElements(contract.BaseClassContract, requiredElements); List members = contract.Members!; for (int i = 0; i < members.Count; i++, memberCount++) { @@ -906,7 +901,7 @@ private bool TryReadPrimitiveArray(Type itemType) return false; string? readArrayMethod = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: readArrayMethod = "TryReadBooleanArray"; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs index afe02b062b6a7..a57df0b14d279 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Xml; @@ -36,14 +37,6 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD return _helper.GenerateCollectionWriter(collectionContract); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The trimmer will never remove the Invoke method from delegates.")] - internal static MethodInfo GetInvokeMethod(Type delegateType) - { - Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType)); - return delegateType.GetMethod("Invoke")!; - } - private sealed class CriticalHelper { private CodeGenerator _ilg = null!; // initialized in GenerateXXXWriter @@ -64,7 +57,7 @@ internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract cla bool memberAccessFlag = classContract.RequiresMemberAccessForWrite(null); try { - BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(classContract.StableName.Name) + "ToJson", typeof(JsonFormatClassWriterDelegate), memberAccessFlag); + BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(classContract.XmlName.Name) + "ToJson", typeof(JsonFormatClassWriterDelegate), memberAccessFlag); } catch (SecurityException securityException) { @@ -90,7 +83,7 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD bool memberAccessFlag = collectionContract.RequiresMemberAccessForWrite(null); try { - BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(collectionContract.StableName.Name) + "ToJson", typeof(JsonFormatCollectionWriterDelegate), memberAccessFlag); + BeginMethod(_ilg, "Write" + DataContract.SanitizeTypeName(collectionContract.XmlName.Name) + "ToJson", typeof(JsonFormatCollectionWriterDelegate), memberAccessFlag); } catch (SecurityException securityException) { @@ -114,7 +107,7 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD private static void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = GetInvokeMethod(delegateType); + MethodInfo signature = CodeGenerator.GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -149,13 +142,6 @@ private void InitArgs(Type objType) _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfMemoryStream); _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamAdapterMethod); } - //Copy the KeyValuePair to a KeyValuePairAdapter. - else if (objType.IsGenericType && objType.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePairAdapter) - { - ClassDataContract dc = (ClassDataContract)DataContract.GetDataContract(objType); - _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfKeyValuePair.MakeGenericType(dc.KeyValuePairGenericArguments!)); - _ilg.New(dc.KeyValuePairAdapterConstructorInfo!); - } else { _ilg.ConvertValue(objectArg.ArgType, objType); @@ -182,8 +168,8 @@ private void ThrowIfCannotSerializeReadOnlyTypes(PropertyInfo serializationExcep private void InvokeOnSerializing(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerializing(classContract.BaseClassContract); if (classContract.OnSerializing != null) { _ilg.LoadAddress(_objectLocal); @@ -195,8 +181,8 @@ private void InvokeOnSerializing(ClassDataContract classContract) private void InvokeOnSerialized(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerialized(classContract.BaseClassContract); if (classContract.OnSerialized != null) { _ilg.LoadAddress(_objectLocal); @@ -238,8 +224,8 @@ private void WriteClass(ClassDataContract classContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { - int memberCount = (classContract.BaseContract == null) ? 0 : - WriteMembers(classContract.BaseContract, extensionDataLocal, derivedMostClassContract); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + WriteMembers(classContract.BaseClassContract, extensionDataLocal, derivedMostClassContract); int classMemberCount = classContract.Members!.Count; _ilg.Call(thisObj: _contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classMemberCount); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs index 078c4c5179339..7e745db47e92f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs @@ -1,10 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Runtime.Serialization; -using System.Globalization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Serialization; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs index 8bc4a2d1f5042..5cf4f54cec7ad 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs @@ -3,10 +3,11 @@ using System; using System.Collections.Generic; -using System.Text; using System.Diagnostics; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs index e65c58c9caed5..1e3379ceb62f2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs @@ -3,10 +3,11 @@ using System; using System.Collections.Generic; -using System.Text; using System.Diagnostics; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Text; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs index bd4b68daf4103..3dc37084b1d74 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs index 99640f43461ac..f08ce85a82ffb 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs @@ -4,9 +4,12 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Xml; +using DataContractDictionary = System.Collections.Generic.Dictionary; + namespace System.Runtime.Serialization.Json { internal sealed class JsonXmlDataContract : JsonDataContract @@ -58,20 +61,20 @@ private static List GetKnownTypesFromContext(XmlObjectSerializerContext? c List knownTypesList = new List(); if (context != null) { - List stableNames = new List(); - Dictionary[] entries = context.scopedKnownTypes.dataContractDictionaries; + List xmlNames = new List(); + DataContractDictionary[] entries = context.scopedKnownTypes.dataContractDictionaries; if (entries != null) { for (int i = 0; i < entries.Length; i++) { - Dictionary entry = entries[i]; + DataContractDictionary entry = entries[i]; if (entry != null) { foreach (KeyValuePair pair in entry) { - if (!stableNames.Contains(pair.Key)) + if (!xmlNames.Contains(pair.Key)) { - stableNames.Add(pair.Key); + xmlNames.Add(pair.Key); knownTypesList.Add(pair.Value.UnderlyingType); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs index 4161f53a29aec..b8564fda29941 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs index 74fbefe525979..b0417289512db 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -27,8 +28,7 @@ public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlOb [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract collectionContract) { - JsonWriterDelegator? jsonWriter = xmlWriter as JsonWriterDelegator; - if (jsonWriter == null) + if (xmlWriter is not JsonWriterDelegator jsonWriter) { throw new ArgumentException(nameof(xmlWriter)); } @@ -146,7 +146,7 @@ private static bool ReflectionTryWritePrimitiveArray(JsonWriterDelegator jsonWri XmlDictionaryString? itemNamespace = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: ReflectionWriteArrayAttribute(jsonWriter); @@ -199,8 +199,8 @@ protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, obje { Debug.Assert(memberNames != null); - int memberCount = (classContract.BaseContract == null) ? 0 : - ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseContract, derivedMostClassContract, childElementIndex, memberNames); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseClassContract, derivedMostClassContract, childElementIndex, memberNames); childElementIndex += memberCount; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs index 9c61887a150b4..e94d7ff80848a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs @@ -3,11 +3,12 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Xml; using System.Runtime.Serialization; -using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization.Json { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs index 8a644b13bec9e..2c5fa8f44a2b5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs @@ -2,13 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Xml; -using System.Reflection; -using System.Collections; -using System.IO; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { @@ -255,7 +256,7 @@ internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, private void VerifyType(DataContract dataContract, Type declaredType) { bool knownTypesAddedInCurrentScope = false; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); knownTypesAddedInCurrentScope = true; @@ -263,7 +264,7 @@ private void VerifyType(DataContract dataContract, Type declaredType) if (!IsKnownType(dataContract, declaredType)) { - throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace)); + throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.XmlName.Name, dataContract.XmlName.Namespace)); } if (knownTypesAddedInCurrentScope) @@ -383,7 +384,7 @@ internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHan [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal static DataContract? ResolveJsonDataContractFromRootDataContract(XmlObjectSerializerContext context, XmlQualifiedName typeQName, DataContract rootTypeDataContract) { - if (rootTypeDataContract.StableName == typeQName) + if (rootTypeDataContract.XmlName == typeQName) return rootTypeDataContract; CollectionDataContract? collectionContract = rootTypeDataContract as CollectionDataContract; @@ -399,7 +400,7 @@ internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHan { itemContract = context.GetDataContract(context.GetSurrogatedType(collectionContract.ItemType)); } - if (itemContract.StableName == typeQName) + if (itemContract.XmlName == typeQName) { return itemContract; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs index ab1d030913b57..ca96ede659dab 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/KnownTypeDataContractResolver.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Reflection; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs index 8cd388f9601b0..638ba22d8842f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs @@ -9,10 +9,13 @@ using System.Reflection; using System.Xml; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal abstract class PrimitiveDataContract : DataContract { + internal const string ContractTypeString = nameof(PrimitiveDataContract); + public override string? ContractType => ContractTypeString; + internal static readonly PrimitiveDataContract NullContract = new NullPrimitiveDataContract(); private readonly PrimitiveDataContractCriticalHelper _helper; @@ -44,7 +47,7 @@ public override XmlDictionaryString? TopLevelElementNamespace get { return DictionaryGlobals.SerializationNamespace; } - set + internal set { } } @@ -90,7 +93,7 @@ internal MethodInfo XmlFormatContentWriterMethod _helper.XmlFormatReaderMethod ??= typeof(XmlReaderDelegator).GetMethod(ReadMethodName, Globals.ScanAllMembers)!; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { xmlWriter.WriteAnyType(obj); } @@ -115,6 +118,17 @@ protected static bool TryReadNullAtTopLevel(XmlReaderDelegator reader) return false; } + internal override bool Equals(object? other, HashSet? checkedContracts) + { + if (other is PrimitiveDataContract) + { + Type thisType = GetType(); + Type otherType = other.GetType(); + return (thisType.Equals(otherType) || thisType.IsSubclassOf(otherType) || otherType.IsSubclassOf(thisType)); + } + return false; + } + private sealed class PrimitiveDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private MethodInfo? _xmlFormatWriterMethod; @@ -131,20 +145,20 @@ internal PrimitiveDataContractCriticalHelper( internal MethodInfo? XmlFormatWriterMethod { - get { return _xmlFormatWriterMethod; } - set { _xmlFormatWriterMethod = value; } + get => _xmlFormatWriterMethod; + set => _xmlFormatWriterMethod = value; } internal MethodInfo? XmlFormatContentWriterMethod { - get { return _xmlFormatContentWriterMethod; } - set { _xmlFormatContentWriterMethod = value; } + get => _xmlFormatContentWriterMethod; + set => _xmlFormatContentWriterMethod = value; } internal MethodInfo? XmlFormatReaderMethod { - get { return _xmlFormatReaderMethod; } - set { _xmlFormatReaderMethod = value; } + get => _xmlFormatReaderMethod; + set => _xmlFormatReaderMethod = value; } } } @@ -159,24 +173,24 @@ internal CharDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba { } - internal override string WriteMethodName { get { return "WriteChar"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsChar"; } } + internal override string WriteMethodName => "WriteChar"; + internal override string ReadMethodName => "ReadElementContentAsChar"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteChar((char)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsChar() : HandleReadValue(reader.ReadElementContentAsChar(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteChar((char)obj!, name, ns); } @@ -193,24 +207,24 @@ public BooleanDataContract() : base(typeof(bool), DictionaryGlobals.BooleanLocal { } - internal override string WriteMethodName { get { return "WriteBoolean"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsBoolean"; } } + internal override string WriteMethodName => "WriteBoolean"; + internal override string ReadMethodName => "ReadElementContentAsBoolean"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBoolean((bool)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsBoolean() : HandleReadValue(reader.ReadElementContentAsBoolean(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteBoolean((bool)obj!, name, ns); } @@ -222,24 +236,24 @@ public SignedByteDataContract() : base(typeof(sbyte), DictionaryGlobals.SignedBy { } - internal override string WriteMethodName { get { return "WriteSignedByte"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsSignedByte"; } } + internal override string WriteMethodName => "WriteSignedByte"; + internal override string ReadMethodName => "ReadElementContentAsSignedByte"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteSignedByte((sbyte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsSignedByte() : HandleReadValue(reader.ReadElementContentAsSignedByte(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteSignedByte((sbyte)obj!, name, ns); } @@ -251,24 +265,24 @@ public UnsignedByteDataContract() : base(typeof(byte), DictionaryGlobals.Unsigne { } - internal override string WriteMethodName { get { return "WriteUnsignedByte"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedByte"; } } + internal override string WriteMethodName => "WriteUnsignedByte"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedByte"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedByte((byte)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedByte() : HandleReadValue(reader.ReadElementContentAsUnsignedByte(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedByte((byte)obj!, name, ns); } @@ -280,24 +294,24 @@ public ShortDataContract() : base(typeof(short), DictionaryGlobals.ShortLocalNam { } - internal override string WriteMethodName { get { return "WriteShort"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsShort"; } } + internal override string WriteMethodName => "WriteShort"; + internal override string ReadMethodName => "ReadElementContentAsShort"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteShort((short)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsShort() : HandleReadValue(reader.ReadElementContentAsShort(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteShort((short)obj!, name, ns); } @@ -309,24 +323,24 @@ public UnsignedShortDataContract() : base(typeof(ushort), DictionaryGlobals.Unsi { } - internal override string WriteMethodName { get { return "WriteUnsignedShort"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedShort"; } } + internal override string WriteMethodName => "WriteUnsignedShort"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedShort"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedShort((ushort)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedShort() : HandleReadValue(reader.ReadElementContentAsUnsignedShort(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedShort((ushort)obj!, name, ns); } @@ -364,19 +378,19 @@ internal override string WriteMethodName } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { throw new NotImplementedException(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { throw new NotImplementedException(); } @@ -388,24 +402,24 @@ public IntDataContract() : base(typeof(int), DictionaryGlobals.IntLocalName, Dic { } - internal override string WriteMethodName { get { return "WriteInt"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsInt"; } } + internal override string WriteMethodName => "WriteInt"; + internal override string ReadMethodName => "ReadElementContentAsInt"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteInt((int)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsInt() : HandleReadValue(reader.ReadElementContentAsInt(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteInt((int)obj!, name, ns); } @@ -417,24 +431,24 @@ public UnsignedIntDataContract() : base(typeof(uint), DictionaryGlobals.Unsigned { } - internal override string WriteMethodName { get { return "WriteUnsignedInt"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedInt"; } } + internal override string WriteMethodName => "WriteUnsignedInt"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedInt"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedInt((uint)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedInt() : HandleReadValue(reader.ReadElementContentAsUnsignedInt(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedInt((uint)obj!, name, ns); } @@ -450,24 +464,24 @@ internal LongDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba { } - internal override string WriteMethodName { get { return "WriteLong"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsLong"; } } + internal override string WriteMethodName => "WriteLong"; + internal override string ReadMethodName => "ReadElementContentAsLong"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteLong((long)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsLong() : HandleReadValue(reader.ReadElementContentAsLong(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteLong((long)obj!, name, ns); } @@ -504,24 +518,24 @@ public UnsignedLongDataContract() : base(typeof(ulong), DictionaryGlobals.Unsign { } - internal override string WriteMethodName { get { return "WriteUnsignedLong"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUnsignedLong"; } } + internal override string WriteMethodName => "WriteUnsignedLong"; + internal override string ReadMethodName => "ReadElementContentAsUnsignedLong"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUnsignedLong((ulong)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsUnsignedLong() : HandleReadValue(reader.ReadElementContentAsUnsignedLong(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteUnsignedLong((ulong)obj!, name, ns); } @@ -533,24 +547,24 @@ public FloatDataContract() : base(typeof(float), DictionaryGlobals.FloatLocalNam { } - internal override string WriteMethodName { get { return "WriteFloat"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsFloat"; } } + internal override string WriteMethodName => "WriteFloat"; + internal override string ReadMethodName => "ReadElementContentAsFloat"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteFloat((float)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsFloat() : HandleReadValue(reader.ReadElementContentAsFloat(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteFloat((float)obj!, name, ns); } @@ -562,24 +576,24 @@ public DoubleDataContract() : base(typeof(double), DictionaryGlobals.DoubleLocal { } - internal override string WriteMethodName { get { return "WriteDouble"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsDouble"; } } + internal override string WriteMethodName => "WriteDouble"; + internal override string ReadMethodName => "ReadElementContentAsDouble"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDouble((double)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDouble() : HandleReadValue(reader.ReadElementContentAsDouble(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDouble((double)obj!, name, ns); } @@ -591,24 +605,24 @@ public DecimalDataContract() : base(typeof(decimal), DictionaryGlobals.DecimalLo { } - internal override string WriteMethodName { get { return "WriteDecimal"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsDecimal"; } } + internal override string WriteMethodName => "WriteDecimal"; + internal override string ReadMethodName => "ReadElementContentAsDecimal"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDecimal((decimal)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDecimal() : HandleReadValue(reader.ReadElementContentAsDecimal(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDecimal((decimal)obj!, name, ns); } @@ -624,24 +638,24 @@ public DateTimeDataContract() : base(typeof(DateTime), DictionaryGlobals.DateTim { } - internal override string WriteMethodName { get { return "WriteDateTime"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsDateTime"; } } + internal override string WriteMethodName => "WriteDateTime"; + internal override string ReadMethodName => "ReadElementContentAsDateTime"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteDateTime((DateTime)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsDateTime() : HandleReadValue(reader.ReadElementContentAsDateTime(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteDateTime((DateTime)obj!, name, ns); } @@ -657,17 +671,17 @@ internal StringDataContract(XmlDictionaryString name, XmlDictionaryString ns) : { } - internal override string WriteMethodName { get { return "WriteString"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsString"; } } + internal override string WriteMethodName => "WriteString"; + internal override string ReadMethodName => "ReadElementContentAsString"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteString((string)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -680,7 +694,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteString(xmlWriter, (string?)obj, name, ns); } @@ -792,17 +806,17 @@ public ByteArrayDataContract() : base(typeof(byte[]), DictionaryGlobals.ByteArra { } - internal override string WriteMethodName { get { return "WriteBase64"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsBase64"; } } + internal override string WriteMethodName => "WriteBase64"; + internal override string ReadMethodName => "ReadElementContentAsBase64"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteBase64((byte[])obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -815,7 +829,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteStartElement(name, ns); xmlWriter.WriteBase64((byte[]?)obj); @@ -829,20 +843,20 @@ public ObjectDataContract() : base(typeof(object), DictionaryGlobals.ObjectLocal { } - internal override string WriteMethodName { get { return "WriteAnyType"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsAnyType"; } } + internal override string WriteMethodName => "WriteAnyType"; + internal override string ReadMethodName => "ReadElementContentAsAnyType"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { // write nothing } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { object obj; - if (XmlReaderDelegator.IsEmptyElement) + if (reader.IsEmptyElement) { reader.Skip(); obj = new object(); @@ -865,15 +879,9 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj return (context == null) ? obj : HandleReadValue(obj, context); } - internal override bool CanContainReferences - { - get { return true; } - } + internal override bool CanContainReferences => true; - internal override bool IsPrimitive - { - get { return false; } - } + internal override bool IsPrimitive => false; } internal class TimeSpanDataContract : PrimitiveDataContract @@ -886,24 +894,24 @@ internal TimeSpanDataContract(XmlDictionaryString name, XmlDictionaryString ns) { } - internal override string WriteMethodName { get { return "WriteTimeSpan"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsTimeSpan"; } } + internal override string WriteMethodName => "WriteTimeSpan"; + internal override string ReadMethodName => "ReadElementContentAsTimeSpan"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteTimeSpan((TimeSpan)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsTimeSpan() : HandleReadValue(reader.ReadElementContentAsTimeSpan(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteTimeSpan((TimeSpan)obj!, name, ns); } @@ -924,24 +932,24 @@ internal GuidDataContract(XmlDictionaryString name, XmlDictionaryString ns) : ba { } - internal override string WriteMethodName { get { return "WriteGuid"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsGuid"; } } + internal override string WriteMethodName => "WriteGuid"; + internal override string ReadMethodName => "ReadElementContentAsGuid"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteGuid((Guid)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { return (context == null) ? reader.ReadElementContentAsGuid() : HandleReadValue(reader.ReadElementContentAsGuid(), context); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { xmlWriter.WriteGuid((Guid)obj!, name, ns); } @@ -958,17 +966,17 @@ public UriDataContract() : base(typeof(Uri), DictionaryGlobals.UriLocalName, Dic { } - internal override string WriteMethodName { get { return "WriteUri"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsUri"; } } + internal override string WriteMethodName => "WriteUri"; + internal override string ReadMethodName => "ReadElementContentAsUri"; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteUri((Uri)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -981,7 +989,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { writer.WriteUri((Uri?)obj, name, ns); } @@ -993,22 +1001,19 @@ public QNameDataContract() : base(typeof(XmlQualifiedName), DictionaryGlobals.QN { } - internal override string WriteMethodName { get { return "WriteQName"; } } - internal override string ReadMethodName { get { return "ReadElementContentAsQName"; } } + internal override string WriteMethodName => "WriteQName"; + internal override string ReadMethodName => "ReadElementContentAsQName"; - internal override bool IsPrimitive - { - get { return false; } - } + internal override bool IsPrimitive => false; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context) { writer.WriteQName((XmlQualifiedName)obj); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context) { if (context == null) { @@ -1021,7 +1026,7 @@ public override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObj } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) + internal override void WriteXmlElement(XmlWriterDelegator writer, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns) { context.WriteQName(writer, (XmlQualifiedName?)obj, name, ns); } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs index 3cc3c97a7261e..10c2cc1f199f6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionClassWriter.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -124,8 +125,8 @@ protected static bool ReflectionTryWritePrimitive(XmlWriterDelegator xmlWriter, private void InvokeOnSerializing(object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerializing(obj, context, classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerializing(obj, context, classContract.BaseClassContract); if (classContract.OnSerializing != null) { var contextArg = context.GetStreamingContext(); @@ -135,8 +136,8 @@ private void InvokeOnSerializing(object obj, XmlObjectSerializerWriteContext con private void InvokeOnSerialized(object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerialized(obj, context, classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerialized(obj, context, classContract.BaseClassContract); if (classContract.OnSerialized != null) { var contextArg = context.GetStreamingContext(); @@ -155,11 +156,6 @@ private static object ResolveAdapterType(object obj, ClassDataContract classCont { obj = MemoryStreamAdapter.GetMemoryStreamAdapter((MemoryStream)obj); } - else if (type.IsGenericType && type.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePair) - { - obj = classContract.KeyValuePairAdapterConstructorInfo!.Invoke(new object[] { obj }); - } - return obj; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs deleted file mode 100644 index d071056761ce3..0000000000000 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.Serialization -{ - internal static class ReflectionBasedSerializationFeature - { - public const string Name = "System.Runtime.Serialization.ReflectionBasedSerialization"; - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs index b28f41784478c..a1982aa1ac3db 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionReader.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -197,7 +198,7 @@ protected virtual bool ReflectionReadSpecialCollection(XmlReaderDelegator xmlRea protected int ReflectionGetMembers(ClassDataContract classContract, DataMember[] members) { - int memberCount = (classContract.BaseContract == null) ? 0 : ReflectionGetMembers(classContract.BaseContract, members); + int memberCount = (classContract.BaseClassContract == null) ? 0 : ReflectionGetMembers(classContract.BaseClassContract, members); int childElementIndex = memberCount; for (int i = 0; i < classContract.Members!.Count; i++, memberCount++) { @@ -222,7 +223,7 @@ protected void ReflectionReadMember(XmlReaderDelegator xmlReader, XmlObjectSeria else { context.ResetCollectionMemberInfo(); - var value = ReflectionReadValue(xmlReader, context, dataMember, classContract.StableName.Namespace); + var value = ReflectionReadValue(xmlReader, context, dataMember, classContract.XmlName.Namespace); MemberInfo memberInfo = dataMember.MemberInfo; Debug.Assert(memberInfo != null); @@ -377,8 +378,8 @@ private static void ReflectionSetMemberValue(ref object obj, object? memberValue private void InvokeOnDeserializing(XmlObjectSerializerReadContext context, ClassDataContract classContract, object obj) { - if (classContract.BaseContract != null) - InvokeOnDeserializing(context, classContract.BaseContract, obj); + if (classContract.BaseClassContract != null) + InvokeOnDeserializing(context, classContract.BaseClassContract, obj); if (classContract.OnDeserializing != null) { var contextArg = context.GetStreamingContext(); @@ -388,8 +389,8 @@ private void InvokeOnDeserializing(XmlObjectSerializerReadContext context, Class private void InvokeOnDeserialized(XmlObjectSerializerReadContext context, ClassDataContract classContract, object obj) { - if (classContract.BaseContract != null) - InvokeOnDeserialized(context, classContract.BaseContract, obj); + if (classContract.BaseClassContract != null) + InvokeOnDeserialized(context, classContract.BaseClassContract, obj); if (classContract.OnDeserialized != null) { var contextArg = context.GetStreamingContext(); @@ -426,11 +427,6 @@ private static object ResolveAdapterObject(object obj, ClassDataContract classCo { obj = MemoryStreamAdapter.GetMemoryStream((MemoryStreamAdapter)obj); } - else if (obj is IKeyValuePairAdapter) - { - obj = classContract.GetKeyValuePairMethodInfo!.Invoke(obj, Array.Empty())!; - } - return obj; } @@ -612,7 +608,7 @@ private static bool ReflectionTryReadPrimitiveArray(XmlReaderDelegator xmlReader if (primitiveContract == null) return false; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs index 98e3c1ea9a4ff..6c31a151498d7 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatReader.cs @@ -2,15 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Xml; -using System.Xml.Schema; -using System.Reflection; -using System.Reflection.Emit; using System.Collections; using System.Collections.Generic; -using System.Security; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { @@ -101,7 +102,7 @@ protected override void ReflectionReadMembers(XmlReaderDelegator xmlReader, XmlO protected override string GetClassContractNamespace(ClassDataContract classContract) { - return classContract.StableName!.Namespace; + return classContract.XmlName!.Namespace; } protected override string GetCollectionContractItemName(CollectionDataContract collectionContract) @@ -111,7 +112,7 @@ protected override string GetCollectionContractItemName(CollectionDataContract c protected override string GetCollectionContractNamespace(CollectionDataContract collectionContract) { - return collectionContract.StableName.Namespace; + return collectionContract.XmlName.Namespace; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -135,7 +136,7 @@ private bool[] GetRequiredMembers(ClassDataContract contract, out int firstRequi private int GetRequiredMembers(ClassDataContract contract, bool[] requiredMembers) { - int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers(contract.BaseContract, requiredMembers); + int memberCount = (contract.BaseClassContract == null) ? 0 : GetRequiredMembers(contract.BaseClassContract, requiredMembers); List members = contract.Members!; for (int i = 0; i < members.Count; i++, memberCount++) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs index dc70a9cb4ae83..339cb9c1021fb 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Serialization.DataContracts; using System.Xml; namespace System.Runtime.Serialization @@ -95,7 +96,7 @@ private static bool ReflectionTryWritePrimitiveArray(XmlWriterDelegator xmlWrite if (primitiveContract == null) return false; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: xmlWriter.WriteBooleanArray((bool[])obj, collectionItemName, itemNamespace); @@ -131,8 +132,8 @@ internal sealed class ReflectionXmlClassWriter : ReflectionClassWriter [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? emptyStringArray) { - int memberCount = (classContract.BaseContract == null) ? 0 : - ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseContract, derivedMostClassContract, childElementIndex, emptyStringArray); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseClassContract, derivedMostClassContract, childElementIndex, emptyStringArray); childElementIndex += memberCount; @@ -230,11 +231,11 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac // Check for conflict with derived type members string? name = member.Name; - string? ns = classContract.StableName.Namespace; + string? ns = classContract.XmlName.Namespace; ClassDataContract? currentContract = derivedMostClassContract; while (currentContract != null && currentContract != classContract) { - if (ns == currentContract.StableName.Namespace) + if (ns == currentContract.XmlName.Namespace) { List members = currentContract.Members!; for (int j = 0; j < members.Count; j++) @@ -243,7 +244,7 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac return CheckIfConflictingMembersHaveDifferentTypes(members[j]); } } - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } return false; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs index 1c76e5760047e..976d9880a41d4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs @@ -10,6 +10,7 @@ using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -42,7 +43,7 @@ internal void Export() { // Remove this if we decide to publish serialization schema at well-known location ExportSerializationSchema(); - foreach (KeyValuePair pair in _dataContractSet) + foreach (KeyValuePair pair in _dataContractSet.Contracts) { DataContract dataContract = pair.Value; if (!_dataContractSet.IsContractProcessed(dataContract)) @@ -80,7 +81,7 @@ private void ExportDataContract(DataContract dataContract) ExportXmlDataContract((XmlDataContract)dataContract); else { - XmlSchema schema = GetSchema(dataContract.StableName.Namespace); + XmlSchema schema = GetSchema(dataContract.XmlName.Namespace); if (dataContract is ClassDataContract classDataContract) { @@ -100,7 +101,7 @@ private void ExportDataContract(DataContract dataContract) private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSchema? schema) { - if (schema == null || dataContract.StableName.Namespace != dataContract.TopLevelElementNamespace!.Value) + if (schema == null || dataContract.XmlName.Namespace != dataContract.TopLevelElementNamespace!.Value) schema = GetSchema(dataContract.TopLevelElementNamespace!.Value); XmlSchemaElement topLevelElement = new XmlSchemaElement(); @@ -115,7 +116,7 @@ private XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSch private void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); - type.Name = classDataContract.StableName.Name; + type.Name = classDataContract.XmlName.Name; schema.Items.Add(type); XmlElement? genericInfoElement = null; if (classDataContract.UnderlyingType.IsGenericType) @@ -129,12 +130,12 @@ private void ExportClassDataContract(ClassDataContract classDataContract, XmlSch XmlSchemaElement element = new XmlSchemaElement(); element.Name = dataMember.Name; XmlElement? actualTypeElement = null; - DataContract memberTypeContract = DataContractSet.GetMemberTypeDataContract(dataMember); + DataContract memberTypeContract = _dataContractSet.GetMemberTypeDataContract(dataMember); if (CheckIfMemberHasConflict(dataMember)) { element.SchemaTypeName = AnytypeQualifiedName; - actualTypeElement = ExportActualType(memberTypeContract.StableName); - SchemaHelper.AddSchemaImport(memberTypeContract.StableName.Namespace, schema); + actualTypeElement = ExportActualType(memberTypeContract.XmlName); + SchemaHelper.AddSchemaImport(memberTypeContract.XmlName.Namespace, schema); } else SetElementType(element, memberTypeContract, schema); @@ -149,11 +150,11 @@ private void ExportClassDataContract(ClassDataContract classDataContract, XmlSch } XmlElement? isValueTypeElement = null; - if (classDataContract.BaseContract != null) + if (classDataContract.BaseClassContract != null) { - XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseContract.StableName, schema); + XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseClassContract.XmlName, schema); extension.Particle = rootSequence; - if (classDataContract.IsReference && !classDataContract.BaseContract.IsReference) + if (classDataContract.IsReference && !classDataContract.BaseClassContract.IsReference) { AddReferenceAttributes(extension.Attributes, schema); } @@ -179,19 +180,18 @@ private static void AddReferenceAttributes(XmlSchemaObjectCollection attributes, private static void SetElementType(XmlSchemaElement element, DataContract dataContract, XmlSchema schema) { - XmlDataContract? xmlDataContract = dataContract as XmlDataContract; - if (xmlDataContract != null && xmlDataContract.IsAnonymous) + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) { element.SchemaType = xmlDataContract.XsdType; } else { - element.SchemaTypeName = dataContract.StableName; + element.SchemaTypeName = dataContract.XmlName; if (element.SchemaTypeName.Namespace.Equals(Globals.SerializationNamespace)) schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace); - SchemaHelper.AddSchemaImport(dataContract.StableName.Namespace, schema); + SchemaHelper.AddSchemaImport(dataContract.XmlName.Namespace, schema); } } @@ -279,7 +279,7 @@ private XmlElement ExportGenericInfo(Type clrType, string elementName, string el genericArgumentCounts = DataContract.GetDataContractNameForGenericName(typeName, null); clrType = clrType.GetGenericTypeDefinition(); } - XmlQualifiedName dcqname = DataContract.GetStableName(clrType); + XmlQualifiedName dcqname = DataContract.GetXmlName(clrType); if (nestedCollectionLevel > 0) { string collectionName = dcqname.Name; @@ -329,17 +329,32 @@ private XmlElement ExportGenericInfo(Type clrType, string elementName, string el return typeElement; } - private static XmlElement? ExportSurrogateData(object key) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private XmlElement? ExportSurrogateData(object key) { - // IDataContractSurrogate is not available on NetCore. - return null; + object? surrogateData = _dataContractSet.GetSurrogateData(key); + if (surrogateData == null) + return null; + StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); + XmlWriterSettings writerSettings = new XmlWriterSettings(); + writerSettings.OmitXmlDeclaration = true; + XmlWriter xmlWriter = XmlWriter.Create(stringWriter, writerSettings); + Collection knownTypes = new Collection(); + if (_dataContractSet.SerializationExtendedSurrogateProvider != null) + DataContractSurrogateCaller.GetKnownCustomDataTypes(_dataContractSet.SerializationExtendedSurrogateProvider, knownTypes); + DataContractSerializer serializer = new DataContractSerializer(Globals.TypeOfObject, + SurrogateDataAnnotationName.Name, SurrogateDataAnnotationName.Namespace, knownTypes, int.MaxValue, + ignoreExtensionDataObject: false, preserveObjectReferences: true); + serializer.WriteObject(xmlWriter, surrogateData); + xmlWriter.Flush(); + return (XmlElement?)XmlDoc.ReadNode(XmlReader.Create(new StringReader(stringWriter.ToString()))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); - type.Name = collectionDataContract.StableName.Name; + type.Name = collectionDataContract.XmlName.Name; schema.Items.Add(type); XmlElement? genericInfoElement = null, isDictionaryElement = null; if (collectionDataContract.UnderlyingType.IsGenericType && CollectionDataContract.IsCollectionDataContract(collectionDataContract.UnderlyingType)) @@ -363,7 +378,7 @@ private void ExportCollectionDataContract(CollectionDataContract collectionDataC { XmlSchemaElement keyValueElement = new XmlSchemaElement(); keyValueElement.Name = dataMember.Name; - SetElementType(keyValueElement, DataContractSet.GetMemberTypeDataContract(dataMember), schema); + SetElementType(keyValueElement, _dataContractSet.GetMemberTypeDataContract(dataMember), schema); SchemaHelper.AddElementForm(keyValueElement, schema); if (dataMember.IsNullable) keyValueElement.IsNillable = true; @@ -377,7 +392,7 @@ private void ExportCollectionDataContract(CollectionDataContract collectionDataC { if (collectionDataContract.IsItemTypeNullable) element.IsNillable = true; - DataContract itemContract = DataContractSet.GetItemTypeDataContract(collectionDataContract); + DataContract itemContract = _dataContractSet.GetItemTypeDataContract(collectionDataContract); SetElementType(element, itemContract, schema); } SchemaHelper.AddElementForm(element, schema); @@ -396,12 +411,11 @@ private XmlElement ExportIsDictionary() return isDictionaryElement; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private void ExportEnumDataContract(EnumDataContract enumDataContract, XmlSchema schema) { XmlSchemaSimpleType type = new XmlSchemaSimpleType(); - type.Name = enumDataContract.StableName.Name; - // https://github.com/dotnet/runtime/issues/41448 - enumDataContract.BaseContractName is always null, but this method is not reachable - Debug.Assert(enumDataContract.BaseContractName != null, "BaseContractName is always null, but this method is not reachable. Suppressing compiler error."); + type.Name = enumDataContract.XmlName.Name; XmlElement? actualTypeElement = (enumDataContract.BaseContractName == DefaultEnumBaseTypeName) ? null : ExportActualType(enumDataContract.BaseContractName); type.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(enumDataContract)); schema.Items.Add(type); @@ -441,16 +455,16 @@ internal static long GetDefaultEnumValue(bool isFlags, int index) private void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema) { XmlSchemaComplexType type = new XmlSchemaComplexType(); - type.Name = dataContract.StableName.Name; + type.Name = dataContract.XmlName.Name; schema.Items.Add(type); XmlElement? genericInfoElement = null; if (dataContract.UnderlyingType.IsGenericType) genericInfoElement = ExportGenericInfo(dataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace); XmlElement? isValueTypeElement = null; - if (dataContract.BaseContract != null) + if (dataContract.BaseClassContract != null) { - _ = CreateTypeContent(type, dataContract.BaseContract.StableName, schema); + _ = CreateTypeContent(type, dataContract.BaseClassContract.XmlName, schema); } else { @@ -491,7 +505,7 @@ private void ExportXmlDataContract(XmlDataContract dataContract) if (hasRoot) { - if (!(typeQName.Equals(dataContract.StableName))) + if (!(typeQName.Equals(dataContract.XmlName))) { Fx.Assert("XML data contract type name does not match schema name"); } @@ -565,26 +579,26 @@ private static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate it } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) + internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot) { - if (IsSpecialXmlType(type, out stableName!, out xsdType, out hasRoot)) + if (IsSpecialXmlType(type, out xmlName!, out xsdType, out hasRoot)) return; XmlSchemaSet schemas = new XmlSchemaSet(); schemas.XmlResolver = null; - InvokeSchemaProviderMethod(type, schemas, out stableName, out xsdType, out hasRoot); - if (stableName.Name == null || stableName.Name.Length == 0) + InvokeSchemaProviderMethod(type, schemas, out xmlName, out xsdType, out hasRoot); + if (xmlName.Name == null || xmlName.Name.Length == 0) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidXmlDataContractName, DataContract.GetClrTypeFullName(type)))); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName stableName, out XmlSchemaType? xsdType, out bool hasRoot) + private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName xmlName, out XmlSchemaType? xsdType, out bool hasRoot) { xsdType = null; hasRoot = true; object[] attrs = clrType.GetCustomAttributes(Globals.TypeOfXmlSchemaProviderAttribute, false); if (attrs == null || attrs.Length == 0) { - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); return false; } @@ -599,7 +613,7 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema { if (!provider.IsAny) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidGetSchemaMethod, DataContract.GetClrTypeFullName(clrType)))); - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); } else { @@ -616,26 +630,25 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema { if (typeInfo != null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidNonNullReturnValueByIsAny, DataContract.GetClrTypeFullName(clrType), methodName))); - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); } else if (typeInfo == null) { xsdType = CreateAnyElementType(); hasRoot = false; - stableName = DataContract.GetDefaultStableName(clrType); + xmlName = DataContract.GetDefaultXmlName(clrType); } else { - XmlSchemaType? providerXsdType = typeInfo as XmlSchemaType; - if (providerXsdType != null) + if (typeInfo is XmlSchemaType providerXsdType) { string? typeName = providerXsdType.Name; string? typeNs = null; if (typeName == null || typeName.Length == 0) { - DataContract.GetDefaultStableName(DataContract.GetClrTypeFullName(clrType), out typeName, out typeNs); - stableName = new XmlQualifiedName(typeName, typeNs); - providerXsdType.Annotation = GetSchemaAnnotation(ExportActualType(stableName, new XmlDocument())); + DataContract.GetDefaultXmlName(DataContract.GetClrTypeFullName(clrType), out typeName, out typeNs); + xmlName = new XmlQualifiedName(typeName, typeNs); + providerXsdType.Annotation = GetSchemaAnnotation(ExportActualType(xmlName, new XmlDocument())); xsdType = providerXsdType; } else @@ -655,11 +668,11 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema } if (typeNs == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.MissingSchemaType, typeName, DataContract.GetClrTypeFullName(clrType)))); - stableName = new XmlQualifiedName(typeName, typeNs); + xmlName = new XmlQualifiedName(typeName, typeNs); } } else - stableName = (XmlQualifiedName)typeInfo; + xmlName = (XmlQualifiedName)typeInfo; } } return true; @@ -668,19 +681,19 @@ private static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schema private static void InvokeGetSchemaMethod( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type clrType, - XmlSchemaSet schemas, XmlQualifiedName stableName) + XmlSchemaSet schemas, XmlQualifiedName xmlName) { IXmlSerializable ixmlSerializable = (IXmlSerializable)Activator.CreateInstance(clrType)!; XmlSchema? schema = ixmlSerializable.GetSchema(); if (schema == null) { - AddDefaultDatasetType(schemas, stableName.Name, stableName.Namespace); + AddDefaultDatasetType(schemas, xmlName.Name, xmlName.Namespace); } else { if (schema.Id == null || schema.Id.Length == 0) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidReturnSchemaOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType)))); - AddDefaultTypedDatasetType(schemas, schema, stableName.Name, stableName.Namespace); + AddDefaultTypedDatasetType(schemas, schema, xmlName.Name, xmlName.Namespace); } } @@ -738,7 +751,7 @@ internal static bool IsSpecialXmlType(Type type, [NotNullWhen(true)] out XmlQual name = "ArrayOfXmlNode"; hasRoot = true; } - typeName = new XmlQualifiedName(name, DataContract.GetDefaultStableNamespace(type)); + typeName = new XmlQualifiedName(name, DataContract.GetDefaultXmlNamespace(type)); return true; } typeName = null; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs index da820ba98babb..2a632482b459e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs @@ -7,11 +7,40 @@ using System.Collections; using System.Collections.Generic; +using SchemaObjectDictionary = System.Collections.Generic.Dictionary; + namespace System.Runtime.Serialization { - internal static class SchemaHelper + internal sealed class SchemaObjectInfo { + internal XmlSchemaType? _type; + internal XmlSchemaElement? _element; + internal XmlSchema? _schema; + internal List? _knownTypes; + internal SchemaObjectInfo(XmlSchemaType? type, XmlSchemaElement? element, XmlSchema? schema, List? knownTypes) + { + _type = type; + _element = element; + _schema = schema; + _knownTypes = knownTypes; + } + } + + internal sealed class SchemaDefinedType + { + internal XmlQualifiedName _xmlName; + + public SchemaDefinedType(XmlQualifiedName xmlName) + { + _xmlName = xmlName; + } + } + + internal enum SchemaDefinedEnum { SchemaDefinedEnumValue }; + + internal static class SchemaHelper + { internal static bool NamespacesEqual(string? ns1, string? ns2) { if (ns1 == null || ns1.Length == 0) @@ -20,6 +49,16 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) return ns1 == ns2; } + internal static XmlSchemaType? GetSchemaType(SchemaObjectDictionary schemaInfo, XmlQualifiedName typeName) + { + SchemaObjectInfo? schemaObjectInfo; + if (schemaInfo.TryGetValue(typeName, out schemaObjectInfo)) + { + return schemaObjectInfo._type; + } + return null; + } + internal static XmlSchemaType? GetSchemaType(XmlSchemaSet schemas, XmlQualifiedName typeQName, out XmlSchema? outSchema) { outSchema = null; @@ -32,8 +71,7 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) outSchema = schema; foreach (XmlSchemaObject schemaObj in schema.Items) { - XmlSchemaType? schemaType = schemaObj as XmlSchemaType; - if (schemaType != null && schemaType.Name == typeQName.Name) + if (schemaObj is XmlSchemaType schemaType && schemaType.Name == typeQName.Name) { return schemaType; } @@ -43,6 +81,16 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) return null; } + internal static XmlSchemaElement? GetSchemaElement(SchemaObjectDictionary schemaInfo, XmlQualifiedName elementName) + { + SchemaObjectInfo? schemaObjectInfo; + if (schemaInfo.TryGetValue(elementName, out schemaObjectInfo)) + { + return schemaObjectInfo._element; + } + return null; + } + internal static XmlSchemaElement? GetSchemaElement(XmlSchemaSet schemas, XmlQualifiedName elementQName, out XmlSchema? outSchema) { outSchema = null; @@ -55,8 +103,7 @@ internal static bool NamespacesEqual(string? ns1, string? ns2) outSchema = schema; foreach (XmlSchemaObject schemaObj in schema.Items) { - XmlSchemaElement? schemaElement = schemaObj as XmlSchemaElement; - if (schemaElement != null && schemaElement.Name == elementQName.Name) + if (schemaObj is XmlSchemaElement schemaElement && schemaElement.Name == elementQName.Name) { return schemaElement; } @@ -124,5 +171,59 @@ internal static void AddSchemaImport(string ns, XmlSchema schema) import.Namespace = ns; schema.Includes.Add(import); } + + internal static XmlSchema? GetSchemaWithType(SchemaObjectDictionary schemaInfo, XmlSchemaSet schemas, XmlQualifiedName typeName) + { + SchemaObjectInfo? schemaObjectInfo; + if (schemaInfo.TryGetValue(typeName, out schemaObjectInfo)) + { + if (schemaObjectInfo._schema != null) + return schemaObjectInfo._schema; + } + ICollection currentSchemas = schemas.Schemas(); + string ns = typeName.Namespace; + foreach (XmlSchema schema in currentSchemas) + { + if (NamespacesEqual(ns, schema.TargetNamespace)) + { + return schema; + } + } + return null; + } + + internal static XmlSchema? GetSchemaWithGlobalElementDeclaration(XmlSchemaElement element, XmlSchemaSet schemas) + { + ICollection currentSchemas = schemas.Schemas(); + foreach (XmlSchema schema in currentSchemas) + { + foreach (XmlSchemaObject schemaObject in schema.Items) + { + if (schemaObject is XmlSchemaElement schemaElement && schemaElement == element) + { + return schema; + } + } + } + return null; + } + + internal static XmlQualifiedName? GetGlobalElementDeclaration(XmlSchemaSet schemas, XmlQualifiedName typeQName, out bool isNullable) + { + ICollection currentSchemas = schemas.Schemas(); + isNullable = false; + foreach (XmlSchema schema in currentSchemas) + { + foreach (XmlSchemaObject schemaObject in schema.Items) + { + if (schemaObject is XmlSchemaElement schemaElement && schemaElement.SchemaTypeName.Equals(typeQName)) + { + isNullable = schemaElement.IsNillable; + return new XmlQualifiedName(schemaElement.Name, schema.TargetNamespace); + } + } + } + return null; + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaImporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaImporter.cs new file mode 100644 index 0000000000000..2590d866c26f8 --- /dev/null +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaImporter.cs @@ -0,0 +1,1459 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Xml.Schema; + +using DataContractDictionary = System.Collections.Generic.Dictionary; +using SchemaObjectDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization +{ + + internal sealed class SchemaImporter + { + private DataContractSet _dataContractSet; + private XmlSchemaSet _schemaSet; + private IEnumerable? _typeNames; + private IEnumerable? _elements; + private bool _importXmlDataType; + private SchemaObjectDictionary _schemaObjects = null!; // Not directly referenced. Always lazy initialized by property getter. + private List _redefineList = null!; // Not directly referenced. Always lazy initialized by property getter. + private bool _needToImportKnownTypesForObject; + + private static Hashtable? s_serializationSchemaElements; + + internal SchemaImporter(XmlSchemaSet schemas, IEnumerable? typeNames, IEnumerable? elements, DataContractSet dataContractSet, bool importXmlDataType) + { + _dataContractSet = dataContractSet; + _schemaSet = schemas; + _typeNames = typeNames; + _elements = elements; + _importXmlDataType = importXmlDataType; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal void Import([NotNullIfNotNull("_elements")] out List? elementTypeNames) + { + elementTypeNames = null!; + + if (!_schemaSet.Contains(Globals.SerializationNamespace)) + { + StringReader reader = new StringReader(Globals.SerializationSchema); + XmlSchema? schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); + if (schema == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace))); + _schemaSet.Add(schema); + } + + try + { + CompileSchemaSet(_schemaSet); + } +#pragma warning suppress 56500 // covered by FxCOP + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.CannotImportInvalidSchemas), ex)); + } + + if (_typeNames == null) + { + ICollection schemaList = _schemaSet.Schemas(); + foreach (object schemaObj in schemaList) + { + if (schemaObj == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.CannotImportNullSchema))); + + XmlSchema schema = (XmlSchema)schemaObj; + if (schema.TargetNamespace != Globals.SerializationNamespace + && schema.TargetNamespace != Globals.SchemaNamespace) + { + foreach (XmlSchemaObject typeObj in schema.SchemaTypes.Values) + { + ImportType((XmlSchemaType)typeObj); + } + foreach (XmlSchemaElement element in schema.Elements.Values) + { + if (element.SchemaType != null) + ImportAnonymousGlobalElement(element, element.QualifiedName, schema.TargetNamespace); + } + } + } + } + else + { + foreach (XmlQualifiedName typeName in _typeNames) + { + if (typeName == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.CannotImportNullDataContractName))); + ImportType(typeName); + } + + if (_elements != null) + { + var elementNameList = new List(); + foreach (XmlSchemaElement element in _elements) + { + XmlQualifiedName typeName = element.SchemaTypeName; + if (typeName != null && typeName.Name.Length > 0) + { + elementNameList.Add(ImportType(typeName).XmlName); + } + else + { + XmlSchema? schema = SchemaHelper.GetSchemaWithGlobalElementDeclaration(element, _schemaSet); + if (schema == null) + { + elementNameList.Add(ImportAnonymousElement(element, element.QualifiedName).XmlName); + } + else + { + elementNameList.Add(ImportAnonymousGlobalElement(element, element.QualifiedName, schema.TargetNamespace).XmlName); + } + } + } + + if (elementNameList.Count > 0) + elementTypeNames = elementNameList; + } + } + ImportKnownTypesForObject(); + } + + internal static void CompileSchemaSet(XmlSchemaSet schemaSet) + { + if (schemaSet.Contains(XmlSchema.Namespace)) + schemaSet.Compile(); + else + { + // Add base XSD schema with top level element named "schema" + XmlSchema xsdSchema = new XmlSchema(); + xsdSchema.TargetNamespace = XmlSchema.Namespace; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = Globals.SchemaLocalName; + element.SchemaType = new XmlSchemaComplexType(); + xsdSchema.Items.Add(element); + schemaSet.Add(xsdSchema); + schemaSet.Compile(); + } + } + + private SchemaObjectDictionary SchemaObjects + { + get + { + if (_schemaObjects == null) + _schemaObjects = CreateSchemaObjects(); + return _schemaObjects; + } + } + + private List RedefineList + { + get + { + if (_redefineList == null) + _redefineList = CreateRedefineList(); + return _redefineList; + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportKnownTypes(XmlQualifiedName typeName) + { + SchemaObjectInfo? schemaObjectInfo; + if (SchemaObjects.TryGetValue(typeName, out schemaObjectInfo)) + { + List? knownTypes = schemaObjectInfo._knownTypes; + if (knownTypes != null) + { + foreach (XmlSchemaType knownType in knownTypes) + ImportType(knownType); + } + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal static bool IsObjectContract(DataContract dataContract) + { + Dictionary previousCollectionTypes = new Dictionary(); + while (dataContract is CollectionDataContract) + { + if (dataContract.OriginalUnderlyingType == null) + { + dataContract = ((CollectionDataContract)dataContract).ItemContract; + continue; + } + + if (!previousCollectionTypes.ContainsKey(dataContract.OriginalUnderlyingType)) + { + previousCollectionTypes.Add(dataContract.OriginalUnderlyingType, dataContract.OriginalUnderlyingType); + dataContract = ((CollectionDataContract)dataContract).ItemContract; + } + else + { + break; + } + } + + return dataContract is PrimitiveDataContract && ((PrimitiveDataContract)dataContract).UnderlyingType == Globals.TypeOfObject; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportKnownTypesForObject() + { + if (!_needToImportKnownTypesForObject) + return; + _needToImportKnownTypesForObject = false; + if (_dataContractSet.KnownTypesForObject == null) + { + SchemaObjectInfo? schemaObjectInfo; + if (SchemaObjects.TryGetValue(SchemaExporter.AnytypeQualifiedName, out schemaObjectInfo)) + { + List? knownTypes = schemaObjectInfo._knownTypes; + if (knownTypes != null) + { + DataContractDictionary knownDataContracts = new DataContractDictionary(); + foreach (XmlSchemaType knownType in knownTypes) + { + // Expected: will throw exception if schema set contains types that are not supported + DataContract dataContract = ImportType(knownType); + if (!knownDataContracts.TryGetValue(dataContract.XmlName, out _)) + { + knownDataContracts.Add(dataContract.XmlName, dataContract); + } + } + _dataContractSet.KnownTypesForObject = knownDataContracts; + } + } + } + } + + internal SchemaObjectDictionary CreateSchemaObjects() + { + SchemaObjectDictionary schemaObjects = new SchemaObjectDictionary(); + ICollection schemaList = _schemaSet.Schemas(); + List knownTypesForObject = new List(); + schemaObjects.Add(SchemaExporter.AnytypeQualifiedName, new SchemaObjectInfo(null, null, null, knownTypesForObject)); + + foreach (XmlSchema schema in schemaList) + { + if (schema.TargetNamespace != Globals.SerializationNamespace) + { + foreach (XmlSchemaObject schemaObj in schema.SchemaTypes.Values) + { + if (schemaObj is XmlSchemaType schemaType) + { + knownTypesForObject.Add(schemaType); + + XmlQualifiedName currentTypeName = new XmlQualifiedName(schemaType.Name, schema.TargetNamespace); + SchemaObjectInfo? schemaObjectInfo; + if (schemaObjects.TryGetValue(currentTypeName, out schemaObjectInfo)) + { + schemaObjectInfo._type = schemaType; + schemaObjectInfo._schema = schema; + } + else + { + schemaObjects.Add(currentTypeName, new SchemaObjectInfo(schemaType, null, schema, null)); + } + + XmlQualifiedName? baseTypeName = GetBaseTypeName(schemaType); + if (baseTypeName != null) + { + SchemaObjectInfo? baseTypeInfo; + if (schemaObjects.TryGetValue(baseTypeName, out baseTypeInfo)) + { + if (baseTypeInfo._knownTypes == null) + { + baseTypeInfo._knownTypes = new List(); + } + } + else + { + baseTypeInfo = new SchemaObjectInfo(null, null, null, new List()); + schemaObjects.Add(baseTypeName, baseTypeInfo); + } + baseTypeInfo._knownTypes!.Add(schemaType); // Verified not-null above, or created not-null. + } + } + } + foreach (XmlSchemaObject schemaObj in schema.Elements.Values) + { + if (schemaObj is XmlSchemaElement schemaElement) + { + XmlQualifiedName currentElementName = new XmlQualifiedName(schemaElement.Name, schema.TargetNamespace); + SchemaObjectInfo? schemaObjectInfo; + if (schemaObjects.TryGetValue(currentElementName, out schemaObjectInfo)) + { + schemaObjectInfo._element = schemaElement; + schemaObjectInfo._schema = schema; + } + else + { + schemaObjects.Add(currentElementName, new SchemaObjectInfo(null, schemaElement, schema, null)); + } + } + } + } + } + return schemaObjects; + } + + private static XmlQualifiedName? GetBaseTypeName(XmlSchemaType type) + { + XmlQualifiedName? baseTypeName = null; + if (type is XmlSchemaComplexType complexType) + { + if (complexType.ContentModel != null) + { + if (complexType.ContentModel is XmlSchemaComplexContent complexContent) + { + if (complexContent.Content is XmlSchemaComplexContentExtension extension) + { + baseTypeName = extension.BaseTypeName; + } + } + } + } + return baseTypeName; + } + + private List CreateRedefineList() + { + List list = new List(); + + ICollection schemaList = _schemaSet.Schemas(); + foreach (object schemaObj in schemaList) + { + if (schemaObj is XmlSchema schema) + { + foreach (XmlSchemaExternal ext in schema.Includes) + { + if (ext is XmlSchemaRedefine redefine) + list.Add(redefine); + } + } + } + + return list; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportAnonymousGlobalElement(XmlSchemaElement element, XmlQualifiedName typeQName, string? ns) + { + DataContract contract = ImportAnonymousElement(element, typeQName); + if (contract is XmlDataContract xmlDataContract) + { + xmlDataContract.SetTopLevelElementName(new XmlQualifiedName(element.Name, ns)); + xmlDataContract.IsTopLevelElementNullable = element.IsNillable; + } + return contract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportAnonymousElement(XmlSchemaElement element, XmlQualifiedName typeQName) + { + if (SchemaHelper.GetSchemaType(SchemaObjects, typeQName) != null) + { + for (int i = 1;; i++) + { + typeQName = new XmlQualifiedName(typeQName.Name + i.ToString(NumberFormatInfo.InvariantInfo), typeQName.Namespace); + if (SchemaHelper.GetSchemaType(SchemaObjects, typeQName) == null) + break; + if (i == int.MaxValue) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CannotComputeUniqueName, element.Name))); + } + } + if (element.SchemaType == null) + return ImportType(SchemaExporter.AnytypeQualifiedName); + else + return ImportType(element.SchemaType, typeQName, true/*isAnonymous*/); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportType(XmlQualifiedName typeName) + { + DataContract? dataContract = DataContract.GetBuiltInDataContract(typeName.Name, typeName.Namespace); + if (dataContract == null) + { + XmlSchemaType? type = SchemaHelper.GetSchemaType(SchemaObjects, typeName); + if (type == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.SpecifiedTypeNotFoundInSchema, typeName.Name, typeName.Namespace))); + dataContract = ImportType(type); + } + if (IsObjectContract(dataContract)) + _needToImportKnownTypesForObject = true; + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportType(XmlSchemaType type) + { + return ImportType(type, type.QualifiedName, false/*isAnonymous*/); + } + + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportType(XmlSchemaType type, XmlQualifiedName typeName, bool isAnonymous) + { + DataContract? dataContract = _dataContractSet.GetDataContract(typeName); + if (dataContract != null) + return dataContract; + + InvalidDataContractException invalidContractException; + try + { + foreach (XmlSchemaRedefine redefine in RedefineList) + { + if (redefine.SchemaTypes[typeName] != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RedefineNotSupported)); + } + + if (type is XmlSchemaSimpleType simpleType) + { + XmlSchemaSimpleTypeContent? content = simpleType.Content; + if (content is XmlSchemaSimpleTypeUnion) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SimpleTypeUnionNotSupported)); + else if (content is XmlSchemaSimpleTypeList) + dataContract = ImportFlagsEnum(typeName, (XmlSchemaSimpleTypeList)content, simpleType.Annotation); + else if (content is XmlSchemaSimpleTypeRestriction restriction) + { + if (CheckIfEnum(restriction)) + { + dataContract = ImportEnum(typeName, restriction, false /*isFlags*/, simpleType.Annotation); + } + else + { + dataContract = ImportSimpleTypeRestriction(typeName, restriction); + if (dataContract.IsBuiltInDataContract && !isAnonymous) + { + _dataContractSet.InternalAdd(typeName, dataContract); + } + } + } + } + else if (type is XmlSchemaComplexType complexType) + { + if (complexType.ContentModel == null) + { + CheckComplexType(typeName, complexType); + dataContract = ImportType(typeName, complexType.Particle, complexType.Attributes, complexType.AnyAttribute, null /* baseTypeName */, complexType.Annotation); + } + else + { + XmlSchemaContentModel contentModel = complexType.ContentModel; + if (contentModel is XmlSchemaSimpleContent) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SimpleContentNotSupported)); + else if (contentModel is XmlSchemaComplexContent complexContent) + { + if (complexContent.IsMixed) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.MixedContentNotSupported)); + + if (complexContent.Content is XmlSchemaComplexContentExtension extension) + { + dataContract = ImportType(typeName, extension.Particle, extension.Attributes, extension.AnyAttribute, extension.BaseTypeName, complexType.Annotation); + } + else if (complexContent.Content is XmlSchemaComplexContentRestriction restriction) + { + XmlQualifiedName baseTypeName = restriction.BaseTypeName; + if (baseTypeName == SchemaExporter.AnytypeQualifiedName) + dataContract = ImportType(typeName, restriction.Particle, restriction.Attributes, restriction.AnyAttribute, null /* baseTypeName */, complexType.Annotation); + else + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ComplexTypeRestrictionNotSupported)); + } + } + } + } + + if (dataContract == null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, string.Empty); + if (type.QualifiedName != XmlQualifiedName.Empty) + ImportTopLevelElement(typeName); + Debug.Assert(dataContract != null); // Throws above if false + ImportDataContractExtension(type, dataContract); + ImportGenericInfo(type, dataContract); + ImportKnownTypes(typeName); + + return dataContract; + } + catch (InvalidDataContractException e) + { + invalidContractException = e; + } + + // Execution gets to this point if InvalidDataContractException was thrown + if (_importXmlDataType) + { + RemoveFailedContract(typeName); + return ImportXmlDataType(typeName, type, isAnonymous); + } + Type? referencedType; + if (_dataContractSet.TryGetReferencedType(typeName, dataContract, out referencedType) + || (string.IsNullOrEmpty(type.Name) && _dataContractSet.TryGetReferencedType(ImportActualType(type.Annotation, typeName, typeName), dataContract, out referencedType))) + { + if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(referencedType)) + { + RemoveFailedContract(typeName); + return ImportXmlDataType(typeName, type, isAnonymous); + } + } + XmlDataContract? specialContract = ImportSpecialXmlDataType(type, isAnonymous); + if (specialContract != null) + { + _dataContractSet.Remove(typeName); + return specialContract; + } + + throw invalidContractException; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void RemoveFailedContract(XmlQualifiedName typeName) + { + ClassDataContract? oldContract = _dataContractSet.GetDataContract(typeName) as ClassDataContract; + _dataContractSet.Remove(typeName); + if (oldContract != null) + { + ClassDataContract? ancestorDataContract = oldContract.BaseClassContract; + while (ancestorDataContract != null) + { + ancestorDataContract.KnownDataContracts?.Remove(typeName); + ancestorDataContract = ancestorDataContract.BaseClassContract; + } + if (_dataContractSet.KnownTypesForObject != null) + _dataContractSet.KnownTypesForObject.Remove(typeName); + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private bool CheckIfEnum(XmlSchemaSimpleTypeRestriction restriction) + { + foreach (XmlSchemaFacet facet in restriction.Facets) + { + if (facet is not XmlSchemaEnumerationFacet) + return false; + } + + XmlQualifiedName expectedBase = SchemaExporter.StringQualifiedName; + if (restriction.BaseTypeName != XmlQualifiedName.Empty) + { + return ((restriction.BaseTypeName == expectedBase && restriction.Facets.Count > 0) || ImportType(restriction.BaseTypeName) is EnumDataContract); + } + else if (restriction.BaseType != null) + { + DataContract baseContract = ImportType(restriction.BaseType); + return (baseContract.XmlName == expectedBase || baseContract is EnumDataContract); + } + + return false; + } + + private static bool CheckIfCollection(XmlSchemaSequence rootSequence) + { + if (rootSequence.Items == null || rootSequence.Items.Count == 0) + return false; + RemoveOptionalUnknownSerializationElements(rootSequence.Items); + if (rootSequence.Items.Count != 1) + return false; + + XmlSchemaObject o = rootSequence.Items[0]; + if (o is XmlSchemaElement localElement) + return (localElement.MaxOccursString == Globals.OccursUnbounded || localElement.MaxOccurs > 1); + + return false; + } + + private static bool CheckIfISerializable(XmlSchemaSequence rootSequence, XmlSchemaObjectCollection attributes) + { + if (rootSequence.Items == null || rootSequence.Items.Count == 0) + return false; + + RemoveOptionalUnknownSerializationElements(rootSequence.Items); + + if (attributes == null || attributes.Count == 0) + return false; + + return (rootSequence.Items.Count == 1 && rootSequence.Items[0] is XmlSchemaAny); + } + + private static void RemoveOptionalUnknownSerializationElements(XmlSchemaObjectCollection items) + { + for (int i = 0; i < items.Count; i++) + { + if (items[i] is XmlSchemaElement element && element.RefName != null && + element.RefName.Namespace == Globals.SerializationNamespace && + element.MinOccurs == 0) + { + if (s_serializationSchemaElements == null) + { + XmlSchema serializationSchema = XmlSchema.Read(XmlReader.Create(new StringReader(Globals.SerializationSchema)), null)!; // Source is our constant. Schema is valid. + s_serializationSchemaElements = new Hashtable(); + foreach (XmlSchemaObject schemaObject in serializationSchema.Items) + { + if (schemaObject is XmlSchemaElement schemaElement) + if (schemaElement.Name != null) + s_serializationSchemaElements.Add(schemaElement.Name, schemaElement); + } + } + if (!s_serializationSchemaElements.ContainsKey(element.RefName.Name)) + { + items.RemoveAt(i); + i--; + } + } + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract? ImportType(XmlQualifiedName typeName, XmlSchemaParticle? rootParticle, XmlSchemaObjectCollection attributes, XmlSchemaAnyAttribute? anyAttribute, XmlQualifiedName? baseTypeName, XmlSchemaAnnotation? annotation) + { + DataContract? dataContract = null; + bool isDerived = (baseTypeName != null); + + bool isReference; + ImportAttributes(typeName, attributes, anyAttribute, out isReference); + + if (rootParticle == null) + dataContract = ImportClass(typeName, new XmlSchemaSequence(), baseTypeName, annotation, isReference); + else if (rootParticle is XmlSchemaSequence rootSequence) + { + if (rootSequence.MinOccurs != 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RootSequenceMustBeRequired)); + if (rootSequence.MaxOccurs != 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RootSequenceMaxOccursMustBe)); + + if (!isDerived && CheckIfCollection(rootSequence)) + dataContract = ImportCollection(typeName, rootSequence, attributes, annotation, isReference); + else if (CheckIfISerializable(rootSequence, attributes)) + dataContract = ImportISerializable(typeName, rootSequence, baseTypeName, attributes, annotation); + else + dataContract = ImportClass(typeName, rootSequence, baseTypeName, annotation, isReference); + } + else + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.RootParticleMustBeSequence)); + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private ClassDataContract ImportClass(XmlQualifiedName typeName, XmlSchemaSequence rootSequence, XmlQualifiedName? baseTypeName, XmlSchemaAnnotation? annotation, bool isReference) + { + ClassDataContract dataContract = new ClassDataContract(Globals.TypeOfSchemaDefinedType); + dataContract.XmlName = typeName; + AddDataContract(dataContract); + + dataContract.IsValueType = IsValueType(typeName, annotation); + dataContract.IsReference = isReference; + if (baseTypeName != null) + { + ImportBaseContract(baseTypeName, dataContract); + Debug.Assert(dataContract.BaseClassContract != null); // ImportBaseContract will always set this... or throw. + if (dataContract.BaseClassContract.IsISerializable) + { + if (IsISerializableDerived(typeName, rootSequence)) + dataContract.IsISerializable = true; + else + ThrowTypeCannotBeImportedException(dataContract.XmlName.Name, dataContract.XmlName.Namespace, SR.Format(SR.DerivedTypeNotISerializable, baseTypeName.Name, baseTypeName.Namespace)); + } + if (dataContract.BaseClassContract.IsReference) + { + dataContract.IsReference = true; + } + } + + if (!dataContract.IsISerializable) + { + dataContract.Members = new List(); + RemoveOptionalUnknownSerializationElements(rootSequence.Items); + for (int memberIndex = 0; memberIndex < rootSequence.Items.Count; memberIndex++) + { + XmlSchemaElement? element = rootSequence.Items[memberIndex] as XmlSchemaElement; + if (element == null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.MustContainOnlyLocalElements)); + ImportClassMember(element!, dataContract); + } + } + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportXmlDataType(XmlQualifiedName typeName, XmlSchemaType xsdType, bool isAnonymous) + { + DataContract? dataContract = _dataContractSet.GetDataContract(typeName); + if (dataContract != null) + return dataContract; + + XmlDataContract? xmlDataContract = ImportSpecialXmlDataType(xsdType, isAnonymous); + if (xmlDataContract != null) + return xmlDataContract; + + xmlDataContract = new XmlDataContract(Globals.TypeOfSchemaDefinedType); + xmlDataContract.XmlName = typeName; + xmlDataContract.IsValueType = false; + AddDataContract(xmlDataContract); + if (xsdType != null) + { + ImportDataContractExtension(xsdType, xmlDataContract); + xmlDataContract.IsValueType = IsValueType(typeName, xsdType.Annotation); + xmlDataContract.IsTypeDefinedOnImport = true; + xmlDataContract.XsdType = isAnonymous ? xsdType : null; + xmlDataContract.HasRoot = !IsXmlAnyElementType(xsdType as XmlSchemaComplexType); + } + if (!isAnonymous) + { + bool isNullable; + xmlDataContract.SetTopLevelElementName(SchemaHelper.GetGlobalElementDeclaration(_schemaSet, typeName, out isNullable)); + xmlDataContract.IsTopLevelElementNullable = isNullable; + } + return xmlDataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private XmlDataContract? ImportSpecialXmlDataType(XmlSchemaType xsdType, bool isAnonymous) + { + if (!isAnonymous) + return null; + if (xsdType is not XmlSchemaComplexType complexType) + return null; + if (IsXmlAnyElementType(complexType)) + { + //check if the type is XElement + XmlQualifiedName xlinqTypeName = new XmlQualifiedName("XElement", "http://schemas.datacontract.org/2004/07/System.Xml.Linq"); + Type? referencedType; + if (_dataContractSet.TryGetReferencedType(xlinqTypeName, null, out referencedType) + && Globals.TypeOfIXmlSerializable.IsAssignableFrom(referencedType)) + { + XmlDataContract xmlDataContract = new XmlDataContract(referencedType); + AddDataContract(xmlDataContract); + return xmlDataContract; + } + //otherwise, assume XmlElement + return (XmlDataContract?)DataContract.GetBuiltInDataContract(Globals.TypeOfXmlElement); + } + if (IsXmlAnyType(complexType)) + return (XmlDataContract?)DataContract.GetBuiltInDataContract(Globals.TypeOfXmlNodeArray); + return null; + } + + private static bool IsXmlAnyElementType(XmlSchemaComplexType? xsdType) + { + if (xsdType == null) + return false; + + if (xsdType.Particle is XmlSchemaSequence sequence) + { + if (sequence.Items == null || sequence.Items.Count != 1) + return false; + + XmlSchemaAny? any = sequence.Items[0] as XmlSchemaAny; + if (any == null || any.Namespace != null) + return false; + + if (xsdType.AnyAttribute != null || (xsdType.Attributes != null && xsdType.Attributes.Count > 0)) + return false; + + return true; + } + + return false; + } + + private static bool IsXmlAnyType(XmlSchemaComplexType xsdType) + { + if (xsdType == null) + return false; + + if (xsdType.Particle is XmlSchemaSequence sequence) + { + if (sequence.Items == null || sequence.Items.Count != 1) + return false; + + XmlSchemaAny? any = sequence.Items[0] as XmlSchemaAny; + if (any == null || any.Namespace != null) + return false; + + if (any.MaxOccurs != decimal.MaxValue) + return false; + + if (xsdType.AnyAttribute == null || xsdType.Attributes.Count > 0) + return false; + + return true; + } + + return false; + } + + private static bool IsValueType(XmlQualifiedName typeName, XmlSchemaAnnotation? annotation) + { + string? isValueTypeInnerText = GetInnerText(typeName, ImportAnnotation(annotation, SchemaExporter.IsValueTypeName)); + if (isValueTypeInnerText != null) + { + try + { + return XmlConvert.ToBoolean(isValueTypeInnerText); + } + catch (FormatException fe) + { + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.IsValueTypeFormattedIncorrectly, isValueTypeInnerText, fe.Message)); + } + } + return false; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private ClassDataContract ImportISerializable(XmlQualifiedName typeName, XmlSchemaSequence rootSequence, XmlQualifiedName? baseTypeName, XmlSchemaObjectCollection attributes, XmlSchemaAnnotation? annotation) + { + ClassDataContract dataContract = new ClassDataContract(Globals.TypeOfSchemaDefinedType); + dataContract.XmlName = typeName; + dataContract.IsISerializable = true; + AddDataContract(dataContract); + + dataContract.IsValueType = IsValueType(typeName, annotation); + if (baseTypeName == null) + CheckISerializableBase(typeName, rootSequence, attributes); + else + { + ImportBaseContract(baseTypeName, dataContract); + Debug.Assert(dataContract.BaseClassContract != null); // ImportBaseContract will always set this... or throw. + if (!dataContract.BaseClassContract.IsISerializable) + ThrowISerializableTypeCannotBeImportedException(dataContract.XmlName.Name, dataContract.XmlName.Namespace, SR.Format(SR.BaseTypeNotISerializable, baseTypeName.Name, baseTypeName.Namespace)); + if (!IsISerializableDerived(typeName, rootSequence)) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDerivedContainsOneOrMoreItems)); + } + + return dataContract; + } + + private static void CheckISerializableBase(XmlQualifiedName typeName, XmlSchemaSequence? rootSequence, XmlSchemaObjectCollection attributes) + { + if (rootSequence == null) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDoesNotContainAny)); + + if (rootSequence.Items == null || rootSequence.Items.Count < 1) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDoesNotContainAny)); + else if (rootSequence.Items.Count > 1) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableContainsMoreThanOneItems)); + + XmlSchemaObject o = rootSequence.Items[0]; + if (!(o is XmlSchemaAny)) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableDoesNotContainAny)); + + XmlSchemaAny wildcard = (XmlSchemaAny)o; + XmlSchemaAny iSerializableWildcardElement = SchemaExporter.ISerializableWildcardElement; + if (wildcard.MinOccurs != iSerializableWildcardElement.MinOccurs) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardMinOccursMustBe, iSerializableWildcardElement.MinOccurs)); + if (wildcard.MaxOccursString != iSerializableWildcardElement.MaxOccursString) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardMaxOccursMustBe, iSerializableWildcardElement.MaxOccursString)); + if (wildcard.Namespace != iSerializableWildcardElement.Namespace) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardNamespaceInvalid, iSerializableWildcardElement.Namespace)); + if (wildcard.ProcessContents != iSerializableWildcardElement.ProcessContents) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableWildcardProcessContentsInvalid, iSerializableWildcardElement.ProcessContents)); + + XmlQualifiedName factoryTypeAttributeRefName = SchemaExporter.ISerializableFactoryTypeAttribute.RefName; + bool containsFactoryTypeAttribute = false; + if (attributes != null) + { + for (int i = 0; i < attributes.Count; i++) + { + o = attributes[i]; + if (o is XmlSchemaAttribute) + { + if (((XmlSchemaAttribute)o).RefName == factoryTypeAttributeRefName) + { + containsFactoryTypeAttribute = true; + break; + } + } + } + } + if (!containsFactoryTypeAttribute) + ThrowISerializableTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ISerializableMustRefFactoryTypeAttribute, factoryTypeAttributeRefName.Name, factoryTypeAttributeRefName.Namespace)); + } + + private static bool IsISerializableDerived(XmlQualifiedName typeName, XmlSchemaSequence? rootSequence) + { + return (rootSequence == null || rootSequence.Items == null || rootSequence.Items.Count == 0); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportBaseContract(XmlQualifiedName baseTypeName, ClassDataContract dataContract) + { + ClassDataContract? baseContract = ImportType(baseTypeName) as ClassDataContract; + if (baseContract == null) + ThrowTypeCannotBeImportedException(dataContract.XmlName.Name, dataContract.XmlName.Namespace, SR.Format(dataContract.IsISerializable ? SR.InvalidISerializableDerivation : SR.InvalidClassDerivation, baseTypeName.Name, baseTypeName.Namespace)); + + // Note: code ignores IsValueType annotation if derived type exists + if (baseContract.IsValueType) + baseContract.IsValueType = false; + + ClassDataContract? ancestorDataContract = baseContract; + while (ancestorDataContract != null) + { + DataContractDictionary knownDataContracts = ancestorDataContract.KnownDataContracts!; // Might be .Count == 0, but always non-null for ClassDataContract + knownDataContracts.Add(dataContract.XmlName, dataContract); + ancestorDataContract = ancestorDataContract.BaseClassContract; + } + + dataContract.BaseClassContract = baseContract; + } + + private void ImportTopLevelElement(XmlQualifiedName typeName) + { + XmlSchemaElement? topLevelElement = SchemaHelper.GetSchemaElement(SchemaObjects, typeName); + // Top level element of same name is not required, but is validated if it is present + if (topLevelElement == null) + return; + else + { + XmlQualifiedName elementTypeName = topLevelElement.SchemaTypeName; + if (elementTypeName.IsEmpty) + { + if (topLevelElement.SchemaType != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AnonymousTypeNotSupported, typeName.Name, typeName.Namespace)); + else + elementTypeName = SchemaExporter.AnytypeQualifiedName; + } + if (elementTypeName != typeName) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.TopLevelElementRepresentsDifferentType, topLevelElement.SchemaTypeName.Name, topLevelElement.SchemaTypeName.Namespace)); + CheckIfElementUsesUnsupportedConstructs(typeName, topLevelElement); + } + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportClassMember(XmlSchemaElement element, ClassDataContract dataContract) + { + XmlQualifiedName typeName = dataContract.XmlName; + + if (element.MinOccurs > 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementMinOccursMustBe, element.Name)); + if (element.MaxOccurs != 1) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementMaxOccursMustBe, element.Name)); + + DataContract? memberTypeContract = null; + string? memberName = element.Name; + bool memberIsRequired = (element.MinOccurs > 0); + bool memberIsNullable = element.IsNillable; + bool memberEmitDefaultValue; + int memberOrder = 0; + + XmlSchemaForm elementForm = element.Form; + if (elementForm == XmlSchemaForm.None) + { + XmlSchema? schema = SchemaHelper.GetSchemaWithType(SchemaObjects, _schemaSet, typeName); + if (schema != null) + elementForm = schema.ElementFormDefault; + } + if (elementForm != XmlSchemaForm.Qualified) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.FormMustBeQualified, element.Name)); + CheckIfElementUsesUnsupportedConstructs(typeName, element); + + if (element.SchemaTypeName.IsEmpty) + { + if (element.SchemaType != null) + memberTypeContract = ImportAnonymousElement(element, new XmlQualifiedName(string.Format(CultureInfo.InvariantCulture, "{0}.{1}Type", typeName.Name, element.Name), typeName.Namespace)); + else if (!element.RefName.IsEmpty) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementRefOnLocalElementNotSupported, element.RefName.Name, element.RefName.Namespace)); + else + memberTypeContract = ImportType(SchemaExporter.AnytypeQualifiedName); + } + else + { + XmlQualifiedName memberTypeName = ImportActualType(element.Annotation, element.SchemaTypeName, typeName); + memberTypeContract = ImportType(memberTypeName); + if (IsObjectContract(memberTypeContract)) + _needToImportKnownTypesForObject = true; + } + bool? emitDefaultValueFromAnnotation = ImportEmitDefaultValue(element.Annotation, typeName); + if (!memberTypeContract.IsValueType && !memberIsNullable) + { + if (emitDefaultValueFromAnnotation != null && emitDefaultValueFromAnnotation.Value) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidEmitDefaultAnnotation, memberName, typeName.Name, typeName.Namespace))); + memberEmitDefaultValue = false; + } + else + memberEmitDefaultValue = emitDefaultValueFromAnnotation != null ? emitDefaultValueFromAnnotation.Value : Globals.DefaultEmitDefaultValue; + + Debug.Assert(dataContract.Members != null); // This method is only called from ImportClass() after that method has initialized the Members collection. + Debug.Assert(memberName != null); // At this point, elements without a name should have been handled. + + int prevMemberIndex = dataContract.Members.Count - 1; + if (prevMemberIndex >= 0) + { + DataMember prevMember = dataContract.Members![prevMemberIndex]; + if (prevMember.Order > Globals.DefaultOrder) + memberOrder = dataContract.Members.Count; + DataMember currentMember = new DataMember(memberTypeContract, memberName, memberIsNullable, memberIsRequired, memberEmitDefaultValue, memberOrder); + int compare = ClassDataContract.DataMemberComparer.Singleton.Compare(prevMember, currentMember); + if (compare == 0) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.CannotHaveDuplicateElementNames, memberName)); + else if (compare > 0) + memberOrder = dataContract.Members.Count; + } + DataMember dataMember = new DataMember(memberTypeContract, memberName, memberIsNullable, memberIsRequired, memberEmitDefaultValue, memberOrder); + + XmlQualifiedName surrogateDataAnnotationName = SchemaExporter.SurrogateDataAnnotationName; + _dataContractSet.SetSurrogateData(dataMember, ImportSurrogateData(ImportAnnotation(element.Annotation, surrogateDataAnnotationName), surrogateDataAnnotationName.Name, surrogateDataAnnotationName.Namespace)); + + dataContract.Members.Add(dataMember); + } + + private static bool? ImportEmitDefaultValue(XmlSchemaAnnotation? annotation, XmlQualifiedName typeName) + { + XmlElement? defaultValueElement = ImportAnnotation(annotation, SchemaExporter.DefaultValueAnnotation); + if (defaultValueElement == null) + return null; + XmlNode? emitDefaultValueAttribute = defaultValueElement.Attributes.GetNamedItem(Globals.EmitDefaultValueAttribute); + if (emitDefaultValueAttribute?.Value == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, SchemaExporter.DefaultValueAnnotation.Name, typeName.Name, typeName.Namespace, Globals.EmitDefaultValueAttribute))); + return XmlConvert.ToBoolean(emitDefaultValueAttribute.Value); + } + + internal static XmlQualifiedName ImportActualType(XmlSchemaAnnotation? annotation, XmlQualifiedName defaultTypeName, XmlQualifiedName typeName) + { + XmlElement? actualTypeElement = ImportAnnotation(annotation, SchemaExporter.ActualTypeAnnotationName); + if (actualTypeElement == null) + return defaultTypeName; + + XmlNode? nameAttribute = actualTypeElement.Attributes.GetNamedItem(Globals.ActualTypeNameAttribute); + if (nameAttribute?.Value == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, SchemaExporter.ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, Globals.ActualTypeNameAttribute))); + XmlNode? nsAttribute = actualTypeElement.Attributes.GetNamedItem(Globals.ActualTypeNamespaceAttribute); + if (nsAttribute?.Value == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.AnnotationAttributeNotFound, SchemaExporter.ActualTypeAnnotationName.Name, typeName.Name, typeName.Namespace, Globals.ActualTypeNamespaceAttribute))); + return new XmlQualifiedName(nameAttribute.Value, nsAttribute.Value); + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private CollectionDataContract ImportCollection(XmlQualifiedName typeName, XmlSchemaSequence rootSequence, XmlSchemaObjectCollection attributes, XmlSchemaAnnotation? annotation, bool isReference) + { + CollectionDataContract dataContract = new CollectionDataContract(Globals.TypeOfSchemaDefinedType, CollectionKind.Array); + dataContract.XmlName = typeName; + AddDataContract(dataContract); + + dataContract.IsReference = isReference; + + // CheckIfCollection has already checked if sequence contains exactly one item with maxOccurs="unbounded" or maxOccurs > 1 + XmlSchemaElement element = (XmlSchemaElement)rootSequence.Items[0]; + + Debug.Assert(element.Name != null); + + dataContract.IsItemTypeNullable = element.IsNillable; + dataContract.ItemName = element.Name; + + XmlSchemaForm elementForm = element.Form; + if (elementForm == XmlSchemaForm.None) + { + XmlSchema? schema = SchemaHelper.GetSchemaWithType(SchemaObjects, _schemaSet, typeName); + if (schema != null) + elementForm = schema.ElementFormDefault; + } + if (elementForm != XmlSchemaForm.Qualified) + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ArrayItemFormMustBe, element.Name)); + CheckIfElementUsesUnsupportedConstructs(typeName, element); + + if (element.SchemaTypeName.IsEmpty) + { + if (element.SchemaType != null) + { + XmlQualifiedName shortName = new XmlQualifiedName(element.Name, typeName.Namespace); + DataContract? contract = _dataContractSet.GetDataContract(shortName); + if (contract == null) + { + dataContract.ItemContract = ImportAnonymousElement(element, shortName); + } + else + { + XmlQualifiedName fullName = new XmlQualifiedName(string.Format(CultureInfo.InvariantCulture, "{0}.{1}Type", typeName.Name, element.Name), typeName.Namespace); + dataContract.ItemContract = ImportAnonymousElement(element, fullName); + } + } + else if (!element.RefName.IsEmpty) + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.ElementRefOnLocalElementNotSupported, element.RefName.Name, element.RefName.Namespace)); + else + dataContract.ItemContract = ImportType(SchemaExporter.AnytypeQualifiedName); + } + else + { + dataContract.ItemContract = ImportType(element.SchemaTypeName); + } + + if (IsDictionary(typeName, annotation)) + { + ClassDataContract? keyValueContract = dataContract.ItemContract as ClassDataContract; + DataMember key = null!, value = null!; // Set in the following || conditional chain. If the chain triggers before setting these, an exception is thrown. + if (keyValueContract == null || keyValueContract.Members == null || keyValueContract.Members.Count != 2 + || !(key = keyValueContract.Members[0]).IsRequired || !(value = keyValueContract.Members[1]).IsRequired) + { + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.InvalidKeyValueType, element.Name)); + } + if (keyValueContract.Namespace != dataContract.Namespace) + { + ThrowArrayTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.InvalidKeyValueTypeNamespace, element.Name, keyValueContract.Namespace)); + } + keyValueContract.IsValueType = true; + dataContract.KeyName = key.Name; + dataContract.ValueName = value.Name; + if (element.SchemaType != null) + { + _dataContractSet.Remove(keyValueContract.XmlName); + + GenericInfo genericInfo = new GenericInfo(DataContract.GetXmlName(Globals.TypeOfKeyValue), Globals.TypeOfKeyValue.FullName); + genericInfo.Add(GetGenericInfoForDataMember(key)); + genericInfo.Add(GetGenericInfoForDataMember(value)); + genericInfo.AddToLevel(0, 2); + dataContract.ItemContract.XmlName = new XmlQualifiedName(genericInfo.GetExpandedXmlName().Name, typeName.Namespace); + } + } + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private static GenericInfo GetGenericInfoForDataMember(DataMember dataMember) + { + GenericInfo genericInfo; + if (dataMember.MemberTypeContract.IsValueType && dataMember.IsNullable) + { + genericInfo = new GenericInfo(DataContract.GetXmlName(Globals.TypeOfNullable), Globals.TypeOfNullable.FullName); + genericInfo.Add(new GenericInfo(dataMember.MemberTypeContract.XmlName, null)); + } + else + { + genericInfo = new GenericInfo(dataMember.MemberTypeContract.XmlName, null); + } + + return genericInfo; + } + + private static bool IsDictionary(XmlQualifiedName typeName, XmlSchemaAnnotation? annotation) + { + string? isDictionaryInnerText = GetInnerText(typeName, ImportAnnotation(annotation, SchemaExporter.IsDictionaryAnnotationName)); + if (isDictionaryInnerText != null) + { + try + { + return XmlConvert.ToBoolean(isDictionaryInnerText); + } + catch (FormatException fe) + { + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.IsDictionaryFormattedIncorrectly, isDictionaryInnerText, fe.Message)); + } + } + return false; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private EnumDataContract? ImportFlagsEnum(XmlQualifiedName typeName, XmlSchemaSimpleTypeList list, XmlSchemaAnnotation? annotation) + { + XmlSchemaSimpleType? anonymousType = list.ItemType; + if (anonymousType == null) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumListMustContainAnonymousType)); + + XmlSchemaSimpleTypeContent? content = anonymousType.Content; + if (content is XmlSchemaSimpleTypeUnion) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumUnionInAnonymousTypeNotSupported)); + else if (content is XmlSchemaSimpleTypeList) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumListInAnonymousTypeNotSupported)); + else if (content is XmlSchemaSimpleTypeRestriction) + { + if (content is XmlSchemaSimpleTypeRestriction restriction && CheckIfEnum(restriction)) + return ImportEnum(typeName, restriction, true /*isFlags*/, annotation); + else + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumRestrictionInvalid)); + } + return null; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private EnumDataContract ImportEnum(XmlQualifiedName typeName, XmlSchemaSimpleTypeRestriction restriction, bool isFlags, XmlSchemaAnnotation? annotation) + { + EnumDataContract dataContract = new EnumDataContract(Globals.TypeOfSchemaDefinedEnum); + dataContract.XmlName = typeName; + dataContract.BaseContractName = ImportActualType(annotation, SchemaExporter.DefaultEnumBaseTypeName, typeName); + dataContract.IsFlags = isFlags; + AddDataContract(dataContract); + + // CheckIfEnum has already checked if baseType of restriction is string + dataContract.Values = new List(); + dataContract.Members = new List(); + foreach (XmlSchemaFacet facet in restriction.Facets) + { + XmlSchemaEnumerationFacet? enumFacet = facet as XmlSchemaEnumerationFacet; + if (enumFacet == null) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumOnlyEnumerationFacetsSupported)); + if (enumFacet.Value == null) + ThrowEnumTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.EnumEnumerationFacetsMustHaveValue)); + + string? valueInnerText = GetInnerText(typeName, ImportAnnotation(enumFacet.Annotation, SchemaExporter.EnumerationValueAnnotationName)); + long enumValue = (valueInnerText == null) ? SchemaExporter.GetDefaultEnumValue(isFlags, dataContract.Members.Count) + : dataContract.GetEnumValueFromString(valueInnerText); + dataContract.Values.Add(enumValue); + DataMember dataMember = new DataMember(Globals.SchemaMemberInfoPlaceholder) { Name = enumFacet.Value, Order = enumValue }; + dataContract.Members.Add(dataMember); + } + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private DataContract ImportSimpleTypeRestriction(XmlQualifiedName typeName, XmlSchemaSimpleTypeRestriction restriction) + { + DataContract dataContract = null!; // Always assigned by one of the ImportType()s, or exception is thrown. + + if (!restriction.BaseTypeName.IsEmpty) + dataContract = ImportType(restriction.BaseTypeName); + else if (restriction.BaseType != null) + dataContract = ImportType(restriction.BaseType); + else + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SimpleTypeRestrictionDoesNotSpecifyBase)); + + return dataContract; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void ImportDataContractExtension(XmlSchemaType type, DataContract dataContract) + { + if (type.Annotation == null || type.Annotation.Items == null) + return; + foreach (XmlSchemaObject schemaObject in type.Annotation.Items) + { + if (schemaObject is XmlSchemaAppInfo appInfo && appInfo.Markup != null) + { + foreach (XmlNode? xmlNode in appInfo.Markup) + { + XmlElement? typeElement = xmlNode as XmlElement; + XmlQualifiedName surrogateDataAnnotationName = SchemaExporter.SurrogateDataAnnotationName; + if (typeElement != null && typeElement.NamespaceURI == surrogateDataAnnotationName.Namespace && typeElement.LocalName == surrogateDataAnnotationName.Name) + { + object? surrogateData = ImportSurrogateData(typeElement, surrogateDataAnnotationName.Name, surrogateDataAnnotationName.Namespace); + _dataContractSet.SetSurrogateData(dataContract, surrogateData); + } + } + } + } + } + + private void ImportGenericInfo(XmlSchemaType type, DataContract dataContract) + { + if (type.Annotation == null || type.Annotation.Items == null) + return; + foreach (XmlSchemaObject schemaObject in type.Annotation.Items) + { + if (schemaObject is XmlSchemaAppInfo appInfo && appInfo.Markup != null) + { + foreach (XmlNode? xmlNode in appInfo.Markup) + { + XmlElement? typeElement = xmlNode as XmlElement; + if (typeElement != null && typeElement.NamespaceURI == Globals.SerializationNamespace) + { + if (typeElement.LocalName == Globals.GenericTypeLocalName) + dataContract.GenericInfo = ImportGenericInfo(typeElement, type); + } + } + } + } + } + + private GenericInfo ImportGenericInfo(XmlElement typeElement, XmlSchemaType type) + { + string? name = typeElement.Attributes.GetNamedItem(Globals.GenericNameAttribute)?.Value; + if (name == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationAttributeNotFound, type.Name, Globals.GenericNameAttribute))); + string? ns = typeElement.Attributes.GetNamedItem(Globals.GenericNamespaceAttribute)?.Value; + if (ns == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationAttributeNotFound, type.Name, Globals.GenericNamespaceAttribute))); + if (typeElement.ChildNodes.Count > 0) //Generic Type + name = DataContract.EncodeLocalName(name); + + int currentLevel = 0; + GenericInfo genInfo = new GenericInfo(new XmlQualifiedName(name, ns), type.Name); + foreach (XmlNode childNode in typeElement.ChildNodes) + { + if (childNode is XmlElement argumentElement) + { + if (argumentElement.LocalName != Globals.GenericParameterLocalName || + argumentElement.NamespaceURI != Globals.SerializationNamespace) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationHasInvalidElement, argumentElement.LocalName, argumentElement.NamespaceURI, type.Name))); + XmlNode? nestedLevelAttribute = argumentElement.Attributes.GetNamedItem(Globals.GenericParameterNestedLevelAttribute); + int argumentLevel = 0; + if (nestedLevelAttribute != null) + { + if (!int.TryParse(nestedLevelAttribute.Value, out argumentLevel)) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationHasInvalidAttributeValue, argumentElement.LocalName, argumentElement.NamespaceURI, type.Name, nestedLevelAttribute.Value, nestedLevelAttribute.LocalName, Globals.TypeOfInt.Name))); + } + if (argumentLevel < currentLevel) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationForNestedLevelMustBeIncreasing, argumentElement.LocalName, argumentElement.NamespaceURI, type.Name))); + genInfo.Add(ImportGenericInfo(argumentElement, type)); + genInfo.AddToLevel(argumentLevel, 1); + currentLevel = argumentLevel; + } + } + + XmlNode? typeNestedLevelsAttribute = typeElement.Attributes.GetNamedItem(Globals.GenericParameterNestedLevelAttribute); + if (typeNestedLevelsAttribute != null) + { + int nestedLevels; + if (!int.TryParse(typeNestedLevelsAttribute.Value, out nestedLevels)) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericAnnotationHasInvalidAttributeValue, typeElement.LocalName, typeElement.NamespaceURI, type.Name, typeNestedLevelsAttribute.Value, typeNestedLevelsAttribute.LocalName, Globals.TypeOfInt.Name))); + if ((nestedLevels - 1) > currentLevel) + genInfo.AddToLevel(nestedLevels - 1, 0); + } + return genInfo; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private object? ImportSurrogateData(XmlElement? typeElement, string name, string ns) + { + if (_dataContractSet.SerializationExtendedSurrogateProvider != null && typeElement != null) + { + Collection knownTypes = new Collection(); + DataContractSurrogateCaller.GetKnownCustomDataTypes(_dataContractSet.SerializationExtendedSurrogateProvider, knownTypes); + DataContractSerializer serializer = new DataContractSerializer(Globals.TypeOfObject, name, ns, knownTypes, + int.MaxValue, false /*ignoreExtensionDataObject*/, true /*preserveObjectReferences*/); + return serializer.ReadObject(new XmlNodeReader(typeElement)); + } + return null; + } + + private static void CheckComplexType(XmlQualifiedName typeName, XmlSchemaComplexType type) + { + if (type.IsAbstract) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AbstractTypeNotSupported)); + if (type.IsMixed) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.MixedContentNotSupported)); + } + + private static void CheckIfElementUsesUnsupportedConstructs(XmlQualifiedName typeName, XmlSchemaElement element) + { + if (element.IsAbstract) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AbstractElementNotSupported, element.Name)); + if (element.DefaultValue != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.DefaultOnElementNotSupported, element.Name)); + if (element.FixedValue != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.FixedOnElementNotSupported, element.Name)); + if (!element.SubstitutionGroup.IsEmpty) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.SubstitutionGroupOnElementNotSupported, element.Name)); + } + + private static void ImportAttributes(XmlQualifiedName typeName, XmlSchemaObjectCollection attributes, XmlSchemaAnyAttribute? anyAttribute, out bool isReference) + { + if (anyAttribute != null) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.AnyAttributeNotSupported)); + + isReference = false; + if (attributes != null) + { + bool foundId = false, foundRef = false; + for (int i = 0; i < attributes.Count; i++) + { + XmlSchemaObject o = attributes[i]; + if (o is XmlSchemaAttribute attribute) + { + if (attribute.Use == XmlSchemaUse.Prohibited) + continue; + if (TryCheckIfAttribute(typeName, attribute, Globals.IdQualifiedName, ref foundId)) + continue; + if (TryCheckIfAttribute(typeName, attribute, Globals.RefQualifiedName, ref foundRef)) + continue; + if (attribute.RefName.IsEmpty || attribute.RefName.Namespace != Globals.SerializationNamespace || attribute.Use == XmlSchemaUse.Required) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.TypeShouldNotContainAttributes, Globals.SerializationNamespace)); + } + } + isReference = (foundId && foundRef); + } + } + + private static bool TryCheckIfAttribute(XmlQualifiedName typeName, XmlSchemaAttribute attribute, XmlQualifiedName refName, ref bool foundAttribute) + { + if (attribute.RefName != refName) + return false; + if (foundAttribute) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.CannotHaveDuplicateAttributeNames, refName.Name)); + foundAttribute = true; + return true; + } + + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private void AddDataContract(DataContract dataContract) + { + _dataContractSet.Add(dataContract.XmlName, dataContract); + } + + private static string? GetInnerText(XmlQualifiedName typeName, XmlElement? xmlElement) + { + if (xmlElement != null) + { + XmlNode? child = xmlElement.FirstChild; + while (child != null) + { + if (child.NodeType == XmlNodeType.Element) + ThrowTypeCannotBeImportedException(typeName.Name, typeName.Namespace, SR.Format(SR.InvalidAnnotationExpectingText, xmlElement.LocalName, xmlElement.NamespaceURI, child.LocalName, child.NamespaceURI)); + child = child.NextSibling; + } + return xmlElement.InnerText; + } + return null; + } + + 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++) + { + if (markup[i] is XmlElement annotationElement && annotationElement.LocalName == annotationQualifiedName.Name && annotationElement.NamespaceURI == annotationQualifiedName.Namespace) + return annotationElement; + } + } + } + return null; + } + + [DoesNotReturn] + private static void ThrowTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.TypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowArrayTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.ArrayTypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowEnumTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.EnumTypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowISerializableTypeCannotBeImportedException(string name, string ns, string message) + { + ThrowTypeCannotBeImportedException(SR.Format(SR.ISerializableTypeCannotBeImported, name, ns, message)); + } + + [DoesNotReturn] + private static void ThrowTypeCannotBeImportedException(string message) + { + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.TypeCannotBeImportedHowToFix, message))); + } + } + +} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs index 727fc4e6c4953..149a661a95bff 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ScopedKnownTypes.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.Serialization.DataContracts; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SerializationMode.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SerializationMode.cs deleted file mode 100644 index 142ae2c009638..0000000000000 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SerializationMode.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.Serialization -{ - internal enum SerializationMode - { - SharedContract - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs index 010871e02e13a..978de94bae99d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Xml; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class SpecialTypeDataContract : DataContract { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs index 02b82aaee0d80..77a2e181052fd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; using System.Security; -namespace System.Runtime.Serialization +namespace System.Runtime.Serialization.DataContracts { internal sealed class SurrogateDataContract : DataContract { @@ -20,13 +20,10 @@ internal SurrogateDataContract(Type type, ISerializationSurrogate serializationS _helper = (base.Helper as SurrogateDataContractCriticalHelper)!; } - internal ISerializationSurrogate SerializationSurrogate - { - get { return _helper.SerializationSurrogate; } - } + internal ISerializationSurrogate SerializationSurrogate => _helper.SerializationSurrogate; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { Debug.Assert(context != null); @@ -62,7 +59,7 @@ private void SerializationSurrogateGetObjectData(object obj, SerializationInfo s } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { Debug.Assert(context != null); @@ -95,14 +92,11 @@ internal SurrogateDataContractCriticalHelper( { this.serializationSurrogate = serializationSurrogate; string name, ns; - DataContract.GetDefaultStableName(DataContract.GetClrTypeFullName(type), out name, out ns); + DataContract.GetDefaultXmlName(DataContract.GetClrTypeFullName(type), out name, out ns); SetDataContractName(CreateQualifiedName(name, ns)); } - internal ISerializationSurrogate SerializationSurrogate - { - get { return serializationSurrogate; } - } + internal ISerializationSurrogate SerializationSurrogate => serializationSurrogate; } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/TypeCode.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/TypeCode.cs deleted file mode 100644 index f9b2b9d4ca526..0000000000000 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/TypeCode.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.Serialization -{ - internal static class TypeExtensionMethods - { - public static TypeCode GetTypeCode(this Type type) - { - if (type == null) - { - return TypeCode.Empty; - } - else if (type == typeof(bool)) - { - return TypeCode.Boolean; - } - else if (type == typeof(char)) - { - return TypeCode.Char; - } - else if (type == typeof(sbyte)) - { - return TypeCode.SByte; - } - else if (type == typeof(byte)) - { - return TypeCode.Byte; - } - else if (type == typeof(short)) - { - return TypeCode.Int16; - } - else if (type == typeof(ushort)) - { - return TypeCode.UInt16; - } - else if (type == typeof(int)) - { - return TypeCode.Int32; - } - else if (type == typeof(uint)) - { - return TypeCode.UInt32; - } - else if (type == typeof(long)) - { - return TypeCode.Int64; - } - else if (type == typeof(ulong)) - { - return TypeCode.UInt64; - } - else if (type == typeof(float)) - { - return TypeCode.Single; - } - else if (type == typeof(double)) - { - return TypeCode.Double; - } - else if (type == typeof(decimal)) - { - return TypeCode.Decimal; - } - else if (type == typeof(DateTime)) - { - return TypeCode.DateTime; - } - else if (type == typeof(string)) - { - return TypeCode.String; - } - else - { - return TypeCode.Object; - } - } - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs index 364038c247a3e..2fac683e1d727 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs @@ -2,12 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Text; -using System.Reflection; -using System.Globalization; using System.Collections.Generic; -using System.Xml; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Text; +using System.Xml; namespace System.Runtime.Serialization { @@ -80,9 +81,9 @@ private static DataContract ProcessClassDataContract(ClassDataContract contract, private static IEnumerable GetDataMembers(ClassDataContract contract) { - if (contract.BaseContract != null) + if (contract.BaseClassContract != null) { - foreach (DataMember baseClassMember in GetDataMembers(contract.BaseContract)) + foreach (DataMember baseClassMember in GetDataMembers(contract.BaseClassContract)) { yield return baseClassMember; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs index 9ca8757798163..af769058a1f9a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs @@ -12,13 +12,17 @@ using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; -using DataContractDictionary = System.Collections.Generic.Dictionary; -namespace System.Runtime.Serialization +using DataContractDictionary = System.Collections.Generic.Dictionary; + +namespace System.Runtime.Serialization.DataContracts { internal delegate IXmlSerializable CreateXmlSerializableDelegate(); - internal sealed class XmlDataContract : DataContract + public sealed class XmlDataContract : DataContract { + internal const string ContractTypeString = nameof(XmlDataContract); + public override string? ContractType => ContractTypeString; + private readonly XmlDataContractCriticalHelper _helper; [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -30,57 +34,55 @@ internal XmlDataContract(Type type) : base(new XmlDataContractCriticalHelper(typ public override DataContractDictionary? KnownDataContracts { [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - get - { return _helper.KnownDataContracts; } - - set - { _helper.KnownDataContracts = value; } + get => _helper.KnownDataContracts; + internal set => _helper.KnownDataContracts = value; } - internal XmlSchemaType? XsdType + public XmlSchemaType? XsdType { - get { return _helper.XsdType; } - set { _helper.XsdType = value; } + get => _helper.XsdType; + internal set => _helper.XsdType = value; } - - internal bool IsAnonymous + public bool IsAnonymous { - get - { return _helper.IsAnonymous; } + get => _helper.IsAnonymous; } - public override bool HasRoot + public new bool IsValueType { - get - { return _helper.HasRoot; } + get => _helper.IsValueType; + set => _helper.IsValueType = value; + } - set - { _helper.HasRoot = value; } + public new bool HasRoot + { + get => _helper.HasRoot; + internal set => _helper.HasRoot = value; } public override XmlDictionaryString? TopLevelElementName { - get - { return _helper.TopLevelElementName; } - - set - { _helper.TopLevelElementName = value; } + get => _helper.TopLevelElementName; + internal set => _helper.TopLevelElementName = value; } public override XmlDictionaryString? TopLevelElementNamespace { - get - { return _helper.TopLevelElementNamespace; } + get => _helper.TopLevelElementNamespace; + internal set => _helper.TopLevelElementNamespace = value; + } - set - { _helper.TopLevelElementNamespace = value; } + public bool IsTopLevelElementNullable + { + get => _helper.IsTopLevelElementNullable; + internal set => _helper.IsTopLevelElementNullable = value; } - internal bool IsTopLevelElementNullable + public bool IsTypeDefinedOnImport { - get { return _helper.IsTopLevelElementNullable; } - set { _helper.IsTopLevelElementNullable = value; } + get => _helper.IsTypeDefinedOnImport; + set => _helper.IsTypeDefinedOnImport = value; } internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate @@ -122,7 +124,7 @@ private sealed class XmlDataContractCriticalHelper : DataContract.DataContractCr private XmlDictionaryString? _topLevelElementName; private XmlDictionaryString? _topLevelElementNamespace; private bool _isTopLevelElementNullable; - private bool _hasRoot; + private bool _isTypeDefinedOnImport; private CreateXmlSerializableDelegate? _createXmlSerializable; private XmlSchemaType? _xsdType; @@ -136,20 +138,22 @@ internal XmlDataContractCriticalHelper( if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.IXmlSerializableCannotHaveCollectionDataContract, DataContract.GetClrTypeFullName(type)))); bool hasRoot; - XmlQualifiedName stableName; - SchemaExporter.GetXmlTypeInfo(type, out stableName, out _, out hasRoot); - this.StableName = stableName; - this.HasRoot = hasRoot; + XmlSchemaType? xsdType; + XmlQualifiedName xmlName; + SchemaExporter.GetXmlTypeInfo(type, out xmlName, out xsdType, out hasRoot); + XmlName = xmlName; + XsdType = xsdType; + HasRoot = hasRoot; XmlDictionary dictionary = new XmlDictionary(); - this.Name = dictionary.Add(StableName.Name); - this.Namespace = dictionary.Add(StableName.Namespace); + 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.StableName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace; + _topLevelElementNamespace = (this.XmlName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace; _isTopLevelElementNullable = true; } } @@ -186,6 +190,7 @@ internal override DataContractDictionary? KnownDataContracts Interlocked.MemoryBarrier(); _isKnownTypeAttributeChecked = true; } + _knownDataContracts ??= new DataContractDictionary(); } } return _knownDataContracts; @@ -197,47 +202,40 @@ internal override DataContractDictionary? KnownDataContracts internal XmlSchemaType? XsdType { - get { return _xsdType; } - set { _xsdType = value; } + get => _xsdType; + set => _xsdType = value; } internal bool IsAnonymous => _xsdType != null; - internal override bool HasRoot - { - get - { return _hasRoot; } - - set - { _hasRoot = value; } - } - internal override XmlDictionaryString? TopLevelElementName { - get - { return _topLevelElementName; } - set - { _topLevelElementName = value; } + get => _topLevelElementName; + set => _topLevelElementName = value; } internal override XmlDictionaryString? TopLevelElementNamespace { - get - { return _topLevelElementNamespace; } - set - { _topLevelElementNamespace = value; } + get => _topLevelElementNamespace; + set => _topLevelElementNamespace = value; } internal bool IsTopLevelElementNullable { - get { return _isTopLevelElementNullable; } - set { _isTopLevelElementNullable = value; } + get => _isTopLevelElementNullable; + set => _isTopLevelElementNullable = value; + } + + internal bool IsTypeDefinedOnImport + { + get => _isTypeDefinedOnImport; + set => _isTypeDefinedOnImport = value; } internal CreateXmlSerializableDelegate? CreateXmlSerializableDelegate { - get { return _createXmlSerializable; } - set { _createXmlSerializable = value; } + get => _createXmlSerializable; + set => _createXmlSerializable = value; } } @@ -253,6 +251,16 @@ internal CreateXmlSerializableDelegate? CreateXmlSerializableDelegate return ctor; } + internal void SetTopLevelElementName(XmlQualifiedName? elementName) + { + if (elementName != null) + { + XmlDictionary dictionary = new XmlDictionary(); + TopLevelElementName = dictionary.Add(elementName.Name); + TopLevelElementNamespace = dictionary.Add(elementName.Namespace); + } + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate() { @@ -370,8 +378,30 @@ internal IXmlSerializable ReflectionCreateXmlSerializable(Type type) } } + internal override bool Equals(object? other, HashSet? 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; + } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) + internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context) { if (context == null) XmlObjectSerializerWriteContext.WriteRootIXmlSerializable(xmlWriter, obj); @@ -380,7 +410,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - public override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) + internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context) { object? o; if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs index 6791ee9e45f58..ec6392e62e7c3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatGeneratorStatics.cs @@ -1,11 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Reflection; -using System.Xml; using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Xml; namespace System.Runtime.Serialization { @@ -70,6 +71,9 @@ internal static MethodInfo WriteNamespaceDeclMethod private static PropertyInfo? s_extensionDataProperty; internal static PropertyInfo ExtensionDataProperty => s_extensionDataProperty ??= typeof(IExtensibleDataObject).GetProperty("ExtensionData")!; + private static MethodInfo? s_boxPointer; + internal static MethodInfo BoxPointer => s_boxPointer ??= typeof(Pointer).GetMethod("Box")!; + private static ConstructorInfo? s_dictionaryEnumeratorCtor; internal static ConstructorInfo DictionaryEnumeratorCtor { @@ -163,7 +167,7 @@ internal static MethodInfo GetUninitializedObjectMethod { if (s_getUninitializedObjectMethod == null) { - s_getUninitializedObjectMethod = typeof(XmlFormatReaderGenerator).GetMethod("UnsafeGetUninitializedObject", Globals.ScanAllMembers, new Type[] { typeof(int) }); + s_getUninitializedObjectMethod = typeof(XmlFormatReaderGenerator).GetMethod("UnsafeGetUninitializedObject", Globals.ScanAllMembers, new Type[] { typeof(int) })!; Debug.Assert(s_getUninitializedObjectMethod != null); } return s_getUninitializedObjectMethod; @@ -184,6 +188,9 @@ internal static MethodInfo OnDeserializationMethod } } + private static MethodInfo? s_unboxPointer; + internal static MethodInfo UnboxPointer => s_unboxPointer ??= typeof(Pointer).GetMethod("Unbox")!; + private static PropertyInfo? s_nodeTypeProperty; internal static PropertyInfo NodeTypeProperty { @@ -198,9 +205,11 @@ internal static PropertyInfo NodeTypeProperty } } + private static ConstructorInfo? s_serializationExceptionCtor; + internal static ConstructorInfo SerializationExceptionCtor => s_serializationExceptionCtor ??= typeof(SerializationException).GetConstructor(new Type[] { typeof(string) })!; + private static ConstructorInfo? s_extensionDataObjectCtor; - internal static ConstructorInfo ExtensionDataObjectCtor => s_extensionDataObjectCtor ??= - typeof(ExtensionDataObject).GetConstructor(Globals.ScanAllMembers, Type.EmptyTypes)!; + internal static ConstructorInfo ExtensionDataObjectCtor => s_extensionDataObjectCtor ??= typeof(ExtensionDataObject).GetConstructor(Globals.ScanAllMembers, Type.EmptyTypes)!; private static ConstructorInfo? s_hashtableCtor; internal static ConstructorInfo HashtableCtor @@ -529,9 +538,24 @@ internal static MethodInfo AddNewObjectWithIdMethod } } + private static MethodInfo? s_replaceDeserializedObjectMethod; + internal static MethodInfo ReplaceDeserializedObjectMethod + { + get + { + if (s_replaceDeserializedObjectMethod == null) + { + s_replaceDeserializedObjectMethod = typeof(XmlObjectSerializerReadContext).GetMethod("ReplaceDeserializedObject", Globals.ScanAllMembers); + Debug.Assert(s_replaceDeserializedObjectMethod != null); + } + return s_replaceDeserializedObjectMethod; + } + } + private static MethodInfo? s_getExistingObjectMethod; internal static MethodInfo GetExistingObjectMethod { + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] get { if (s_getExistingObjectMethod == null) @@ -613,20 +637,6 @@ internal static MethodInfo GetArrayLengthMethod } } - private static MethodInfo? s_createSerializationExceptionMethod; - internal static MethodInfo CreateSerializationExceptionMethod - { - get - { - if (s_createSerializationExceptionMethod == null) - { - s_createSerializationExceptionMethod = typeof(XmlObjectSerializerReadContext).GetMethod("CreateSerializationException", Globals.ScanAllMembers, new Type[] { typeof(string) }); - Debug.Assert(s_createSerializationExceptionMethod != null); - } - return s_createSerializationExceptionMethod; - } - } - private static MethodInfo? s_readSerializationInfoMethod; internal static MethodInfo ReadSerializationInfoMethod { @@ -821,21 +831,6 @@ internal static MethodInfo WriteISerializableMethod } } - - private static MethodInfo? s_isMemberTypeSameAsMemberValue; - internal static MethodInfo IsMemberTypeSameAsMemberValue - { - get - { - if (s_isMemberTypeSameAsMemberValue == null) - { - s_isMemberTypeSameAsMemberValue = typeof(XmlObjectSerializerWriteContext).GetMethod("IsMemberTypeSameAsMemberValue", Globals.ScanAllMembers, new Type[] { typeof(object), typeof(Type) }); - Debug.Assert(s_isMemberTypeSameAsMemberValue != null); - } - return s_isMemberTypeSameAsMemberValue; - } - } - private static MethodInfo? s_writeExtensionDataMethod; internal static MethodInfo WriteExtensionDataMethod { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs index 50ed91a7377f4..5144ee966b5ed 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs @@ -2,17 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Xml; -using System.Xml.Schema; -using System.Reflection; -using System.Reflection.Emit; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Security; +using System.Reflection; +using System.Reflection.Emit; using System.Runtime.CompilerServices; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { @@ -83,7 +84,7 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC bool memberAccessFlag = classContract.RequiresMemberAccessForRead(null); try { - _ilg.BeginMethod("Read" + classContract.StableName.Name + "FromXml", Globals.TypeOfXmlFormatClassReaderDelegate, memberAccessFlag); + _ilg.BeginMethod("Read" + classContract.XmlName.Name + "FromXml", Globals.TypeOfXmlFormatClassReaderDelegate, memberAccessFlag); } catch (SecurityException securityException) { @@ -118,14 +119,14 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC ReadClass(classContract); } - _ = InvokeFactoryMethod(classContract, objectId); + bool isFactoryType = InvokeFactoryMethod(classContract, objectId); if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom(classContract.UnderlyingType)) { _ilg.Call(_objectLocal, XmlFormatGeneratorStatics.OnDeserializationMethod, null); } InvokeOnDeserialized(classContract); - if (objectId == null) + if (objectId == null || !isFactoryType) { _ilg.Load(_objectLocal); @@ -145,12 +146,6 @@ public XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classC _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamMethod); _ilg.ConvertValue(Globals.TypeOfMemoryStream, _ilg.CurrentMethod.ReturnType); } - //Copy the KeyValuePairAdapter to a KeyValuePair. - else if (classContract.IsKeyValuePairAdapter) - { - _ilg.Call(classContract.GetKeyValuePairMethodInfo); - _ilg.ConvertValue(Globals.TypeOfKeyValuePair.MakeGenericType(classContract.KeyValuePairGenericArguments), _ilg.CurrentMethod.ReturnType); - } else { _ilg.ConvertValue(_objectLocal.LocalType, _ilg.CurrentMethod.ReturnType); @@ -212,11 +207,11 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll { if (isGetOnlyCollection) { - _ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + "IsGetOnly", Globals.TypeOfXmlFormatGetOnlyCollectionReaderDelegate, memberAccessFlag); + _ilg.BeginMethod("Read" + collectionContract.XmlName.Name + "FromXml" + "IsGetOnly", Globals.TypeOfXmlFormatGetOnlyCollectionReaderDelegate, memberAccessFlag); } else { - _ilg.BeginMethod("Read" + collectionContract.StableName.Name + "FromXml" + string.Empty, Globals.TypeOfXmlFormatCollectionReaderDelegate, memberAccessFlag); + _ilg.BeginMethod("Read" + collectionContract.XmlName.Name + "FromXml" + string.Empty, Globals.TypeOfXmlFormatCollectionReaderDelegate, memberAccessFlag); } } catch (SecurityException securityException) @@ -287,8 +282,8 @@ private void InvokeOnDeserializing(ClassDataContract classContract) Debug.Assert(_objectLocal != null); Debug.Assert(_objectType != null); - if (classContract.BaseContract != null) - InvokeOnDeserializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserializing(classContract.BaseClassContract); if (classContract.OnDeserializing != null) { _ilg.LoadAddress(_objectLocal); @@ -304,8 +299,8 @@ private void InvokeOnDeserialized(ClassDataContract classContract) Debug.Assert(_objectLocal != null); Debug.Assert(_objectType != null); - if (classContract.BaseContract != null) - InvokeOnDeserialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnDeserialized(classContract.BaseClassContract); if (classContract.OnDeserialized != null) { _ilg.LoadAddress(_objectLocal); @@ -355,7 +350,7 @@ private void ReadClass(ClassDataContract classContract) MethodInfo? extensionDataSetMethod = currentContract.ExtensionDataSetMethod; if (extensionDataSetMethod != null) _ilg.Call(_objectLocal, extensionDataSetMethod, extensionDataLocal); - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } } else @@ -384,9 +379,16 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio _ilg.Call(_contextArg, XmlFormatGeneratorStatics.GetMemberIndexWithRequiredMembersMethod, _xmlReaderArg, _memberNamesArg, _memberNamespacesArg, memberIndexLocal, requiredIndexLocal, extensionDataLocal); else _ilg.Call(_contextArg, XmlFormatGeneratorStatics.GetMemberIndexMethod, _xmlReaderArg, _memberNamesArg, _memberNamespacesArg, memberIndexLocal, extensionDataLocal); - Label[] memberLabels = _ilg.Switch(memberCount); - ReadMembers(classContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal); - _ilg.EndSwitch(); + if (memberCount > 0) + { + Label[] memberLabels = _ilg.Switch(memberCount); + ReadMembers(classContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal); + _ilg.EndSwitch(); + } + else + { + _ilg.Pop(); + } _ilg.EndFor(); if (hasRequiredMembers) { @@ -402,7 +404,7 @@ private int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, Debug.Assert(_objectLocal != null); Debug.Assert(_objectType != null); - int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers(classContract.BaseContract, requiredMembers, + int memberCount = (classContract.BaseClassContract == null) ? 0 : ReadMembers(classContract.BaseClassContract, requiredMembers, memberLabels, memberIndexLocal, requiredIndexLocal); for (int i = 0; i < classContract.Members!.Count; i++, memberCount++) @@ -428,12 +430,12 @@ private int ReadMembers(ClassDataContract classContract, bool[] requiredMembers, value = _ilg.DeclareLocal(memberType, dataMember.Name + "Value"); _ilg.Stloc(value); _ilg.Call(_contextArg, XmlFormatGeneratorStatics.StoreCollectionMemberInfoMethod, value); - ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace); + ReadValue(memberType, dataMember.Name, classContract.XmlName.Namespace); } else { _ilg.Call(_contextArg, XmlFormatGeneratorStatics.ResetCollectionMemberInfoMethod); - value = ReadValue(memberType, dataMember.Name, classContract.StableName.Namespace); + value = ReadValue(memberType, dataMember.Name, classContract.XmlName.Namespace); _ilg.LoadAddress(_objectLocal); _ilg.ConvertAddress(_objectLocal.LocalType, _objectType); _ilg.Ldloc(value); @@ -460,7 +462,7 @@ private bool[] GetRequiredMembers(ClassDataContract contract, out int firstRequi private int GetRequiredMembers(ClassDataContract contract, bool[] requiredMembers) { - int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers(contract.BaseContract, requiredMembers); + int memberCount = (contract.BaseClassContract == null) ? 0 : GetRequiredMembers(contract.BaseClassContract, requiredMembers); List members = contract.Members!; for (int i = 0; i < members.Count; i++, memberCount++) { @@ -580,14 +582,18 @@ private void InternalDeserialize(LocalBuilder value, Type type, string name, str { _ilg.Load(_contextArg); _ilg.Load(_xmlReaderArg); - Type declaredType = type; + Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type; _ilg.Load(DataContract.GetId(declaredType.TypeHandle)); _ilg.Ldtoken(declaredType); _ilg.Load(name); _ilg.Load(ns); _ilg.Call(XmlFormatGeneratorStatics.InternalDeserializeMethod); - _ilg.ConvertValue(Globals.TypeOfObject, type); + if (type.IsPointer) + _ilg.Call(XmlFormatGeneratorStatics.UnboxPointer); + else + _ilg.ConvertValue(Globals.TypeOfObject, type); + _ilg.Stloc(value); } @@ -640,7 +646,7 @@ private void ReadCollection(CollectionDataContract collectionContract) } } string itemName = collectionContract.ItemName; - string itemNs = collectionContract.StableName.Namespace; + string itemNs = collectionContract.XmlName.Namespace; _objectLocal = _ilg.DeclareLocal(type, "objectDeserialized"); if (!isArray) @@ -755,7 +761,7 @@ private void ReadGetOnlyCollection(CollectionDataContract collectionContract) Type itemType = collectionContract.ItemType; bool isArray = (collectionContract.Kind == CollectionKind.Array); string itemName = collectionContract.ItemName; - string itemNs = collectionContract.StableName.Namespace; + string itemNs = collectionContract.XmlName.Namespace; _objectLocal = _ilg.DeclareLocal(type, "objectDeserialized"); _ilg.Load(_contextArg); @@ -821,7 +827,7 @@ private bool TryReadPrimitiveArray(Type type, Type itemType, LocalBuilder size) return false; string? readArrayMethod = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: readArrayMethod = "TryReadBooleanArray"; @@ -953,19 +959,15 @@ private void ThrowUnexpectedStateException(XmlNodeType expectedState) _ilg.Throw(); } - private void ThrowValidationException(string msg, params object[] values) + private void ThrowValidationException(string msg) { - { - _ilg.Load(msg); - } + _ilg.Load(msg); ThrowValidationException(); } private void ThrowValidationException() { - //SerializationException is internal in SL and so cannot be directly invoked from DynamicMethod - //So use helper function to create SerializationException - _ilg.Call(XmlFormatGeneratorStatics.CreateSerializationExceptionMethod); + _ilg.New(XmlFormatGeneratorStatics.SerializationExceptionCtor); _ilg.Throw(); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs index b510882ffec99..2d49e4b023b47 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs @@ -2,17 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Xml; -using System.Xml.Schema; -using System.Reflection; -using System.Reflection.Emit; using System.Collections; using System.Collections.Generic; -using System.Globalization; -using System.Security; -using System.Runtime.CompilerServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { @@ -78,7 +79,7 @@ internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract clas bool memberAccessFlag = classContract.RequiresMemberAccessForWrite(null); try { - _ilg.BeginMethod("Write" + classContract.StableName.Name + "ToXml", Globals.TypeOfXmlFormatClassWriterDelegate, memberAccessFlag); + _ilg.BeginMethod("Write" + classContract.XmlName.Name + "ToXml", Globals.TypeOfXmlFormatClassWriterDelegate, memberAccessFlag); } catch (SecurityException securityException) { @@ -92,6 +93,10 @@ internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract clas } } InitArgs(classContract.UnderlyingType); + if (classContract.IsReadOnlyContract) + { + ThrowIfCannotSerializeReadOnlyTypes(classContract); + } WriteClass(classContract); return (XmlFormatClassWriterDelegate)_ilg.EndMethod(); } @@ -116,7 +121,7 @@ internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDa bool memberAccessFlag = collectionContract.RequiresMemberAccessForWrite(null); try { - _ilg.BeginMethod("Write" + collectionContract.StableName.Name + "ToXml", Globals.TypeOfXmlFormatCollectionWriterDelegate, memberAccessFlag); + _ilg.BeginMethod("Write" + collectionContract.XmlName.Name + "ToXml", Globals.TypeOfXmlFormatCollectionWriterDelegate, memberAccessFlag); } catch (SecurityException securityException) { @@ -130,6 +135,10 @@ internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDa } } InitArgs(collectionContract.UnderlyingType); + if (collectionContract.IsReadOnlyContract) + { + ThrowIfCannotSerializeReadOnlyTypes(collectionContract); + } WriteCollection(collectionContract); return (XmlFormatCollectionWriterDelegate)_ilg.EndMethod(); } @@ -160,13 +169,6 @@ private void InitArgs(Type objType) _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfMemoryStream); _ilg.Call(XmlFormatGeneratorStatics.GetMemoryStreamAdapterMethod); } - //Copy the KeyValuePair to a KeyValuePairAdapter. - else if (objType.IsGenericType && objType.GetGenericTypeDefinition() == Globals.TypeOfKeyValuePairAdapter) - { - ClassDataContract dc = (ClassDataContract)DataContract.GetDataContract(objType); - _ilg.ConvertValue(objectArg.ArgType, Globals.TypeOfKeyValuePair.MakeGenericType(dc.KeyValuePairGenericArguments!)); - _ilg.New(dc.KeyValuePairAdapterConstructorInfo!); - } else { _ilg.ConvertValue(objectArg.ArgType, objType); @@ -174,11 +176,32 @@ private void InitArgs(Type objType) _ilg.Stloc(_objectLocal); } + private void ThrowIfCannotSerializeReadOnlyTypes(ClassDataContract classContract) + { + ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.ClassSerializationExceptionMessageProperty); + } + + private void ThrowIfCannotSerializeReadOnlyTypes(CollectionDataContract classContract) + { + ThrowIfCannotSerializeReadOnlyTypes(XmlFormatGeneratorStatics.CollectionSerializationExceptionMessageProperty); + } + + private void ThrowIfCannotSerializeReadOnlyTypes(PropertyInfo serializationExceptionMessageProperty) + { + _ilg.Load(_contextArg); + _ilg.LoadMember(XmlFormatGeneratorStatics.SerializeReadOnlyTypesProperty); + _ilg.IfNot(); + _ilg.Load(_dataContractArg); + _ilg.LoadMember(serializationExceptionMessageProperty); + _ilg.Load(null); + _ilg.Call(XmlFormatGeneratorStatics.ThrowInvalidDataContractExceptionMethod); + _ilg.EndIf(); + } private void InvokeOnSerializing(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerializing(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerializing(classContract.BaseClassContract); if (classContract.OnSerializing != null) { _ilg.LoadAddress(_objectLocal); @@ -190,8 +213,8 @@ private void InvokeOnSerializing(ClassDataContract classContract) private void InvokeOnSerialized(ClassDataContract classContract) { - if (classContract.BaseContract != null) - InvokeOnSerialized(classContract.BaseContract); + if (classContract.BaseClassContract != null) + InvokeOnSerialized(classContract.BaseClassContract); if (classContract.OnSerialized != null) { _ilg.LoadAddress(_objectLocal); @@ -257,8 +280,8 @@ private void WriteClass(ClassDataContract classContract) [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] private int WriteMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal, ClassDataContract derivedMostClassContract) { - int memberCount = (classContract.BaseContract == null) ? 0 : - WriteMembers(classContract.BaseContract, extensionDataLocal, derivedMostClassContract); + int memberCount = (classContract.BaseClassContract == null) ? 0 : + WriteMembers(classContract.BaseClassContract, extensionDataLocal, derivedMostClassContract); LocalBuilder namespaceLocal = _ilg.DeclareLocal(typeof(XmlDictionaryString), "ns"); if (_contractNamespacesLocal == null) @@ -559,7 +582,7 @@ private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value return false; string? writeArrayMethod = null; - switch (itemType.GetTypeCode()) + switch (Type.GetTypeCode(itemType)) { case TypeCode.Boolean: writeArrayMethod = "WriteBooleanArray"; @@ -601,6 +624,15 @@ private bool TryWritePrimitiveArray(Type type, Type itemType, LocalBuilder value private void WriteValue(LocalBuilder memberValue, bool writeXsiType) { Type memberType = memberValue.LocalType; + if (memberType.IsPointer) + { + _ilg.Load(memberValue); + _ilg.Load(memberType); + _ilg.Call(XmlFormatGeneratorStatics.BoxPointer); + memberType = Globals.TypeOfReflectionPointer; + memberValue = _ilg.DeclareLocal(memberType, "memberValueRefPointer"); + _ilg.Store(memberValue); + } bool isNullableOfT = (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable); if (memberType.IsValueType && !isNullableOfT) @@ -670,10 +702,12 @@ private void InternalSerialize(MethodInfo methodInfo, LocalBuilder memberValue, _ilg.Load(_xmlWriterArg); _ilg.Load(memberValue); _ilg.ConvertValue(memberValue.LocalType, Globals.TypeOfObject); - //In SL GetTypeHandle throws MethodAccessException as its internal and extern. - //So as a workaround, call XmlObjectSerializerWriteContext.IsMemberTypeSameAsMemberValue that - //does the actual comparison and returns the bool value we care. - _ilg.Call(null, XmlFormatGeneratorStatics.IsMemberTypeSameAsMemberValue, memberValue, memberType); + LocalBuilder typeHandleValue = _ilg.DeclareLocal(typeof(RuntimeTypeHandle), "typeHandleValue"); + _ilg.Call(null, typeof(Type).GetMethod("GetTypeHandle")!, memberValue); + _ilg.Stloc(typeHandleValue); + _ilg.LoadAddress(typeHandleValue); + _ilg.Ldtoken(memberType); + _ilg.Call(typeof(RuntimeTypeHandle).GetMethod("Equals", new Type[] { typeof(RuntimeTypeHandle) })!); _ilg.Load(writeXsiType); _ilg.Load(DataContract.GetId(memberType.TypeHandle)); _ilg.Ldtoken(memberType); @@ -748,11 +782,11 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac // Check for conflict with derived type members string name = member.Name; - string ns = classContract.StableName.Namespace; + string ns = classContract.XmlName.Namespace; ClassDataContract? currentContract = derivedMostClassContract; while (currentContract != null && currentContract != classContract) { - if (ns == currentContract.StableName.Namespace) + if (ns == currentContract.XmlName.Namespace) { List members = currentContract.Members!; for (int j = 0; j < members.Count; j++) @@ -761,7 +795,7 @@ private static bool CheckIfMemberHasConflict(DataMember member, ClassDataContrac return CheckIfConflictingMembersHaveDifferentTypes(members[j]); } } - currentContract = currentContract.BaseContract; + currentContract = currentContract.BaseClassContract; } return false; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs index 8ba00fd704175..e656e9b259c83 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs @@ -7,10 +7,12 @@ using System.Globalization; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Text; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -220,7 +222,7 @@ internal static bool CheckIfNeedsContractNsAtRoot(XmlDictionaryString? name, Xml if (name == null) return false; - if (contract.IsBuiltInDataContract || !contract.CanContainReferences) + if (contract.IsBuiltInDataContract || !contract.CanContainReferences || contract.IsISerializable) { return false; } @@ -378,12 +380,12 @@ internal static bool IsRootElement(XmlReaderDelegator reader, DataContract contr ClassDataContract? classContract = contract as ClassDataContract; if (classContract != null) - classContract = classContract.BaseContract; + classContract = classContract.BaseClassContract; while (classContract != null) { if (reader.IsStartElement(classContract.TopLevelElementName!, classContract.TopLevelElementNamespace!)) return true; - classContract = classContract.BaseContract; + classContract = classContract.BaseClassContract; } if (classContract == null) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs index 66ce7c6f8f8c0..57b3353cee2cb 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerContext.cs @@ -6,9 +6,11 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Xml; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -46,19 +48,13 @@ internal XmlObjectSerializerContext(XmlObjectSerializer serializer, int maxItems internal XmlObjectSerializerContext(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver) : this(serializer, serializer.MaxItemsInObjectGraph, - default(StreamingContext), + new StreamingContext(StreamingContextStates.All), serializer.IgnoreExtensionDataObject, dataContractResolver ) { this.rootTypeDataContract = rootTypeDataContract; - this.serializerKnownTypeList = serializer.knownTypeList; - } - - - internal virtual SerializationMode Mode - { - get { return SerializationMode.SharedContract; } + this.serializerKnownTypeList = serializer._knownTypeList; } internal virtual bool IsGetOnlyCollection @@ -67,7 +63,6 @@ internal virtual bool IsGetOnlyCollection set { } } - internal StreamingContext GetStreamingContext() { return _streamingContext; @@ -109,11 +104,11 @@ internal virtual DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type { if (IsGetOnlyCollection) { - return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(typeHandle), typeHandle, type, Mode); + return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(typeHandle), typeHandle, type); } else { - return DataContract.GetDataContract(typeHandle, type, Mode); + return DataContract.GetDataContract(typeHandle, type); } } @@ -135,11 +130,11 @@ internal virtual DataContract GetDataContract(int id, RuntimeTypeHandle typeHand { if (IsGetOnlyCollection) { - return DataContract.GetGetOnlyCollectionDataContract(id, typeHandle, null /*type*/, Mode); + return DataContract.GetGetOnlyCollectionDataContract(id, typeHandle, null /*type*/); } else { - return DataContract.GetDataContract(id, typeHandle, Mode); + return DataContract.GetDataContract(id, typeHandle); } } @@ -201,7 +196,7 @@ internal virtual DataContractDictionary? SerializerKnownDataContracts internal bool IsKnownType(DataContract dataContract, DataContractDictionary? knownDataContracts, Type? declaredType) { bool knownTypesAddedInCurrentScope = false; - if (knownDataContracts != null) + if (knownDataContracts?.Count > 0) { scopedKnownTypes.Push(knownDataContracts); knownTypesAddedInCurrentScope = true; @@ -219,7 +214,7 @@ internal bool IsKnownType(DataContract dataContract, DataContractDictionary? kno [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal bool IsKnownType(DataContract dataContract, Type? declaredType) { - DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.StableName.Name, dataContract.StableName.Namespace, null /*memberTypeContract*/, declaredType); + DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.XmlName.Name, dataContract.XmlName.Namespace, null /*memberTypeContract*/, declaredType); return knownContract != null && knownContract.UnderlyingType == dataContract.UnderlyingType; } @@ -227,7 +222,7 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) internal Type? ResolveNameFromKnownTypes(XmlQualifiedName typeName) { DataContract? dataContract = ResolveDataContractFromKnownTypes(typeName); - return dataContract?.UnderlyingType; + return dataContract?.OriginalUnderlyingType; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -254,13 +249,13 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) { if (memberTypeContract != null && !memberTypeContract.UnderlyingType.IsInterface - && memberTypeContract.StableName == qname) + && memberTypeContract.XmlName == qname) { dataContract = memberTypeContract; } if (dataContract == null && rootTypeDataContract != null) { - if (rootTypeDataContract.StableName == qname) + if (rootTypeDataContract.XmlName == qname) dataContract = rootTypeDataContract; else dataContract = ResolveDataContractFromRootDataContract(qname); @@ -276,7 +271,7 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) while (collectionContract != null) { DataContract itemContract = GetDataContract(GetSurrogatedType(collectionContract.ItemType)); - if (itemContract.StableName == typeQName) + if (itemContract.XmlName == typeQName) { return itemContract; } @@ -284,23 +279,5 @@ internal bool IsKnownType(DataContract dataContract, Type? declaredType) } return null; } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void PushKnownTypes(DataContract dc) - { - if (dc != null && dc.KnownDataContracts != null) - { - scopedKnownTypes.Push(dc.KnownDataContracts); - } - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal void PopKnownTypes(DataContract dc) - { - if (dc != null && dc.KnownDataContracts != null) - { - scopedKnownTypes.Pop(); - } - } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs index 09fb6e269353d..ed546bc93ee99 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs @@ -6,11 +6,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; using System.Security; using System.Text; using System.Xml; using System.Xml.Serialization; -using DataContractDictionary = System.Collections.Generic.Dictionary; + +using DataContractDictionary = System.Collections.Generic.Dictionary; namespace System.Runtime.Serialization { @@ -21,6 +23,7 @@ internal class XmlObjectSerializerReadContext : XmlObjectSerializerContext private XmlSerializableReader? _xmlSerializableReader; private XmlDocument? _xmlDocument; private Attributes? _attributesInXmlData; + private XmlReaderDelegator? _extensionDataReader; private object? _getOnlyCollectionValue; private bool _isGetOnlyCollection; @@ -89,7 +92,7 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) + internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string? name, string? ns) { DataContract dataContract = GetDataContract(declaredType); return InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract); @@ -102,6 +105,7 @@ internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataC return InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract); } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] protected bool TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, string? name, string? ns, ref object? retObj) { ReadAttributes(reader); @@ -135,7 +139,7 @@ protected bool TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, return retObj; bool knownTypesAddedInCurrentScope = false; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); knownTypesAddedInCurrentScope = true; @@ -194,7 +198,7 @@ private bool ReplaceScopedKnownTypesTop(DataContractDictionary? knownDataContrac scopedKnownTypes.Pop(); knownTypesAddedInCurrentScope = false; } - if (knownDataContracts != null) + if (knownDataContracts?.Count > 0) { scopedKnownTypes.Push(knownDataContracts); knownTypesAddedInCurrentScope = true; @@ -331,6 +335,8 @@ internal void AddNewObjectWithId(string id, object? obj) { if (id != Globals.NewObjectId) DeserializedObjects.Add(id, obj); + if (_extensionDataReader?.UnderlyingExtensionDataReader != null) + _extensionDataReader.UnderlyingExtensionDataReader.SetDeserializedValue(obj); } public void ReplaceDeserializedObject(string id, object? oldObj, object? newObj) @@ -355,13 +361,22 @@ public void ReplaceDeserializedObject(string id, object? oldObj, object? newObj) DeserializedObjects.Remove(id); DeserializedObjects.Add(id, newObj); } + if (_extensionDataReader?.UnderlyingExtensionDataReader != null) + _extensionDataReader.UnderlyingExtensionDataReader.SetDeserializedValue(newObj); } - internal object GetExistingObject(string id, Type? type, string? name, string? ns) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + internal object? GetExistingObject(string id, Type? type, string? name, string? ns) { object? retObj = DeserializedObjects.GetObject(id); + if (retObj == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DeserializedObjectWithIdNotFound, id))); + + if (retObj is IDataNode dataNode) + { + retObj = (dataNode.Value != null && dataNode.IsFinalValue) ? dataNode.Value : DeserializeFromExtensionData(dataNode, type ?? dataNode.DataType, name, ns); + } return retObj; } @@ -392,6 +407,18 @@ public object GetRealObject(IObjectReference obj, string id) return realObj; } + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private object? DeserializeFromExtensionData(IDataNode dataNode, Type type, string? name, string? ns) + { + // _extensionDataRead is only ever created here, so we know the casting property 'UnderlyingExtensionDataReader' won't be null. + _extensionDataReader ??= CreateReaderDelegatorForReader(new ExtensionDataReader(this)); + _extensionDataReader.UnderlyingExtensionDataReader!.SetDataNode(dataNode, name, ns); + object? retObj = InternalDeserialize(_extensionDataReader, type, name, ns); + dataNode.Clear(); + _extensionDataReader.UnderlyingExtensionDataReader!.Reset(); + return retObj; + } + internal static void Read(XmlReaderDelegator xmlReader) { if (!xmlReader.Read()) @@ -842,9 +869,11 @@ private ISerializableDataNode ReadUnknownISerializableData(XmlReaderDelegator xm private IDataNode ReadUnknownXmlData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace) { - XmlDataNode dataNode = new XmlDataNode(); + XmlDataNode dataNode = new XmlDataNode() + { + OwnerDocument = Document + }; InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace); - dataNode.OwnerDocument = Document; if (xmlReader.NodeType == XmlNodeType.EndElement) return dataNode; @@ -957,9 +986,11 @@ private IDataNode ReadAndResolveUnknownXmlData(XmlReaderDelegator xmlReader, IDi return ReadUnknownClassData(CreateReaderOverChildNodes(xmlAttributes, xmlChildNodes), dataContractName, dataContractNamespace); else { - XmlDataNode dataNode = new XmlDataNode(); + XmlDataNode dataNode = new XmlDataNode() + { + OwnerDocument = Document + }; InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace); - dataNode.OwnerDocument = Document; dataNode.XmlChildNodes = xmlChildNodes; dataNode.XmlAttributes = xmlAttributes; return dataNode; @@ -983,14 +1014,14 @@ private static bool IsContentNode(XmlNodeType nodeType) internal XmlReaderDelegator CreateReaderOverChildNodes(IList? xmlAttributes, IList xmlChildNodes) { - XmlNode wrapperElement = CreateWrapperXmlElement(Document, xmlAttributes, xmlChildNodes, null, null, null); + XmlElement wrapperElement = CreateWrapperXmlElement(Document, xmlAttributes, xmlChildNodes, null, null, null); XmlReaderDelegator nodeReader = CreateReaderDelegatorForReader(new XmlNodeReader(wrapperElement)); nodeReader.MoveToContent(); Read(nodeReader); return nodeReader; } - internal static XmlNode CreateWrapperXmlElement(XmlDocument document, IList? xmlAttributes, IList xmlChildNodes, string? prefix, string? localName, string? ns) + internal static XmlElement CreateWrapperXmlElement(XmlDocument document, IList? xmlAttributes, IList? xmlChildNodes, string? prefix, string? localName, string? ns) { localName ??= "wrapper"; ns ??= string.Empty; @@ -1026,12 +1057,6 @@ internal static Exception CreateUnexpectedStateException(XmlNodeType expectedSta return XmlObjectSerializer.CreateSerializationExceptionWithReaderDetails(SR.Format(SR.ExpectingState, expectedState), xmlReader); } - //Silverlight only helper function to create SerializationException - internal static Exception CreateSerializationException(string message) - { - return XmlObjectSerializer.CreateSerializationException(message); - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] protected virtual object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs index 53f4021549ec1..7458857221d20 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContextComplex.cs @@ -1,22 +1,22 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.Serialization.DataContracts; namespace System.Runtime.Serialization { internal class XmlObjectSerializerReadContextComplex : XmlObjectSerializerReadContext { private readonly bool _preserveObjectReferences; - private readonly SerializationMode _mode; private readonly ISerializationSurrogateProvider? _serializationSurrogateProvider; internal XmlObjectSerializerReadContextComplex(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver) : base(serializer, rootTypeDataContract, dataContractResolver) { - _mode = SerializationMode.SharedContract; _preserveObjectReferences = serializer.PreserveObjectReferences; _serializationSurrogateProvider = serializer.SerializationSurrogateProvider; } @@ -26,99 +26,31 @@ internal XmlObjectSerializerReadContextComplex(XmlObjectSerializer serializer, i { } - internal override SerializationMode Mode - { - get { return _mode; } - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, string name, string ns) { - if (_mode == SerializationMode.SharedContract) - { - if (_serializationSurrogateProvider == null) - return base.InternalDeserialize(xmlReader, declaredTypeID, declaredTypeHandle, name, ns); - else - return InternalDeserializeWithSurrogate(xmlReader, Type.GetTypeFromHandle(declaredTypeHandle)!, null /*surrogateDataContract*/, name, ns); - } + if (_serializationSurrogateProvider == null) + return base.InternalDeserialize(xmlReader, declaredTypeID, declaredTypeHandle, name, ns); else - { - return InternalDeserializeInSharedTypeMode(xmlReader, declaredTypeID, Type.GetTypeFromHandle(declaredTypeHandle)!, name, ns); - } + return InternalDeserializeWithSurrogate(xmlReader, Type.GetTypeFromHandle(declaredTypeHandle)!, null /*surrogateDataContract*/, name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns) + internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string? name, string? ns) { - if (_mode == SerializationMode.SharedContract) - { - if (_serializationSurrogateProvider == null) - return base.InternalDeserialize(xmlReader, declaredType, name, ns); - else - return InternalDeserializeWithSurrogate(xmlReader, declaredType, null /*surrogateDataContract*/, name, ns); - } + if (_serializationSurrogateProvider == null) + return base.InternalDeserialize(xmlReader, declaredType, name, ns); else - { - return InternalDeserializeInSharedTypeMode(xmlReader, -1, declaredType, name, ns); - } + return InternalDeserializeWithSurrogate(xmlReader, declaredType, null /*surrogateDataContract*/, name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract? dataContract, string? name, string? ns) { - if (_mode == SerializationMode.SharedContract) - { - if (_serializationSurrogateProvider == null) - return base.InternalDeserialize(xmlReader, declaredType, dataContract, name, ns); - else - return InternalDeserializeWithSurrogate(xmlReader, declaredType, dataContract, name, ns); - } - else - { - return InternalDeserializeInSharedTypeMode(xmlReader, -1, declaredType, name, ns); - } - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private object? InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, int declaredTypeID, Type declaredType, string? name, string? ns) - { - Debug.Assert(attributes != null); - - object? retObj = null; - if (TryHandleNullOrRef(xmlReader, declaredType, name, ns, ref retObj)) - return retObj; - - DataContract dataContract; - string? assemblyName = attributes.ClrAssembly; - string? typeName = attributes.ClrType; - if (assemblyName != null && typeName != null) - { - Assembly assembly; - DataContract? tempDataContract = ResolveDataContractInSharedTypeMode(assemblyName, typeName, out assembly, out _); - if (tempDataContract == null) - { - if (assembly == null) - throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.AssemblyNotFound, assemblyName)); - - throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ClrTypeNotFound, assembly.FullName, typeName)); - } - - dataContract = tempDataContract; - //Array covariance is not supported in XSD. If declared type is array, data is sent in format of base array - if (declaredType != null && declaredType.IsArray) - dataContract = (declaredTypeID < 0) ? GetDataContract(declaredType) : GetDataContract(declaredTypeID, declaredType.TypeHandle); - } + if (_serializationSurrogateProvider == null) + return base.InternalDeserialize(xmlReader, declaredType, dataContract, name, ns); else - { - if (assemblyName != null) - throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.Format(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrTypeLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))); - else if (typeName != null) - throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.Format(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrAssemblyLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))); - else if (declaredType == null) - throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.Format(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrTypeLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))); - dataContract = (declaredTypeID < 0) ? GetDataContract(declaredType) : GetDataContract(declaredTypeID, declaredType.TypeHandle); - } - return ReadDataContractValue(dataContract, xmlReader); + return InternalDeserializeWithSurrogate(xmlReader, declaredType, dataContract, name, ns); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -141,44 +73,6 @@ internal override SerializationMode Mode return obj; } - private static Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly) - { - // The method is used only when _mode == SerializationMode.SharedType. - // _mode is set to SerializationMode.SharedType only when the context is for NetDataContractSerializer. - throw new PlatformNotSupportedException(SR.PlatformNotSupported_NetDataContractSerializer); - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private DataContract? ResolveDataContractInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly, out Type type) - { - type = ResolveDataContractTypeInSharedTypeMode(assemblyName, typeName, out assembly); - if (type != null) - { - return GetDataContract(type); - } - - return null; - } - - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - protected override DataContract? ResolveDataContractFromTypeName() - { - Debug.Assert(attributes != null); - - if (_mode == SerializationMode.SharedContract) - { - return base.ResolveDataContractFromTypeName(); - } - else - { - if (attributes.ClrAssembly != null && attributes.ClrType != null) - { - return ResolveDataContractInSharedTypeMode(attributes.ClrAssembly, attributes.ClrType, out _, out _); - } - } - return null; - } - [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs index 00abd388dc092..9abdced84b6e2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs @@ -3,18 +3,20 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Security; using System.Text; using System.Xml; -using System.Collections.Generic; using System.Xml.Serialization; -using System.Security; -using System.Runtime.CompilerServices; + using ExtensionDataObject = System.Object; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization { @@ -122,7 +124,7 @@ internal void SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelega { if (OnHandleIsReference(xmlWriter, dataContract, obj)) return; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); WriteDataContractValue(dataContract, xmlWriter, obj, declaredTypeHandle); @@ -140,7 +142,7 @@ internal virtual void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, Debug.Assert(rootTypeDataContract != null); bool verifyKnownType = false; - Type declaredType = rootTypeDataContract.UnderlyingType; + Type declaredType = rootTypeDataContract.OriginalUnderlyingType; if (declaredType.IsInterface && CollectionDataContract.IsCollectionInterface(declaredType)) { @@ -224,7 +226,7 @@ internal bool OnHandleIsReference(XmlWriterDelegator xmlWriter, DataContract con protected void SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, bool verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) { bool knownTypesAddedInCurrentScope = false; - if (dataContract.KnownDataContracts != null) + if (dataContract.KnownDataContracts?.Count > 0) { scopedKnownTypes.Push(dataContract.KnownDataContracts); knownTypesAddedInCurrentScope = true; @@ -234,10 +236,10 @@ protected void SerializeAndVerifyType(DataContract dataContract, XmlWriterDelega { if (!IsKnownType(dataContract, declaredType)) { - DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.StableName.Name, dataContract.StableName.Namespace, null /*memberTypeContract*/, declaredType); + DataContract? knownContract = ResolveDataContractFromKnownTypes(dataContract.XmlName.Name, dataContract.XmlName.Namespace, null /*memberTypeContract*/, declaredType); if (knownContract == null || knownContract.UnderlyingType != dataContract.UnderlyingType) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.XmlName.Name, dataContract.XmlName.Namespace))); } } } @@ -501,11 +503,12 @@ public void WriteISerializable(XmlWriterDelegator xmlWriter, ISerializable obj) var serInfo = new SerializationInfo(objType, XmlObjectSerializer.FormatterConverter /*!UnsafeTypeForwardingEnabled is always false*/); GetObjectData(obj, serInfo, GetStreamingContext()); - if (!UnsafeTypeForwardingEnabled && serInfo.AssemblyName == Globals.MscorlibAssemblyName) - { - // Throw if a malicious type tries to set its assembly name to "0" to get deserialized in mscorlib - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ISerializableAssemblyNameSetToZero, DataContract.GetClrTypeFullName(obj.GetType())))); - } + // (!UnsafeTypeForwardingEnabled) is always false + //if (!UnsafeTypeForwardingEnabled && serInfo.AssemblyName == Globals.MscorlibAssemblyName) + //{ + // // Throw if a malicious type tries to set its assembly name to "0" to get deserialized in mscorlib + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ISerializableAssemblyNameSetToZero, DataContract.GetClrTypeFullName(obj.GetType())))); + //} WriteSerializationInfo(xmlWriter, objType, serInfo); } @@ -526,7 +529,7 @@ internal void WriteSerializationInfo(XmlWriterDelegator xmlWriter, Type objType, else { string typeName, typeNs; - DataContract.GetDefaultStableName(serInfo.FullTypeName, out typeName, out typeNs); + DataContract.GetDefaultXmlName(serInfo.FullTypeName, out typeName, out typeNs); xmlWriter.WriteAttributeQualifiedName(Globals.SerPrefix, DictionaryGlobals.ISerializableFactoryTypeLocalName, DictionaryGlobals.SerializationNamespace, DataContract.GetClrTypeString(typeName), DataContract.GetClrTypeString(typeNs)); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs index b1abf32a8ee00..1d9ebcb41ddd0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContextComplex.cs @@ -3,28 +3,27 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.DataContracts; +using System.Security; using System.Text; using System.Xml; -using System.Collections.Generic; -using System.Security; -using System.Runtime.CompilerServices; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization { internal class XmlObjectSerializerWriteContextComplex : XmlObjectSerializerWriteContext { private readonly ISerializationSurrogateProvider? _serializationSurrogateProvider; - private readonly SerializationMode _mode; internal XmlObjectSerializerWriteContextComplex(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver) : base(serializer, rootTypeDataContract, dataContractResolver) { - _mode = SerializationMode.SharedContract; this.preserveObjectReferences = serializer.PreserveObjectReferences; _serializationSurrogateProvider = serializer.SerializationSurrogateProvider; } @@ -34,11 +33,6 @@ internal XmlObjectSerializerWriteContextComplex(XmlObjectSerializer serializer, { } - internal override SerializationMode Mode - { - get { return _mode; } - } - internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, DataContract dataContract) { return false; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs index 716e568dda351..597b48e63ac14 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlReaderDelegator.cs @@ -1,11 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Globalization; using System.Collections.Generic; -using System.Xml.Serialization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Xml.Serialization; namespace System.Runtime.Serialization { @@ -55,7 +56,8 @@ internal string GetAttribute(int i) return reader.GetAttribute(i); } - internal static bool IsEmptyElement + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Conceptually, this property describes this instance. Callers should expect to have an instance on hand to 'ask' about this 'emtpy' circumstance.")] + internal bool IsEmptyElement { get { return false; } } @@ -496,7 +498,7 @@ internal virtual DateTime ReadElementContentAsDateTime() if (isEndOfEmptyElement) ThrowNotAtElement(); - return XmlConvert.ToDateTime(reader.ReadElementContentAsString(), XmlDateTimeSerializationMode.RoundtripKind); + return reader.ReadElementContentAsDateTime(); } internal virtual DateTime ReadContentAsDateTime() @@ -783,9 +785,12 @@ private static void CheckExpectedArrayLength(XmlObjectSerializerReadContext cont context.IncrementItemCount(arrayLength); } - protected static int GetArrayLengthQuota(XmlObjectSerializerReadContext context) + protected int GetArrayLengthQuota(XmlObjectSerializerReadContext context) { - return Math.Min(context.RemainingItemCount, int.MaxValue); + if (dictionaryReader?.Quotas == null) + return context.RemainingItemCount; + + return Math.Min(context.RemainingItemCount, dictionaryReader.Quotas.MaxArrayLength); } private static void CheckActualArrayLength(int expectedLength, int actualLength, XmlDictionaryString itemName, XmlDictionaryString itemNamespace) @@ -1039,8 +1044,7 @@ internal bool Normalized { get { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { IXmlTextParser? xmlTextParser = reader as IXmlTextParser; return (xmlTextParser == null) ? false : xmlTextParser.Normalized; @@ -1050,11 +1054,9 @@ internal bool Normalized } set { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { - IXmlTextParser? xmlTextParser = reader as IXmlTextParser; - if (xmlTextParser != null) + if (reader is IXmlTextParser xmlTextParser) xmlTextParser.Normalized = value; } else @@ -1066,8 +1068,7 @@ internal WhitespaceHandling WhitespaceHandling { get { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { IXmlTextParser? xmlTextParser = reader as IXmlTextParser; return (xmlTextParser == null) ? WhitespaceHandling.None : xmlTextParser.WhitespaceHandling; @@ -1077,11 +1078,9 @@ internal WhitespaceHandling WhitespaceHandling } set { - XmlTextReader? xmlTextReader = reader as XmlTextReader; - if (xmlTextReader == null) + if (reader is not XmlTextReader xmlTextReader) { - IXmlTextParser? xmlTextParser = reader as IXmlTextParser; - if (xmlTextParser != null) + if (reader is IXmlTextParser xmlTextParser) xmlTextParser.WhitespaceHandling = value; } else diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs index a6a66b6c44acd..7d01b25c192b5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs @@ -10,7 +10,7 @@ namespace System.Runtime.Serialization { - internal sealed class XmlSerializableReader : XmlReader, IXmlLineInfo + internal sealed class XmlSerializableReader : XmlReader, IXmlLineInfo, IXmlTextParser // IXmlTextParser (Normalized, WhitespaceHandling) was added. Is it ever used? { private XmlReaderDelegator _xmlReader = null!; // initialized in BeginRead private int _startDepth; @@ -79,8 +79,10 @@ public override void Close() public override string BaseURI { get { return InnerReader.BaseURI; } } public override bool IsEmptyElement { get { return InnerReader.IsEmptyElement; } } public override bool IsDefault { get { return InnerReader.IsDefault; } } + public override char QuoteChar { get { return InnerReader.QuoteChar; } } public override XmlSpace XmlSpace { get { return InnerReader.XmlSpace; } } public override string XmlLang { get { return InnerReader.XmlLang; } } + public override IXmlSchemaInfo? SchemaInfo { get { return InnerReader.SchemaInfo; } } public override Type ValueType { get { return InnerReader.ValueType; } } public override int AttributeCount { get { return InnerReader.AttributeCount; } } public override string this[int i] { get { return InnerReader[i]; } } @@ -122,6 +124,41 @@ public override void Close() public override int ReadContentAsBase64(byte[] buffer, int index, int count) { return InnerReader.ReadContentAsBase64(buffer, index, count); } public override int ReadContentAsBinHex(byte[] buffer, int index, int count) { return InnerReader.ReadContentAsBinHex(buffer, index, count); } public override int ReadValueChunk(char[] buffer, int index, int count) { return InnerReader.ReadValueChunk(buffer, index, count); } + public override string ReadString() { return InnerReader.ReadString(); } + + // IXmlTextParser members + bool IXmlTextParser.Normalized + { + get + { + IXmlTextParser? xmlTextParser = InnerReader as IXmlTextParser; + return (xmlTextParser == null) ? _xmlReader.Normalized : xmlTextParser.Normalized; + } + set + { + if (InnerReader is not IXmlTextParser xmlTextParser) + _xmlReader.Normalized = value; + else + xmlTextParser.Normalized = value; + } + } + + WhitespaceHandling IXmlTextParser.WhitespaceHandling + { + get + { + IXmlTextParser? xmlTextParser = InnerReader as IXmlTextParser; + return (xmlTextParser == null) ? _xmlReader.WhitespaceHandling : xmlTextParser.WhitespaceHandling; + } + set + { + if (InnerReader is not IXmlTextParser xmlTextParser) + _xmlReader.WhitespaceHandling = value; + else + xmlTextParser.WhitespaceHandling = value; + } + } + // IXmlLineInfo members bool IXmlLineInfo.HasLineInfo() { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs index 8dc67936bf496..1b9c39249c6ba 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.Serialization.DataContracts; using System.Xml; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs index d9366ebf510cd..9873c19c40abb 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Xml; +using System.Runtime.Serialization.DataContracts; using System.Globalization; @@ -281,7 +282,7 @@ internal void WriteAnyType(object value) internal void WriteAnyType(object value, Type valueType) { bool handled = true; - switch (valueType.GetTypeCode()) + switch (Type.GetTypeCode(valueType)) { case TypeCode.Boolean: WriteBoolean((bool)value); @@ -329,6 +330,7 @@ internal void WriteAnyType(object value, Type valueType) WriteUnsignedLong((ulong)value); break; case TypeCode.Empty: + case TypeCode.DBNull: case TypeCode.Object: default: if (valueType == Globals.TypeOfByteArray) @@ -453,7 +455,7 @@ internal void WriteBoolean(bool value, XmlDictionaryString name, XmlDictionarySt internal virtual void WriteDateTime(DateTime value) { - WriteString(XmlConvert.ToString(value, XmlDateTimeSerializationMode.RoundtripKind)); + writer.WriteValue(value); } internal void WriteDateTime(DateTime value, XmlDictionaryString name, XmlDictionaryString? ns) @@ -618,13 +620,6 @@ internal void WriteTimeSpan(TimeSpan value) writer.WriteRaw(XmlConvert.ToString(value)); } - internal void WriteTimeSpan(char value, XmlDictionaryString name, XmlDictionaryString? ns) - { - WriteStartElementPrimitive(name, ns); - writer.WriteRaw(XmlConvert.ToString(value)); - WriteEndElementPrimitive(); - } - internal void WriteTimeSpan(TimeSpan value, XmlDictionaryString name, XmlDictionaryString? ns) { WriteStartElementPrimitive(name, ns); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs index ad147ba1af8fd..3a59e19a55867 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XsdDataContractExporter.cs @@ -1,42 +1,73 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; -using System.Xml.Schema; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Reflection; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Xml; +using System.Xml.Schema; namespace System.Runtime.Serialization { + /// + /// Allows the transformation of a set of .NET types that are used in data contracts into an XML schema file (.xsd). + /// + /// + /// Use the class when you have created a Web service that incorporates data represented by + /// runtime types and when you need to export XML schemas for each type to be consumed by other Web services. + /// That is, transforms a set of runtime types into XML schemas. The schemas can then be exposed + /// through a Web Services Description Language (WSDL) document for use by others who need to interoperate with your service. + /// + /// Conversely, if you are creating a Web service that must interoperate with an existing Web service, use the XsdDataContractImporter + /// to transform XML schemas and create the runtime types that represent the data in a selected programming language. + /// + /// The generates an object that contains the collection of schemas. + /// Access the set of schemas through the property. + /// public class XsdDataContractExporter { private ExportOptions? _options; private XmlSchemaSet? _schemas; private DataContractSet? _dataContractSet; + /// + /// Initializes a new instance of the class. + /// public XsdDataContractExporter() { } + /// + /// Initializes a new instance of the class with the specified set of schemas. + /// + /// An that contains the schemas to be exported. public XsdDataContractExporter(XmlSchemaSet? schemas) { - this._schemas = schemas; + _schemas = schemas; } + /// + /// Gets or sets an that contains options that can be set for the export operation. + /// public ExportOptions? Options { get { return _options; } set { _options = value; } } + /// + /// Gets the collection of exported XML schemas. + /// public XmlSchemaSet Schemas { get { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_SchemaImporter); + XmlSchemaSet schemaSet = GetSchemaSet(); + SchemaImporter.CompileSchemaSet(schemaSet); + return schemaSet; } } @@ -50,17 +81,28 @@ private XmlSchemaSet GetSchemaSet() return _schemas; } - private static DataContractSet DataContractSet + private DataContractSet DataContractSet { get { - // On .NET Framework , we set _dataContractSet = Options.GetSurrogate()); - // But Options.GetSurrogate() is not available on NetCore because IDataContractSurrogate - // is not in NetStandard. - throw new PlatformNotSupportedException(SR.PlatformNotSupported_IDataContractSurrogate); + if (_dataContractSet == null) + { + _dataContractSet = new DataContractSet(Options?.DataContractSurrogate, null, null); + } + return _dataContractSet; } } + private static void EnsureTypeNotGeneric(Type type) + { + if (type.ContainsGenericParameters) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.GenericTypeNotExportable, type))); + } + + /// + /// Transforms the types contained in the specified collection of assemblies. + /// + /// A (of ) that contains the types to export. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void Export(ICollection assemblies) { @@ -81,13 +123,20 @@ public void Export(ICollection assemblies) Export(); } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Transforms the types contained in the passed to this method. + /// + /// A (of ) that contains the types to export. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void Export(ICollection types) { @@ -105,13 +154,20 @@ public void Export(ICollection types) Export(); } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Transforms the specified .NET Framework type into an XML schema definition language (XSD) schema. + /// + /// The to transform into an XML schema. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public void Export(Type type) { @@ -123,13 +179,21 @@ public void Export(Type type) AddType(type); Export(); } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Returns the contract name and contract namespace for the . + /// + /// The that was exported. + /// An that represents the contract name of the type and its namespace. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public XmlQualifiedName GetSchemaTypeName(Type type) { @@ -137,13 +201,17 @@ public XmlQualifiedName GetSchemaTypeName(Type type) type = GetSurrogatedType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContractSet.EnsureTypeNotGeneric(dataContract.UnderlyingType); - XmlDataContract? xmlDataContract = dataContract as XmlDataContract; - if (xmlDataContract != null && xmlDataContract.IsAnonymous) + EnsureTypeNotGeneric(dataContract.UnderlyingType); + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) return XmlQualifiedName.Empty; - return dataContract.StableName; + return dataContract.XmlName; } + /// + /// Returns the XML schema type for the specified type. + /// + /// The type to return a schema for. + /// An that contains the XML schema. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public XmlSchemaType? GetSchemaType(Type type) { @@ -151,13 +219,17 @@ public XmlQualifiedName GetSchemaTypeName(Type type) type = GetSurrogatedType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContractSet.EnsureTypeNotGeneric(dataContract.UnderlyingType); - XmlDataContract? xmlDataContract = dataContract as XmlDataContract; - if (xmlDataContract != null && xmlDataContract.IsAnonymous) + EnsureTypeNotGeneric(dataContract.UnderlyingType); + if (dataContract is XmlDataContract xmlDataContract && xmlDataContract.IsAnonymous) return xmlDataContract.XsdType; return null; } + /// + /// Returns the top-level name and namespace for the . + /// + /// The to query. + /// The that represents the top-level name and namespace for this Type, which is written to the stream when writing this object. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public XmlQualifiedName? GetRootElementName(Type type) { @@ -165,8 +237,8 @@ public XmlQualifiedName GetSchemaTypeName(Type type) type = GetSurrogatedType(type); DataContract dataContract = DataContract.GetDataContract(type); - DataContractSet.EnsureTypeNotGeneric(dataContract.UnderlyingType); - if (dataContract.HasRoot) + EnsureTypeNotGeneric(dataContract.UnderlyingType); + if (dataContract is not XmlDataContract xdc || xdc.HasRoot) // All non-XmlDataContracts "have root". { return new XmlQualifiedName(dataContract.TopLevelElementName!.Value, dataContract.TopLevelElementNamespace!.Value); } @@ -176,18 +248,17 @@ public XmlQualifiedName GetSchemaTypeName(Type type) } } - private static Type GetSurrogatedType(Type type) + [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] + private Type GetSurrogatedType(Type type) { -#if SUPPORT_SURROGATE - IDataContractSurrogate dataContractSurrogate; - if (options != null && (dataContractSurrogate = Options.GetSurrogate()) != null) - type = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, type); -#endif + ISerializationSurrogateProvider? surrogate = Options?.DataContractSurrogate; + if (surrogate != null) + type = DataContractSurrogateCaller.GetDataContractType(surrogate, type); return type; } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static void CheckAndAddType(Type type) + private void CheckAndAddType(Type type) { type = GetSurrogatedType(type); if (!type.ContainsGenericParameters && DataContract.IsTypeSerializable(type)) @@ -195,7 +266,7 @@ private static void CheckAndAddType(Type type) } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] - private static void AddType(Type type) + private void AddType(Type type) { DataContractSet.Add(type); } @@ -204,8 +275,8 @@ private static void AddType(Type type) private void Export() { AddKnownTypes(); - SchemaExporter schemaExporter = new SchemaExporter(GetSchemaSet(), DataContractSet); - schemaExporter.Export(); + SchemaExporter exporter = new SchemaExporter(GetSchemaSet(), DataContractSet); + exporter.Export(); } [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] @@ -228,6 +299,11 @@ private void AddKnownTypes() } } + /// + /// Gets a value that indicates whether the set of runtime types contained in a set of assemblies can be exported. + /// + /// A of that contains the assemblies with the types to export. + /// true if the types can be exported; otherwise, false. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public bool CanExport(ICollection assemblies) { @@ -253,13 +329,21 @@ public bool CanExport(ICollection assemblies) _dataContractSet = oldValue; return false; } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Gets a value that indicates whether the set of runtime types contained in a can be exported. + /// + /// A that contains the specified types to export. + /// true if the types can be exported; otherwise, false. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public bool CanExport(ICollection types) { @@ -282,13 +366,21 @@ public bool CanExport(ICollection types) _dataContractSet = oldValue; return false; } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } } + /// + /// Gets a value that indicates whether the specified runtime type can be exported. + /// + /// The to export. + /// true if the type can be exported; otherwise, false. [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)] public bool CanExport(Type type) { @@ -306,8 +398,11 @@ public bool CanExport(Type type) _dataContractSet = oldValue; return false; } - catch + catch (Exception ex) { + if (Fx.IsFatal(ex)) + throw; + _dataContractSet = oldValue; throw; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs index 3baa246fd2a4e..b343623355ec3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs @@ -17,6 +17,9 @@ public TArray[] ReadArray(XmlDictionaryReader reader, TArgument localName, TArgu int count; if (reader.TryGetArrayLength(out count)) { + if (count > maxArrayLength) + XmlExceptionHelper.ThrowMaxArrayLengthOrMaxItemsQuotaExceeded(reader, maxArrayLength); + if (count > XmlDictionaryReader.MaxInitialArrayLength) count = XmlDictionaryReader.MaxInitialArrayLength; } @@ -35,6 +38,8 @@ public TArray[] ReadArray(XmlDictionaryReader reader, TArgument localName, TArgu break; read += actual; } + if (totalRead > maxArrayLength - read) + XmlExceptionHelper.ThrowMaxArrayLengthOrMaxItemsQuotaExceeded(reader, maxArrayLength); totalRead += read; if (read < array.Length || reader.NodeType == XmlNodeType.EndElement) break; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs index 0d9437d5443d0..9b88c45d8561d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs @@ -556,15 +556,15 @@ public override long Position } } - protected override void Dispose(bool disposing) + public override void Close() { if (_stream.CanWrite) { Flush(); } + base.Close(); _stream.Dispose(); - base.Dispose(disposing); } public override void Flush() @@ -726,8 +726,4 @@ public override void SetLength(long value) throw new NotSupportedException(); } } - - // Add format exceptions - // Do we need to modify the stream position/Seek to account for the buffer? - // ASSUMPTION (Microsoft): This class will only be used for EITHER reading OR writing. } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs index eacd50236de1e..50dce6014220d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs @@ -111,9 +111,11 @@ public string GetString(XmlNameTable nameTable) return _bufferReader.GetString(_offset, _length, nameTable); if (type == StringHandleType.Dictionary) return nameTable.Add(_bufferReader.GetDictionaryString(_key).Value); - DiagnosticUtility.DebugAssert(type == StringHandleType.ConstString, "Should be ConstString"); - //If not Utf8 then the StringHandleType is ConstString - return nameTable.Add(s_constStrings[_key]); + if (type == StringHandleType.ConstString) + return nameTable.Add(s_constStrings[_key]); + // If none of the above, must be StringHandleType.EscapedUTF8 + DiagnosticUtility.DebugAssert(type == StringHandleType.EscapedUTF8, "Should be EscapedUTF8"); + return _bufferReader.GetEscapedString(_offset, _length, nameTable); } public string GetString() @@ -123,9 +125,11 @@ public string GetString() return _bufferReader.GetString(_offset, _length); if (type == StringHandleType.Dictionary) return _bufferReader.GetDictionaryString(_key).Value; - DiagnosticUtility.DebugAssert(type == StringHandleType.ConstString, "Should be ConstString"); - //If not Utf8 then the StringHandleType is ConstString - return s_constStrings[_key]; + if (type == StringHandleType.ConstString) + return s_constStrings[_key]; + // If none of the above, must be StringHandleType.EscapedUTF8 + DiagnosticUtility.DebugAssert(type == StringHandleType.EscapedUTF8, "Should be EscapedUTF8"); + return _bufferReader.GetEscapedString(_offset, _length); } public byte[] GetString(out int offset, out int length) @@ -177,6 +181,7 @@ public bool TryGetDictionaryString([NotNullWhen(true)] out XmlDictionaryString? value = null; return false; } + public override string ToString() { return GetString(); @@ -211,7 +216,7 @@ private bool Equals2(string s2) return _bufferReader.GetDictionaryString(_key).Value == s2; if (type == StringHandleType.UTF8) return _bufferReader.Equals2(_offset, _length, s2); - DiagnosticUtility.DebugAssert(type == StringHandleType.ConstString, ""); + DiagnosticUtility.DebugAssert(type == StringHandleType.EscapedUTF8 || type == StringHandleType.ConstString, ""); return GetString() == s2; } @@ -248,6 +253,7 @@ public bool Equals([NotNullWhen(true)] StringHandle? other) { return !s1.Equals2(xmlString2); } + public static bool operator ==(StringHandle s1, string s2) { return s1.Equals2(s2); @@ -275,6 +281,7 @@ public int CompareTo(StringHandle that) else return string.Compare(this.GetString(), that.GetString(), StringComparison.Ordinal); } + public override bool Equals([NotNullWhen(true)] object? obj) { return Equals(obj as StringHandle); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs index f09a06baa1572..9a1195317eb10 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs @@ -114,6 +114,7 @@ public void SetQNameValue(int prefix, int key) { SetValue(ValueHandleType.QName, key, prefix); } + public void SetValue(ValueHandleType type, int offset, int length) { _type = type; @@ -414,6 +415,7 @@ public Guid ToGuid() return XmlConverter.ToGuid(_bufferReader.Buffer, _offset, _length); return XmlConverter.ToGuid(GetString()); } + public override string ToString() { return GetString(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs index 158e62374292d..d8a3f8f6291f6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs @@ -127,6 +127,7 @@ protected void MoveToInitial(XmlDictionaryReaderQuotas quotas) _attributeIndex = -1; _rootElement = false; _readingElement = false; + _signing = false; MoveToNode(s_initialNode); } @@ -191,6 +192,7 @@ private bool CheckDeclAttribute(int index, string localName, string? value, bool return true; } + protected XmlCommentNode MoveToComment() { _commentNode ??= new XmlCommentNode(_bufferReader); @@ -229,6 +231,7 @@ protected XmlTextNode MoveToWhitespaceText() MoveToNode(_whitespaceTextNode); return _whitespaceTextNode; } + protected XmlElementNode ElementNode { get @@ -341,6 +344,7 @@ protected XmlAttributeNode AddXmlAttribute() { return AddAttribute(QNameType.Normal, true); } + protected XmlAttributeNode AddXmlnsAttribute(Namespace ns) { if (!ns.Prefix.IsEmpty && ns.Uri.IsEmpty) @@ -385,6 +389,7 @@ protected void FixXmlAttribute(XmlAttributeNode attributeNode) } } } + protected bool OutsideRootElement { get @@ -392,6 +397,7 @@ protected bool OutsideRootElement return _depth == 0; } } + public override bool CanReadBinaryContent { get { return true; } @@ -610,7 +616,6 @@ private XmlAttributeNode GetAttributeNode(int index) return null; } - public override string GetAttribute(int index) { return GetAttributeNode(index).ValueAsString; @@ -639,6 +644,7 @@ public override string GetAttribute(int index) return null; return attributeNode.ValueAsString; } + public sealed override bool IsEmptyElement { get @@ -781,17 +787,19 @@ private void CheckAttributes(XmlAttributeNode[] attributeNodes, int attributeCou } } - public override void MoveToAttribute(int index) { MoveToNode(GetAttributeNode(index)); + _attributeIndex = index; } + public override bool MoveToAttribute(string name) { XmlNode? attributeNode = GetAttributeNode(name); if (attributeNode == null) return false; MoveToNode(attributeNode); + _attributeIndex = _attributeStart; return true; } @@ -801,6 +809,7 @@ public override bool MoveToAttribute(string localName, string? namespaceUri) if (attributeNode == null) return false; MoveToNode(attributeNode); + _attributeIndex = _attributeStart; return true; } @@ -902,7 +911,7 @@ public override XmlNameTable NameTable { if (_nameTable == null) { - _nameTable = new NameTable(); + _nameTable = new QuotaNameTable(this, _quotas.MaxNameTableCharCount); _nameTable.Add(xml); _nameTable.Add(xmlns); _nameTable.Add(xmlnsNamespace); @@ -953,6 +962,13 @@ public override string Prefix } } + public override char QuoteChar + { + get + { + return _node.QuoteChar; + } + } public override bool IsLocalName(string localName) { @@ -1192,6 +1208,36 @@ public override string ReadElementContentAsString() return s; } } + + public override string ReadElementString() + { + MoveToStartElement(); + if (IsEmptyElement) + { + Read(); + return string.Empty; + } + else + { + Read(); + string s = ReadString(); + ReadEndElement(); + return s; + } + } + + public override string ReadElementString(string name) + { + MoveToStartElement(name); + return ReadElementString(); + } + + public override string ReadElementString(string localName, string namespaceUri) + { + MoveToStartElement(localName, namespaceUri); + return ReadElementString(); + } + public override void ReadStartElement() { if (_node.NodeType != XmlNodeType.Element) @@ -2065,7 +2111,6 @@ protected XmlNode(XmlNodeType nodeType, _depthDelta = depthDelta; _isEmptyElement = false; _quoteChar = '"'; - _qnameType = QNameType.Normal; } @@ -2249,6 +2294,7 @@ public bool IsLocalNameAndNamespaceUri(XmlDictionaryString localName, XmlDiction return this.Namespace.Prefix == localName && ns.Value == xmlnsNamespace; } } + public bool IsPrefixAndLocalName(string prefix, string localName) { if (_qnameType == QNameType.Normal) @@ -2539,6 +2585,7 @@ public XmlCommentNode(XmlBufferReader bufferReader) { } } + protected sealed class XmlEndOfFileNode : XmlNode { public XmlEndOfFileNode(XmlBufferReader bufferReader) @@ -2911,6 +2958,7 @@ public Namespace AddNamespace() return XmlNamespace; return null; } + public Namespace? LookupNamespace(string prefix) { PrefixHandleType shortPrefix; @@ -3060,6 +3108,7 @@ public bool IsUri(XmlDictionaryString s) } return false; } + public StringHandle Uri { get @@ -3080,5 +3129,56 @@ public Namespace? OuterUri } } } + + private sealed class QuotaNameTable : XmlNameTable + { + private readonly XmlDictionaryReader _reader; + private readonly XmlNameTable _nameTable; + private readonly int _maxCharCount; + private int _charCount; + + public QuotaNameTable(XmlDictionaryReader reader, int maxCharCount) + { + _reader = reader; + _nameTable = new NameTable(); + _maxCharCount = maxCharCount; + _charCount = 0; + } + + public override string? Get(char[] chars, int offset, int count) + { + return _nameTable.Get(chars, offset, count); + } + + public override string? Get(string value) + { + return _nameTable.Get(value); + } + + private void Add(int charCount) + { + if (charCount > _maxCharCount - _charCount) + XmlExceptionHelper.ThrowMaxNameTableCharCountExceeded(_reader, _maxCharCount); + _charCount += charCount; + } + + public override string Add(char[] chars, int offset, int count) + { + string? s = _nameTable.Get(chars, offset, count); + if (s != null) + return s; + Add(count); + return _nameTable.Add(chars, offset, count); + } + + public override string Add(string value) + { + string? s = _nameTable.Get(value); + if (s != null) + return s; + Add(value.Length); + return _nameTable.Add(value); + } + } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs index 4ddcff94559aa..d197af209fd8e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs @@ -13,7 +13,7 @@ namespace System.Xml { - internal abstract class XmlBaseWriter : XmlDictionaryWriter + internal abstract class XmlBaseWriter : XmlDictionaryWriter, IFragmentCapableXmlDictionaryWriter { private XmlNodeWriter _writer = null!; // initialized in SetOutput private readonly NamespaceManager _nsMgr; @@ -29,6 +29,10 @@ internal abstract class XmlBaseWriter : XmlDictionaryWriter private int _trailByteCount; private XmlStreamNodeWriter _nodeWriter = null!; // initialized in SetOutput private XmlSigningNodeWriter? _signingWriter; + private XmlUTF8NodeWriter? _textFragmentWriter; + private XmlNodeWriter? _oldWriter; + private Stream? _oldStream; + private int _oldNamespaceBoundary; private bool _inList; private const string xmlnsNamespace = "http://www.w3.org/2000/xmlns/"; private const string xmlNamespace = "http://www.w3.org/XML/1998/namespace"; @@ -57,6 +61,8 @@ protected void SetOutput(XmlStreamNodeWriter writer) } _attributeLocalName = null; _attributeValue = null; + _oldWriter = null; + _oldStream = null; } public override void Flush() @@ -98,6 +104,9 @@ public override void Close() _attributeLocalName = null; _nodeWriter.Close(); _signingWriter?.Close(); + _textFragmentWriter?.Close(); + _oldWriter = null; + _oldStream = null; } } @@ -431,7 +440,7 @@ public override void WriteComment(string? text) { text = string.Empty; } - else if (text.Contains("--") || text.StartsWith('-')) + else if (text.Contains("--") || text.EndsWith('-')) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.XmlInvalidCommentChars, nameof(text))); } @@ -926,6 +935,18 @@ public override void WriteEndDocument() _documentState = DocumentState.End; } + protected int NamespaceBoundary + { + get + { + return _nsMgr.NamespaceBoundary; + } + set + { + _nsMgr.NamespaceBoundary = value; + } + } + public override void WriteEntityRef(string name) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.XmlMethodNotSupported, "WriteEntityRef"))); @@ -1149,6 +1170,10 @@ public override void WriteValue(object value) { WriteValue((Array)value); } + else if (value is IStreamProvider) + { + WriteValue((IStreamProvider)value); + } else { WritePrimitiveValue(value); @@ -1426,11 +1451,27 @@ public override void WriteValue(TimeSpan value) } } + private static void EnsureBufferBounds(byte[] buffer, int offset, int count) + { + ArgumentNullException.ThrowIfNull(buffer); + + // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. + if (offset < 0) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), SR.ValueMustBeNonNegative)); + + if (count < 0) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); + if (count > buffer.Length - offset) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); + } + public override void WriteBinHex(byte[] buffer, int offset, int count) { if (IsClosed) ThrowClosed(); + EnsureBufferBounds(buffer, offset, count); + WriteRaw(BinHexEncoding.GetString(buffer, offset, count)); } @@ -1439,16 +1480,7 @@ public override void WriteBase64(byte[] buffer, int offset, int count) if (IsClosed) ThrowClosed(); - ArgumentNullException.ThrowIfNull(buffer); - - // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. - if (offset < 0) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), SR.ValueMustBeNonNegative)); - - if (count < 0) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); - if (count > buffer.Length - offset) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); + EnsureBufferBounds(buffer, offset, count); if (count > 0) { @@ -1473,6 +1505,7 @@ public override void WriteBase64(byte[] buffer, int offset, int count) WriteAttributeText(XmlConverter.Base64Encoding.GetString(_trailBytes, 0, _trailByteCount)); WriteAttributeText(XmlConverter.Base64Encoding.GetString(buffer, offset, actualByteCount - _trailByteCount)); } + if (!_isXmlnsAttribute) { StartContent(); @@ -1480,6 +1513,7 @@ public override void WriteBase64(byte[] buffer, int offset, int count) EndContent(); } _trailByteCount = (totalByteCount - actualByteCount); + if (_trailByteCount > 0) { int trailOffset = offset + count - _trailByteCount; @@ -1500,16 +1534,7 @@ public override Task WriteBase64Async(byte[] buffer, int offset, int count) if (IsClosed) ThrowClosed(); - ArgumentNullException.ThrowIfNull(buffer); - - // Not checking upper bound because it will be caught by "count". This is what XmlTextWriter does. - if (offset < 0) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), SR.ValueMustBeNonNegative)); - - if (count < 0) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); - if (count > buffer.Length - offset) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); + EnsureBufferBounds(buffer, offset, count); return WriteBase64AsyncImpl(buffer, offset, count); } @@ -1603,6 +1628,125 @@ public override void EndCanonicalization() protected abstract XmlSigningNodeWriter CreateSigningNodeWriter(); + public virtual bool CanFragment + { + get + { + return true; + } + } + + public void StartFragment(Stream stream, bool generateSelfContainedTextFragment) + { + if (!CanFragment) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); + + if (IsClosed) + ThrowClosed(); + + ArgumentNullException.ThrowIfNull(stream); + + if (_oldStream != null || _oldWriter != null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException()); + if (WriteState == WriteState.Attribute) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.XmlInvalidWriteState, "StartFragment", WriteState.ToString()))); + + FlushElement(); + _writer.Flush(); + + _oldNamespaceBoundary = NamespaceBoundary; + + XmlStreamNodeWriter? fragmentWriter = null; + if (generateSelfContainedTextFragment) + { + this.NamespaceBoundary = _depth + 1; + _textFragmentWriter ??= new XmlUTF8NodeWriter(); + _textFragmentWriter.SetOutput(stream, false, Encoding.UTF8); + fragmentWriter = _textFragmentWriter; + } + + if (Signing) + { + if (fragmentWriter != null) + { + _oldWriter = _signingWriter!.NodeWriter; + _signingWriter.NodeWriter = fragmentWriter; + } + else + { + _oldStream = ((XmlStreamNodeWriter)_signingWriter!.NodeWriter).OutputStream; + ((XmlStreamNodeWriter)_signingWriter.NodeWriter).OutputStream = stream; + } + } + else + { + if (fragmentWriter != null) + { + _oldWriter = _writer; + _writer = fragmentWriter; + } + else + { + _oldStream = _nodeWriter.OutputStream; + _nodeWriter.OutputStream = stream; + } + } + } + + public void EndFragment() + { + if (IsClosed) + ThrowClosed(); + + if (_oldStream == null && _oldWriter == null) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException()); + if (WriteState == WriteState.Attribute) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.XmlInvalidWriteState, "EndFragment", WriteState.ToString()))); + + FlushElement(); + _writer.Flush(); + + if (Signing) + { + if (_oldWriter != null) + _signingWriter!.NodeWriter = _oldWriter; + else + ((XmlStreamNodeWriter)_signingWriter!.NodeWriter).OutputStream = _oldStream!; // Checked above. _oldStream can't be null if _oldWriter is. + } + else + { + if (_oldWriter != null) + _writer = _oldWriter; + else + _nodeWriter.OutputStream = _oldStream!; // Checked above. _oldStream can't be null if _oldWriter is. + } + NamespaceBoundary = _oldNamespaceBoundary; + _oldWriter = null; + _oldStream = null; + } + + public void WriteFragment(byte[] buffer, int offset, int count) + { + if (!CanFragment) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); + + if (IsClosed) + ThrowClosed(); + + EnsureBufferBounds(buffer, offset, count); + + if (WriteState == WriteState.Attribute) + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.XmlInvalidWriteState, "WriteFragment", WriteState.ToString()))); + + if (_writer != _nodeWriter) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException()); + + FlushElement(); + FlushBase64(); + _nodeWriter.Flush(); + _nodeWriter.OutputStream.Write(buffer, offset, count); + } + private void FlushBase64() { if (_trailByteCount > 0) @@ -1802,6 +1946,7 @@ private sealed class NamespaceManager private int _attributeCount; private XmlSpace _space; private string? _lang; + private int _namespaceBoundary; private int _nsTop; private readonly Namespace _defaultNamespace; @@ -1843,9 +1988,29 @@ public void Clear() _attributeCount = 0; _space = XmlSpace.None; _lang = null; + _namespaceBoundary = 0; _lastNameSpace = null; } + public int NamespaceBoundary + { + get + { + return _namespaceBoundary; + } + set + { + int i; + for (i = 0; i < _nsCount; i++) + if (_namespaces![i].Depth >= value) + break; + + _nsTop = i; + _namespaceBoundary = value; + _lastNameSpace = null; + } + } + public void Close() { if (_depth == 0) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs index 9b6911441951b..07765d45883b7 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs @@ -29,6 +29,7 @@ internal sealed class XmlBinaryReader : XmlBaseReader, IXmlBinaryReaderInitializ private int _arrayCount; private int _maxBytesPerRead; private XmlBinaryNodeType _arrayNodeType; + private OnXmlDictionaryReaderClose? _onClose; public XmlBinaryReader() { @@ -50,7 +51,7 @@ public void SetInput(byte[] buffer, int offset, int count, throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.ValueMustBeNonNegative)); if (count > buffer.Length - offset) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset))); - MoveToInitial(quotas, session, null); + MoveToInitial(quotas, session, onClose); BufferReader.SetBuffer(buffer, offset, count, dictionary, session); _buffered = true; } @@ -63,7 +64,7 @@ public void SetInput(Stream stream, { ArgumentNullException.ThrowIfNull(stream); - MoveToInitial(quotas, session, null); + MoveToInitial(quotas, session, onClose); BufferReader.SetBuffer(stream, dictionary, session); _buffered = false; } @@ -73,12 +74,26 @@ private void MoveToInitial(XmlDictionaryReaderQuotas quotas, XmlBinaryReaderSess MoveToInitial(quotas); _maxBytesPerRead = quotas.MaxBytesPerRead; _arrayState = ArrayState.None; + _onClose = onClose; _isTextWithEndElement = false; } public override void Close() { base.Close(); + OnXmlDictionaryReaderClose? onClose = _onClose; + _onClose = null; + if (onClose != null) + { + try + { + onClose(this); + } + catch (Exception e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } } public override string ReadElementContentAsString() @@ -710,8 +725,10 @@ private void ReadAttributes() private void ReadAttributes2() { + int startOffset = 0; + if (_buffered) - _ = BufferReader.Offset; + startOffset = BufferReader.Offset; while (true) { @@ -845,6 +862,8 @@ private void ReadAttributes2() ReadAttributeText(attributeNode.AttributeText!); break; default: + if (_buffered && (BufferReader.Offset - startOffset) > _maxBytesPerRead) + XmlExceptionHelper.ThrowMaxBytesPerReadExceeded(this, _maxBytesPerRead); ProcessAttributes(); return; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs index ef401fd803182..be47e110a151b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs @@ -807,28 +807,8 @@ public override unsafe void WriteDecimalText(decimal d) public override void WriteDateTimeText(DateTime dt) { - WriteTextNodeWithInt64(XmlBinaryNodeType.DateTimeText, ToBinary(dt)); + WriteTextNodeWithInt64(XmlBinaryNodeType.DateTimeText, dt.ToBinary()); } - private static long ToBinary(DateTime dt) - { - long temp = 0; - switch (dt.Kind) - { - case DateTimeKind.Local: - temp |= -9223372036854775808L; // 0x8000000000000000 - temp |= dt.ToUniversalTime().Ticks; - break; - case DateTimeKind.Utc: - temp |= 0x4000000000000000L; - temp |= dt.Ticks; - break; - case DateTimeKind.Unspecified: - temp = dt.Ticks; - break; - } - return temp; - } - public override void WriteUniqueIdText(UniqueId value) { @@ -906,7 +886,7 @@ public void WriteDateTimeArray(DateTime[] array, int offset, int count) WriteArrayInfo(XmlBinaryNodeType.DateTimeTextWithEndElement, count); for (int i = 0; i < count; i++) { - WriteInt64(ToBinary(array[offset + i])); + WriteInt64(array[offset + i].ToBinary()); } } @@ -1185,7 +1165,6 @@ private unsafe void UnsafeWriteArray(string? prefix, string localName, string? n { WriteStartArray(prefix, localName, namespaceUri, count); _writer.UnsafeWriteArray(nodeType, count, array, arrayMax); - // WriteEndArray(); } private unsafe void UnsafeWriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, @@ -1193,7 +1172,6 @@ private unsafe void UnsafeWriteArray(string? prefix, XmlDictionaryString localNa { WriteStartArray(prefix, localName, namespaceUri, count); _writer.UnsafeWriteArray(nodeType, count, array, arrayMax); - // WriteEndArray(); } private static void CheckArray(Array array, int offset, int count) @@ -1212,6 +1190,11 @@ private static void CheckArray(Array array, int offset, int count) public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, bool[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1226,6 +1209,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, bool[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1240,6 +1228,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, short[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1254,6 +1247,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, short[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1268,6 +1266,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, int[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1282,6 +1285,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, int[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1296,6 +1304,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, long[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1310,6 +1323,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, long[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1324,6 +1342,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, float[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1338,6 +1361,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, float[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1352,6 +1380,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, double[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1366,6 +1399,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, double[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1380,6 +1418,11 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, decimal[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1394,6 +1437,11 @@ public override unsafe void WriteArray(string? prefix, string localName, string? public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, decimal[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) @@ -1409,26 +1457,34 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local // DateTime public override void WriteArray(string? prefix, string localName, string? namespaceUri, DateTime[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteDateTimeArray(array, offset, count); - // WriteEndArray(); } } } public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, DateTime[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteDateTimeArray(array, offset, count); - // WriteEndArray(); } } } @@ -1436,26 +1492,34 @@ public override void WriteArray(string? prefix, XmlDictionaryString localName, X // Guid public override void WriteArray(string? prefix, string localName, string? namespaceUri, Guid[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteGuidArray(array, offset, count); - // WriteEndArray(); } } } public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, Guid[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteGuidArray(array, offset, count); - // WriteEndArray(); } } } @@ -1463,26 +1527,34 @@ public override void WriteArray(string? prefix, XmlDictionaryString localName, X // TimeSpan public override void WriteArray(string? prefix, string localName, string? namespaceUri, TimeSpan[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteTimeSpanArray(array, offset, count); - // WriteEndArray(); } } } public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, TimeSpan[] array, int offset, int count) { + if (Signing) + { + base.WriteArray(prefix, localName, namespaceUri, array, offset, count); + } + else { CheckArray(array, offset, count); if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); _writer.WriteTimeSpanArray(array, offset, count); - // WriteEndArray(); } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs index fab4fb8531c1c..2ad5b6cdcabc9 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs @@ -12,6 +12,7 @@ using System.Runtime.Serialization; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Buffers.Binary; namespace System.Xml { @@ -210,13 +211,14 @@ private bool TryEnsureBytes(int count) if (_stream == null) return false; + DiagnosticUtility.DebugAssert(_offset <= int.MaxValue - count, ""); + // The data could be coming from an untrusted source, so we use a standard // "multiply by 2" growth algorithm to avoid overly large memory utilization. // Constant value of 256 comes from MemoryStream implementation. do { - DiagnosticUtility.DebugAssert(_offset <= int.MaxValue - count, ""); int newOffsetMax = _offset + count; if (newOffsetMax <= _offsetMax) return true; @@ -427,16 +429,19 @@ public unsafe double ReadDouble() return value; } - public unsafe decimal ReadDecimal() + public decimal ReadDecimal() { - int offset; - byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out offset); - decimal value; - byte* pb = (byte*)&value; - for (int i = 0; i < sizeof(decimal); i++) - pb[i] = buffer[offset + i]; - Advance(ValueHandleLength.Decimal); - return value; + byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out int offset); + ReadOnlySpan bytes = buffer.AsSpan(offset, sizeof(decimal)); + ReadOnlySpan span = stackalloc int[4] + { + BinaryPrimitives.ReadInt32LittleEndian(bytes), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12)) + }; + + return new decimal(span); } public UniqueId ReadUniqueId() @@ -680,6 +685,13 @@ public string GetEscapedString(int offset, int length) return new string(chars, 0, charCount); } + public string GetEscapedString(int offset, int length, XmlNameTable nameTable) + { + char[] chars = GetCharBuffer(length); + int charCount = GetEscapedChars(offset, length, chars); + return nameTable.Add(chars, 0, charCount); + } + private int GetLessThanCharEntity(int offset, int length) { byte[] buffer = _buffer; @@ -1059,14 +1071,18 @@ public unsafe double GetDouble(int offset) return value; } - public unsafe decimal GetDecimal(int offset) + public decimal GetDecimal(int offset) { - byte[] buffer = _buffer; - decimal value; - byte* pb = (byte*)&value; - for (int i = 0; i < sizeof(decimal); i++) - pb[i] = buffer[offset + i]; - return value; + ReadOnlySpan bytes = _buffer.AsSpan(offset, sizeof(decimal)); + ReadOnlySpan span = stackalloc int[4] + { + BinaryPrimitives.ReadInt32LittleEndian(bytes), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12)) + }; + + return new decimal(span); } public UniqueId GetUniqueId(int offset) @@ -1132,9 +1148,11 @@ public XmlDictionaryString GetDictionaryString(int key) { keyDictionary = _dictionary!; } + XmlDictionaryString? s; if (!keyDictionary.TryLookup(key >> 1, out s)) XmlExceptionHelper.ThrowInvalidBinaryFormat(_reader); + return s; } @@ -1165,6 +1183,7 @@ public int ReadDictionaryKey() XmlExceptionHelper.ThrowXmlDictionaryStringIDUndefinedStatic(_reader, staticKey); } } + return key; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs index 951bd780443cb..4b94ed46ce2d7 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs @@ -127,7 +127,8 @@ public void Close() _inclusivePrefixes = null; } - public static void WriteDeclaration() + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "This class is should roughly mirror the XmlNodeWriter API where this is an instance method.")] + public void WriteDeclaration() { } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs index e50ec3865375e..0a856e04e7f1c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlConverter.cs @@ -247,7 +247,7 @@ public static UniqueId ToUniqueId(string value) { try { - return new UniqueId(Trim(value)); + return new UniqueId(value.Trim()); } catch (ArgumentException exception) { @@ -293,7 +293,7 @@ public static Guid ToGuid(string value) { try { - return new Guid(Trim(value)); + return new Guid(value.Trim()); } catch (FormatException exception) { @@ -318,7 +318,7 @@ public static ulong ToUInt64(string value) { try { - return ulong.Parse(value, NumberStyles.Any, NumberFormatInfo.InvariantInfo); + return ulong.Parse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo); } catch (ArgumentException exception) { @@ -395,7 +395,6 @@ public static int ToChars(byte[] buffer, int offset, int count, char[] chars, in public static string ToString(double value) { return XmlConvert.ToString(value); } public static string ToString(decimal value) { return XmlConvert.ToString(value); } public static string ToString(TimeSpan value) { return XmlConvert.ToString(value); } - public static string ToString(UniqueId value) { return value.ToString(); } public static string ToString(Guid value) { return value.ToString(); } public static string ToString(ulong value) { return value.ToString(NumberFormatInfo.InvariantInfo); } @@ -459,14 +458,14 @@ public static void ToQualifiedName(string qname, out string prefix, out string l if (index < 0) { prefix = string.Empty; - localName = Trim(qname); + localName = qname.Trim(); } else { if (index == qname.Length - 1) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.XmlInvalidQualifiedName, qname))); - prefix = Trim(qname.Substring(0, index)); - localName = Trim(qname.Substring(index + 1)); + prefix = qname.Substring(0, index).Trim(); + localName = qname.Substring(index + 1).Trim(); } } @@ -795,7 +794,7 @@ private static int ToAsciiChars(string s, byte[] buffer, int offset) { for (int i = 0; i < s.Length; i++) { - Fx.Assert(s[i] < 128, ""); + DiagnosticUtility.DebugAssert(s[i] < 128, ""); buffer[offset++] = (byte)s[i]; } return s.Length; @@ -1135,21 +1134,5 @@ public static string StripWhitespace(string s) } }); } - - private static string Trim(string s) - { - int i; - for (i = 0; i < s.Length && IsWhitespace(s[i]); i++) - ; - int j; - for (j = s.Length; j > 0 && IsWhitespace(s[j - 1]); j--) - ; - if (i == 0 && j == s.Length) - return s; - else if (j == 0) - return string.Empty; - else - return s.Substring(i, j - i); - } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs index 92b5bb6c960d2..d065ef92957b4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs @@ -358,6 +358,9 @@ internal byte[] ReadContentAsBase64(int maxByteArrayContentLength, int maxInitia int length; if (TryGetBase64ContentLength(out length)) { + if (length > maxByteArrayContentLength) + XmlExceptionHelper.ThrowMaxArrayLengthExceeded(this, maxByteArrayContentLength); + if (length <= maxInitialCount) { byte[] buffer = new byte[length]; @@ -522,6 +525,8 @@ private byte[] ReadContentAsBytes(bool base64, int maxByteArrayContentLength) break; read += actual; } + if (totalRead > maxByteArrayContentLength - read) + XmlExceptionHelper.ThrowMaxArrayLengthExceeded(this, maxByteArrayContentLength); totalRead += read; if (read < buffer.Length) break; @@ -1497,6 +1502,13 @@ public override string Prefix } } + public override char QuoteChar + { + get + { + return _reader.QuoteChar; + } + } public override bool Read() { @@ -1508,6 +1520,15 @@ public override bool ReadAttributeValue() return _reader.ReadAttributeValue(); } + public override string ReadElementString(string name) + { + return _reader.ReadElementString(name); + } + + public override string ReadElementString(string localName, string namespaceUri) + { + return _reader.ReadElementString(localName, namespaceUri); + } public override string ReadInnerXml() { @@ -1534,6 +1555,11 @@ public override void ReadEndElement() _reader.ReadEndElement(); } + public override string ReadString() + { + return _reader.ReadString(); + } + public override ReadState ReadState { get @@ -1772,9 +1798,7 @@ public override object ReadContentAs(Type type, IXmlNamespaceResolver? namespace public bool HasLineInfo() { - IXmlLineInfo? lineInfo = _reader as IXmlLineInfo; - - if (lineInfo == null) + if (_reader is not IXmlLineInfo lineInfo) return false; return lineInfo.HasLineInfo(); @@ -1784,9 +1808,7 @@ public int LineNumber { get { - IXmlLineInfo? lineInfo = _reader as IXmlLineInfo; - - if (lineInfo == null) + if (_reader is not IXmlLineInfo lineInfo) return 1; return lineInfo.LineNumber; @@ -1797,9 +1819,7 @@ public int LinePosition { get { - IXmlLineInfo? lineInfo = _reader as IXmlLineInfo; - - if (lineInfo == null) + if (_reader is not IXmlLineInfo lineInfo) return 1; return lineInfo.LinePosition; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs index b39afc2bea8ab..938eefb42fd03 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs @@ -251,7 +251,7 @@ private void WriteElementNode(XmlDictionaryReader reader, bool defattr) { WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); } - if (defattr || !reader.IsDefault) + if (defattr || (!reader.IsDefault && (reader.SchemaInfo == null || !reader.SchemaInfo.IsDefault))) { if (reader.MoveToFirstAttribute()) { @@ -375,8 +375,7 @@ protected virtual void WriteTextNode(XmlDictionaryReader reader, bool isAttribut public override void WriteNode(XmlReader reader, bool defattr) { - XmlDictionaryReader? dictionaryReader = reader as XmlDictionaryReader; - if (dictionaryReader != null) + if (reader is XmlDictionaryReader dictionaryReader) WriteNode(dictionaryReader, defattr); else base.WriteNode(reader, defattr); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs index 3d6cc5cb60655..8e0a939970998 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs @@ -31,8 +31,7 @@ private static void ThrowXmlException(XmlDictionaryReader reader, string res, st private static void ThrowXmlException(XmlDictionaryReader reader, string res, string? arg1, string? arg2, string? arg3) { string s = SR.Format(res, arg1, arg2, arg3); - IXmlLineInfo? lineInfo = reader as IXmlLineInfo; - if (lineInfo != null && lineInfo.HasLineInfo()) + if (reader is IXmlLineInfo lineInfo && lineInfo.HasLineInfo()) { s += " " + SR.Format(SR.XmlLineInfo, lineInfo.LineNumber, lineInfo.LinePosition); } @@ -44,8 +43,7 @@ private static void ThrowXmlException(XmlDictionaryReader reader, string res, st public static void ThrowXmlException(XmlDictionaryReader reader, XmlException exception) { string s = exception.Message; - IXmlLineInfo? lineInfo = reader as IXmlLineInfo; - if (lineInfo != null && lineInfo.HasLineInfo()) + if (reader is IXmlLineInfo lineInfo && lineInfo.HasLineInfo()) { s += " " + SR.Format(SR.XmlLineInfo, lineInfo.LineNumber, lineInfo.LinePosition); } @@ -142,6 +140,12 @@ public static void ThrowMaxArrayLengthExceeded(XmlDictionaryReader reader, int m ThrowXmlException(reader, SR.XmlMaxArrayLengthExceeded, maxArrayLength.ToString(NumberFormatInfo.CurrentInfo)); } + [DoesNotReturn] + public static void ThrowMaxArrayLengthOrMaxItemsQuotaExceeded(XmlDictionaryReader reader, int maxQuota) + { + ThrowXmlException(reader, SR.XmlMaxArrayLengthOrMaxItemsQuotaExceeded, maxQuota.ToString(NumberFormatInfo.CurrentInfo)); + } + [DoesNotReturn] public static void ThrowMaxBytesPerReadExceeded(XmlDictionaryReader reader, int maxBytesPerRead) { @@ -160,6 +164,12 @@ public static void ThrowMaxStringContentLengthExceeded(XmlDictionaryReader reade ThrowXmlException(reader, SR.XmlMaxStringContentLengthExceeded, maxStringContentLength.ToString(NumberFormatInfo.CurrentInfo)); } + [DoesNotReturn] + public static void ThrowMaxNameTableCharCountExceeded(XmlDictionaryReader reader, int maxNameTableCharCount) + { + ThrowXmlException(reader, SR.XmlMaxNameTableCharCountExceeded, maxNameTableCharCount.ToString(NumberFormatInfo.CurrentInfo)); + } + [DoesNotReturn] public static void ThrowBase64DataExpected(XmlDictionaryReader reader) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs index 7fb89ae6c825d..9f24a0fc8f756 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs @@ -64,7 +64,7 @@ public override void Close() public override void WriteDeclaration() { _writer.WriteDeclaration(); - XmlCanonicalWriter.WriteDeclaration(); + _signingWriter.WriteDeclaration(); } public override void WriteComment(string text) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs index 6075d0a9c608e..f5c218aa755c8 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs @@ -10,7 +10,6 @@ namespace System.Xml { internal abstract class XmlStreamNodeWriter : XmlNodeWriter { - private Stream _stream = null!; // initialized by SetOutput private readonly byte[] _buffer; private int _offset; private bool _ownsStream; @@ -22,16 +21,22 @@ internal abstract class XmlStreamNodeWriter : XmlNodeWriter protected XmlStreamNodeWriter() { _buffer = new byte[bufferLength]; + OutputStream = null!; // Always initialized by SetOutput() } protected void SetOutput(Stream stream, bool ownsStream, Encoding? encoding) { - _stream = stream; + OutputStream = stream; _ownsStream = ownsStream; _offset = 0; - _encoding = encoding; + + if (encoding != null) + _encoding = encoding; } + // Getting/Setting the Stream exists for fragmenting + public Stream OutputStream { get; set; } + // StreamBuffer/BufferOffset exists only for the BinaryWriter to fix up nodes public byte[] StreamBuffer { @@ -52,19 +57,7 @@ public int Position { get { - return (int)_stream.Position + _offset; - } - } - - private int GetByteCount(char[] chars) - { - if (_encoding == null) - { - return s_UTF8Encoding.GetByteCount(chars); - } - else - { - return _encoding.GetByteCount(chars); + return (int)OutputStream.Position + _offset; } } @@ -225,7 +218,7 @@ public void WriteBytes(byte[] byteBuffer, int byteOffset, int byteCount) else { FlushBuffer(); - _stream.Write(byteBuffer, byteOffset, byteCount); + OutputStream.Write(byteBuffer, byteOffset, byteCount); } } @@ -237,14 +230,14 @@ protected unsafe void UnsafeWriteBytes(byte* bytes, int byteCount) { for (int i = 0; i < bufferLength; i++) buffer[i] = bytes[i]; - _stream.Write(buffer, 0, bufferLength); + OutputStream.Write(buffer, 0, bufferLength); bytes += bufferLength; byteCount -= bufferLength; } { for (int i = 0; i < byteCount; i++) buffer[i] = bytes[i]; - _stream.Write(buffer, 0, byteCount); + OutputStream.Write(buffer, 0, byteCount); } } @@ -282,7 +275,7 @@ protected void WriteUTF8Chars(byte[] chars, int charOffset, int charCount) else { FlushBuffer(); - _stream.Write(chars, charOffset, charCount); + OutputStream.Write(chars, charOffset, charCount); } } @@ -369,12 +362,7 @@ protected unsafe int UnsafeGetUTF8Length(char* chars, int charCount) if (chars == charsMax) return charCount; - char[] chArray = new char[charsMax - chars]; - for (int i = 0; i < chArray.Length; i++) - { - chArray[i] = chars[i]; - } - return (int)(chars - (charsMax - charCount)) + GetByteCount(chArray); + return (int)(chars - (charsMax - charCount)) + (_encoding ?? s_UTF8Encoding).GetByteCount(chars, (int)(charsMax - chars)); } protected unsafe int UnsafeGetUTF8Chars(char* chars, int charCount, byte[] buffer, int offset) @@ -425,7 +413,7 @@ protected virtual void FlushBuffer() { if (_offset != 0) { - _stream.Write(_buffer, 0, _offset); + OutputStream.Write(_buffer, 0, _offset); _offset = 0; } } @@ -434,7 +422,7 @@ protected virtual Task FlushBufferAsync() { if (_offset != 0) { - var task = _stream.WriteAsync(_buffer, 0, _offset); + var task = OutputStream.WriteAsync(_buffer, 0, _offset); _offset = 0; return task; } @@ -445,24 +433,24 @@ protected virtual Task FlushBufferAsync() public override void Flush() { FlushBuffer(); - _stream.Flush(); + OutputStream.Flush(); } public override async Task FlushAsync() { await FlushBufferAsync().ConfigureAwait(false); - await _stream.FlushAsync().ConfigureAwait(false); + await OutputStream.FlushAsync().ConfigureAwait(false); } public override void Close() { - if (_stream != null) + if (OutputStream != null) { if (_ownsStream) { - _stream.Dispose(); + OutputStream.Dispose(); } - _stream = null!; + OutputStream = null!; } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs index 2a1314d1a8792..16b9b3d61bd7f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs @@ -14,7 +14,7 @@ public interface IXmlTextWriterInitializer internal sealed class XmlUTF8TextWriter : XmlBaseWriter, IXmlTextWriterInitializer { - private XmlUTF8NodeWriter? _writer; + private XmlUTF8NodeWriter _writer = null!; // initialized in SetOutput public void SetOutput(Stream stream, Encoding encoding, bool ownsStream) { @@ -31,6 +31,15 @@ public void SetOutput(Stream stream, Encoding encoding, bool ownsStream) SetOutput(_writer); } + public override bool CanFragment + { + get + { + // Fragmenting only works for utf8 + return _writer.Encoding == null; + } + } + protected override XmlSigningNodeWriter CreateSigningNodeWriter() { return new XmlSigningNodeWriter(true); @@ -94,6 +103,14 @@ public XmlUTF8NodeWriter(bool[] isEscapedAttributeChar, bool[] isEscapedElementC _inAttribute = false; } + public Encoding? Encoding + { + get + { + return _encoding; + } + } + private byte[] GetCharEntityBuffer() => _entityChars ??= new byte[maxEntityLength]; private char[] GetCharBuffer(int charCount) @@ -686,7 +703,7 @@ public override void WriteUInt64Text(ulong value) { int offset; byte[] buffer = GetBuffer(XmlConverter.MaxUInt64Chars, out offset); - Advance(XmlConverter.ToChars((double)value, buffer, offset)); + Advance(XmlConverter.ToChars(value, buffer, offset)); } public override void WriteGuidText(Guid value) diff --git a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs index 014c498f6eace..668170766c98c 100644 --- a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs +++ b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs @@ -46,6 +46,8 @@ public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryStri public int MaxItemsInObjectGraph { get { throw null; } } public bool SerializeReadOnlyTypes { get { throw null; } } public bool UseSimpleDictionaryFormat { get { throw null; } } + public System.Runtime.Serialization.ISerializationSurrogateProvider? GetSerializationSurrogateProvider() { throw null; } + public void SetSerializationSurrogateProvider(System.Runtime.Serialization.ISerializationSurrogateProvider? provider) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] diff --git a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj index 927772e02ceb8..350ad3eebfc99 100644 --- a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj +++ b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.csproj @@ -2,12 +2,14 @@ $(NetCoreAppCurrent) + + - - - + + + diff --git a/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs b/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs index 92749bc455abe..63f1abbba95ae 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs +++ b/src/libraries/System.Runtime.Serialization.Primitives/ref/System.Runtime.Serialization.Primitives.cs @@ -76,6 +76,13 @@ public partial interface ISerializationSurrogateProvider object GetObjectToSerialize(object obj, System.Type targetType); System.Type GetSurrogateType(System.Type type); } + public interface ISerializationSurrogateProvider2 : ISerializationSurrogateProvider + { + object? GetCustomDataToExport(Reflection.MemberInfo memberInfo, Type dataContractType); + object? GetCustomDataToExport(Type runtimeType, Type dataContractType); + void GetKnownCustomDataTypes(Collections.ObjectModel.Collection customDataTypes); + Type? GetReferencedTypeOnImport(string typeName, string typeNamespace, object? customData); + } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Struct, Inherited=true, AllowMultiple=true)] public sealed partial class KnownTypeAttribute : System.Attribute { diff --git a/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj b/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj index 13aff766d38dd..bd7267ae26b97 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj +++ b/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj @@ -11,6 +11,7 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs index 6ff2e2c899451..571fc45de92d1 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs +++ b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs @@ -3,10 +3,39 @@ namespace System.Runtime.Serialization { + /// + /// Provides the methods needed to substitute one type for another by DataContractSerializer during serialization + /// and deserialization. This interface together with (and + /// `System.Runtime.Serialization.Schema.ISerializationCodeDomSurrogateProvider`) replace + /// the `IDataContractSurrogate` from .Net 4.8. + /// public interface ISerializationSurrogateProvider { + /// + /// During serialization, deserialization, and schema import and export, returns a data contract type that substitutes the specified type. + /// (Formerly known as `GetDataContractType` on the .Net 4.8 `IDataContractSurrogate` interface.) + /// + /// The runtime type to substitute. + /// The to substitute for the type value. This type must be serializable by the DataContractSerializer. For example, + /// it must be marked with the DataContractAttribute attribute or other mechanisms that the serializer recognizes. Type GetSurrogateType(Type type); + + /// + /// During serialization, returns an object that substitutes the specified object. + /// + /// The object to substitute. + /// The that the substituted object should be assigned to. + /// The substituted object that will be serialized. The object must be serializable by the DataContractSerializer. For example, + /// it must be marked with the attribute or other mechanisms that the serializer recognizes. object GetObjectToSerialize(object obj, Type targetType); + + /// + /// During deserialization, returns an object that is a substitute for the specified object. + /// + /// The deserialized object to be substituted. + /// The that the substituted object should be assigned to. + /// The substituted deserialized object. This object must be of a type that is serializable by the DataContractSerializer. For example, + /// it must be marked with the attribute or other mechanisms that the serializer recognizes. object GetDeserializedObject(object obj, Type targetType); } } diff --git a/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider2.cs b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider2.cs new file mode 100644 index 0000000000000..0194c0f111a6a --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider2.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.ObjectModel; +using System.Reflection; + +namespace System.Runtime.Serialization +{ + /// + /// Provides the methods needed to substitute one type for another by DataContractSerializer during export + /// and import of XML schema documents (XSD). This interface builds upon . + /// Together (along with `System.Runtime.Serialization.Schema.ISerializationCodeDomSurrogateProvider`), these + /// interfaces replace the `IDataContractSurrogate` from .Net 4.8. + /// + public interface ISerializationSurrogateProvider2 : ISerializationSurrogateProvider + { + /// + /// During schema export operations, inserts annotations into the schema for non-null return values. + /// + /// A that describes the member. + /// The data contract type to be annotated. + /// An object that represents the annotation to be inserted into the XML schema definition. + object? GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType); + + /// + /// During schema export operations, inserts annotations into the schema for non-null return values. + /// + /// The runtime type to be replaced. + /// The data contract type to be annotated. + /// An object that represents the annotation to be inserted into the XML schema definition. + object? GetCustomDataToExport(Type runtimeType, Type dataContractType); + + /// + /// Sets the collection of known types to use for serialization and deserialization of the custom data objects. + /// + /// A of to add known types to. + void GetKnownCustomDataTypes(Collection customDataTypes); + + /// + /// During schema import, returns the type referenced by the schema. + /// + /// The name of the type in schema. + /// The namespace of the type in schema. + /// The object that represents the annotation inserted into the XML schema definition, which is data that can be used for finding the referenced type. + /// The to use for the referenced type. + Type? GetReferencedTypeOnImport(string typeName, string typeNamespace, object? customData); + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/Directory.Build.props b/src/libraries/System.Runtime.Serialization.Schema/Directory.Build.props new file mode 100644 index 0000000000000..4a2bc78c16e30 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/Directory.Build.props @@ -0,0 +1,8 @@ + + + + true + + browser;ios;tvos;maccatalyst + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Schema/System.Runtime.Serialization.Schema.sln b/src/libraries/System.Runtime.Serialization.Schema/System.Runtime.Serialization.Schema.sln new file mode 100644 index 0000000000000..b273f4e5bf616 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/System.Runtime.Serialization.Schema.sln @@ -0,0 +1,51 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Serialization.Schema", "ref\System.Runtime.Serialization.Schema.csproj", "{74AE27CF-E940-4EEB-9A19-0968689B627E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Serialization.Schema", "src\System.Runtime.Serialization.Schema.csproj", "{14D5A803-D5BF-44E5-B2B5-0B0BC297748E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Serialization.Schema.Tests", "tests\System.Runtime.Serialization.Schema.Tests.csproj", "{627FFEFC-A317-4AD1-809F-B26CA7F475BD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2D7D470B-B092-45BC-900A-CDB20CA94BE7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{54321C0F-1323-4962-A01C-AC07028C3FA8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6591788E-0894-4655-AE2F-602407C4F766}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186}.Release|Any CPU.Build.0 = Release|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {74AE27CF-E940-4EEB-9A19-0968689B627E}.Release|Any CPU.Build.0 = Release|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E}.Release|Any CPU.Build.0 = Release|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {627FFEFC-A317-4AD1-809F-B26CA7F475BD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A83DE19F-4E19-4FEE-A3D7-FBA9E065B186} = {2D7D470B-B092-45BC-900A-CDB20CA94BE7} + {627FFEFC-A317-4AD1-809F-B26CA7F475BD} = {2D7D470B-B092-45BC-900A-CDB20CA94BE7} + {74AE27CF-E940-4EEB-9A19-0968689B627E} = {54321C0F-1323-4962-A01C-AC07028C3FA8} + {14D5A803-D5BF-44E5-B2B5-0B0BC297748E} = {6591788E-0894-4655-AE2F-602407C4F766} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {70EC5780-3C80-4D52-93B0-7FBF64E29572} + EndGlobalSection +EndGlobal diff --git a/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.cs b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.cs new file mode 100644 index 0000000000000..5f1c074aaf6dd --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Runtime.Serialization +{ + public partial interface ISerializationCodeDomSurrogateProvider + { + System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit); + } + public partial class ImportOptions + { + public System.CodeDom.Compiler.CodeDomProvider? CodeProvider { get { throw null; } set { throw null; } } + public bool EnableDataBinding { get { throw null; } set { throw null; } } + public ISerializationSurrogateProvider? DataContractSurrogate { get { throw null; } set { throw null; } } + public bool GenerateInternal { get { throw null; } set { throw null; } } + public bool GenerateSerializable { get { throw null; } set { throw null; } } + public bool ImportXmlType { get { throw null; } set { throw null; } } + public System.Collections.Generic.IDictionary Namespaces { get { throw null; } } + public System.Collections.Generic.ICollection ReferencedCollectionTypes { get { throw null; } } + public System.Collections.Generic.ICollection ReferencedTypes { get { throw null; } } + } + public partial class XsdDataContractImporter + { + public XsdDataContractImporter() { throw null; } + public XsdDataContractImporter(System.CodeDom.CodeCompileUnit codeCompileUnit) { throw null; } + + public System.CodeDom.CodeCompileUnit CodeCompileUnit { get { throw null; } } + public ImportOptions? Options { get { throw null; } set { throw null; } } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas, System.Collections.Generic.ICollection typeNames) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public bool CanImport(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.Schema.XmlSchemaElement element) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.CodeDom.CodeTypeReference GetCodeTypeReference(System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.CodeDom.CodeTypeReference GetCodeTypeReference(System.Xml.XmlQualifiedName typeName, System.Xml.Schema.XmlSchemaElement element) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.Collections.Generic.ICollection? GetKnownTypeReferences(System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void Import(System.Xml.Schema.XmlSchemaSet schemas) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void Import(System.Xml.Schema.XmlSchemaSet schemas, System.Collections.Generic.ICollection typeNames) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void Import(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.XmlQualifiedName typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.Xml.XmlQualifiedName? Import(System.Xml.Schema.XmlSchemaSet schemas, System.Xml.Schema.XmlSchemaElement element) { throw null; } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.csproj b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.csproj new file mode 100644 index 0000000000000..1e7317def2573 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/ref/System.Runtime.Serialization.Schema.csproj @@ -0,0 +1,13 @@ + + + $(NetCoreAppCurrent) + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/Resources/Strings.resx b/src/libraries/System.Runtime.Serialization.Schema/src/Resources/Strings.resx new file mode 100644 index 0000000000000..54cb0b117a74e --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/Resources/Strings.resx @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Invalid '{0}' annotation in type '{1}' from namespace '{2}'. Attribute '{3}' not present. + + + Cannot export null assembly provided via '{0}' parameter. + + + Cannot export null type provided via KnownTypesCollection. + + + Cannot export null type provided via '{0}' parameter. + + + Existing type '{0}' specified via the referenced types collection has been referenced in the generated code. Members cannot be added for this type since it cannot be modified. + + + Existing type '{0}' specified via the referenced types collection has been referenced in the generated code. Cannot set namespace for this type since it cannot be modified. + + + Type '{0}' cannot be exported as a schema type because it is an open generic type. You can only export a generic type if all its generic parameter types are actual types. + + + Type '{0}' from namespace '{1}' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter. + + + + A unique name cannot be computed for '{0}' because there are already Int32.MaxValue types of with the same name. + + + Type with data contract name '{0}' in namespace '{1}' cannot be imported. Cannot derive from sealed referenced type '{2}'. + + + Collection type cannot be generated for type '{0}' from namespace '{1}'. Cannot use a generic list type as a base type because the language does not support generic type references. + + + It contains a circular reference for type '{0}' from namespace '{1}'. + + + CLR namespace '{2}' has already been mapped to data contract namespace '{0}'. It cannot be mapped to another data contract namespace '{1}'. + + + ISerializable type with data contract name '{0}' in namespace '{1}' cannot be imported. The data contract name cannot be customized for ISerializable type and the generated name '{2}' does not match the expected name '{0}'. Check if the required name has been mapped to a different type or if it is an invalid CLR name which cannot be generated or if the type requires an outer type which is not present. + + + ISerializable type with data contract name '{0}' in namespace '{1}' cannot be imported. The data contract namespace cannot be customized for ISerializable types and the generated namespace '{3}' does not match the required CLR namespace '{2}'. Check if the required namespace has been mapped to a different data contract namespace and consider mapping it explicitly using the namespaces collection. + + + Collection type cannot be generated for type '{0}' from namespace '{1}'. Rename the type to '{2}' in namespace '{3}' or reference an existing collection type that implements '{4}' or '{5}' which can be used as a base type for the generated collection. + + + Referenced type '{0}' with data contract name '{1}' in namespace '{2}' cannot be used since it does not match imported DataContract. Need to exclude this type from referenced types. + + + Type '{0}' in namespace '{1}' cannot be imported. {2} + + + Schema type '{2}' in namespace '{3}' must be imported as an XML type. Type '{0}' cannot be mapped to this schema type because it does not implement '{1}'. Consider not adding type '{0}' to the list of referenced types or changing it to implement '{1}'. + + + An internal error has occurred. Unexpected contract type '{0}' for type '{1}' encountered. + + diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System.Runtime.Serialization.Schema.csproj b/src/libraries/System.Runtime.Serialization.Schema/src/System.Runtime.Serialization.Schema.csproj new file mode 100644 index 0000000000000..d1d5600805109 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System.Runtime.Serialization.Schema.csproj @@ -0,0 +1,28 @@ + + + $(NetCoreAppCurrent) + Microsoft + + true + Provides support for importing and exporting xsd schemas for DataContractSerializer. + +Commonly Used Types: +System.Runtime.Serialization.Schema.XsdDataContractExporter +System.Runtime.Serialization.Schema.XsdDataContractImporter + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/CodeExporter.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/CodeExporter.cs new file mode 100644 index 0000000000000..45476be4423f1 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/CodeExporter.cs @@ -0,0 +1,2033 @@ +// 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.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization.DataContracts; +using System.Security; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +using DataContractDictionary = System.Collections.Generic.Dictionary; +using ExceptionUtil = System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility; + +namespace System.Runtime.Serialization +{ + internal sealed class CodeExporter + { + private const string WildcardNamespaceMapping = "*"; + private const string TypeNameFieldName = "typeName"; + private const int MaxIdentifierLength = 511; + + private static readonly object s_codeUserDataActualTypeKey = new object(); + private static readonly object s_surrogateDataKey = typeof(ISerializationSurrogateProvider2); + + private DataContractSet _dataContractSet; + private CodeCompileUnit _codeCompileUnit; + private ImportOptions? _options; + private Dictionary _namespaces; + private Dictionary _clrNamespaces; + + internal CodeExporter(DataContractSet dataContractSet, ImportOptions? options, CodeCompileUnit codeCompileUnit) + { + _dataContractSet = dataContractSet; + _codeCompileUnit = codeCompileUnit; + AddReferencedAssembly(Assembly.GetExecutingAssembly()); + _options = options; + _namespaces = new Dictionary(); + _clrNamespaces = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Update namespace tables for DataContract(s) that are already processed + foreach (KeyValuePair pair in dataContractSet.Contracts) + { + DataContract dataContract = pair.Value; + if (!(dataContract.IsBuiltInDataContract || dataContract.Is(DataContractType.ClassDataContract))) + { + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + if (contractCodeDomInfo.IsProcessed && !contractCodeDomInfo.UsesWildcardNamespace) + { + string? clrNamespace = contractCodeDomInfo.ClrNamespace; + if (clrNamespace != null && !_clrNamespaces.ContainsKey(clrNamespace)) + { + _clrNamespaces.Add(clrNamespace, dataContract.XmlName.Namespace); + _namespaces.Add(dataContract.XmlName.Namespace, clrNamespace); + } + } + } + } + + // Copy options.Namespaces to namespace tables + if (_options != null) + { + foreach (KeyValuePair pair in _options.Namespaces) + { + string dataContractNamespace = pair.Key; + string clrNamespace = pair.Value; + if (clrNamespace == null) + clrNamespace = string.Empty; + + string? currentDataContractNamespace; + if (_clrNamespaces.TryGetValue(clrNamespace, out currentDataContractNamespace)) + { + if (dataContractNamespace != currentDataContractNamespace) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CLRNamespaceMappedMultipleTimes, currentDataContractNamespace, dataContractNamespace, clrNamespace))); + } + else + _clrNamespaces.Add(clrNamespace, dataContractNamespace); + + string? currentClrNamespace; + if (_namespaces.TryGetValue(dataContractNamespace, out currentClrNamespace)) + { + if (clrNamespace != currentClrNamespace) + { + _namespaces.Remove(dataContractNamespace); + _namespaces.Add(dataContractNamespace, clrNamespace); + } + } + else + _namespaces.Add(dataContractNamespace, clrNamespace); + } + } + + // Update namespace tables for pre-existing namespaces in CodeCompileUnit + foreach (CodeNamespace codeNS in codeCompileUnit.Namespaces) + { + string ns = codeNS.Name ?? string.Empty; + if (!_clrNamespaces.ContainsKey(ns)) + { + _clrNamespaces.Add(ns, null); + } + if (ns.Length == 0) + { + foreach (CodeTypeDeclaration codeTypeDecl in codeNS.Types) + { + AddGlobalTypeName(codeTypeDecl.Name); + } + } + } + + } + + private void AddReferencedAssembly(Assembly assembly) + { + bool alreadyExisting = false; +#pragma warning disable IL3000 // Avoid accessing Assembly file path when publishing as a single file + string assemblyName = System.IO.Path.GetFileName(assembly.Location); + if (string.IsNullOrWhiteSpace(assemblyName)) + assemblyName = $"[{assembly.FullName}]"; +#pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file + + foreach (string? existingName in _codeCompileUnit.ReferencedAssemblies) + { + if (string.Equals(existingName, assemblyName, StringComparison.OrdinalIgnoreCase)) + { + alreadyExisting = true; + break; + } + } + if (!alreadyExisting) + _codeCompileUnit.ReferencedAssemblies.Add(assemblyName); + + } + + private bool GenerateSerializableTypes + { + get { return (_options == null) ? false : _options.GenerateSerializable; } + } + + private bool GenerateInternalTypes + { + get { return (_options == null) ? false : _options.GenerateInternal; } + } + + private bool EnableDataBinding + { + get { return (_options == null) ? false : _options.EnableDataBinding; } + } + + private CodeDomProvider? CodeProvider + { + get { return _options?.CodeProvider; } + } + + private bool SupportsDeclareEvents + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.DeclareEvents); } + } + + private bool SupportsDeclareValueTypes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.DeclareValueTypes); } + } + + private bool SupportsGenericTypeReference + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.GenericTypeReference); } + } + + private bool SupportsAssemblyAttributes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.AssemblyAttributes); } + } + + private bool SupportsPartialTypes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.PartialTypes); } + } + + private bool SupportsNestedTypes + { + get { return (CodeProvider == null) ? true : CodeProvider.Supports(GeneratorSupport.NestedTypes); } + } + + private string FileExtension + { + get { return (CodeProvider == null) ? string.Empty : CodeProvider.FileExtension; } + } + + private Dictionary Namespaces + { + get { return _namespaces; } + } + + private Dictionary ClrNamespaces + { + get { return _clrNamespaces; } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal void Export() + { + try + { + foreach (KeyValuePair pair in _dataContractSet.Contracts) + { + DataContract dataContract = pair.Value; + if (dataContract.IsBuiltInDataContract) + continue; + + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + if (!contractCodeDomInfo.IsProcessed) + { + switch (dataContract.GetContractType()) + { + case DataContractType.ClassDataContract: + if (dataContract.IsISerializable) + ExportISerializableDataContract(dataContract, contractCodeDomInfo); + else + ExportClassDataContractHierarchy(dataContract.XmlName, dataContract, contractCodeDomInfo, new Dictionary()); + break; + case DataContractType.CollectionDataContract: + ExportCollectionDataContract(dataContract, contractCodeDomInfo); + break; + case DataContractType.EnumDataContract: + ExportEnumDataContract(dataContract, contractCodeDomInfo); + break; + default: + if (dataContract is XmlDataContract xmlDataContract) + ExportXmlDataContract(xmlDataContract, contractCodeDomInfo); + else + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.UnexpectedContractType, GetClrTypeFullName(dataContract.GetType()), GetClrTypeFullName(dataContract.UnderlyingType)))); + break; + }; + contractCodeDomInfo.IsProcessed = true; + } + } + + if (_options?.DataContractSurrogate is ISerializationCodeDomSurrogateProvider cdSurrogateProvider) + { + CodeNamespace[] namespaces = new CodeNamespace[_codeCompileUnit.Namespaces.Count]; + _codeCompileUnit.Namespaces.CopyTo(namespaces, 0); + foreach (CodeNamespace codeNamespace in namespaces) + InvokeProcessImportedType(codeNamespace.Types, cdSurrogateProvider); + } + } + finally + { + CodeGenerator.ValidateIdentifiers(_codeCompileUnit); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportClassDataContractHierarchy(XmlQualifiedName typeName, DataContract classContract, ContractCodeDomInfo contractCodeDomInfo, Dictionary contractNamesInHierarchy) + { + Debug.Assert(classContract.Is(DataContractType.ClassDataContract)); + + if (contractNamesInHierarchy.ContainsKey(classContract.XmlName)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.TypeCannotBeImported, typeName.Name, typeName.Namespace, SR.Format(SR.CircularTypeReference, classContract.XmlName.Name, classContract.XmlName.Namespace)))); + contractNamesInHierarchy.Add(classContract.XmlName, null); + + DataContract? baseContract = classContract.BaseContract; + if (baseContract != null) + { + ContractCodeDomInfo baseContractCodeDomInfo = GetContractCodeDomInfo(baseContract); + if (!baseContractCodeDomInfo.IsProcessed) + { + ExportClassDataContractHierarchy(typeName, baseContract, baseContractCodeDomInfo, contractNamesInHierarchy); + baseContractCodeDomInfo.IsProcessed = true; + } + } + ExportClassDataContract(classContract, contractCodeDomInfo); + } + + private void InvokeProcessImportedType(CollectionBase collection, ISerializationCodeDomSurrogateProvider surrogateProvider) + { + object[] objects = new object[collection.Count]; + ((ICollection)collection).CopyTo(objects, 0); + + foreach (object obj in objects) + { + if (obj is CodeTypeDeclaration codeTypeDeclaration) + { + CodeTypeDeclaration? newCodeTypeDeclaration = surrogateProvider.ProcessImportedType(codeTypeDeclaration, _codeCompileUnit); + + if (newCodeTypeDeclaration != codeTypeDeclaration) + { + ((IList)collection).Remove(codeTypeDeclaration); + if (newCodeTypeDeclaration != null) + ((IList)collection).Add(newCodeTypeDeclaration); + } + if (newCodeTypeDeclaration != null) + InvokeProcessImportedType(newCodeTypeDeclaration.Members, surrogateProvider); + } + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal CodeTypeReference GetCodeTypeReference(DataContract dataContract) + { + if (dataContract.IsBuiltInDataContract) + return GetCodeTypeReference(dataContract.UnderlyingType); + + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + GenerateType(dataContract, contractCodeDomInfo); + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeReference != null); + return contractCodeDomInfo.TypeReference!; + } + + private CodeTypeReference GetCodeTypeReference(Type type) + { + AddReferencedAssembly(type.Assembly); + return new CodeTypeReference(type); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetCodeTypeReference(Type type, IList? parameters) + { + CodeTypeReference codeTypeReference = GetCodeTypeReference(type); + + if (parameters != null) + { + foreach (var param in parameters) + { + CodeTypeReference? paramTypeReference = null; + bool isParamValueType = true; // Default not important. It either gets set, or paramTypeReference stays null and we short circuit before referencing this value. + + if (param is DataContract paramContract) + { + paramTypeReference = GetCodeTypeReference(paramContract); + isParamValueType = paramContract.IsValueType; + } + else if (param is Tuple typeParameters) + { + paramTypeReference = GetCodeTypeReference(typeParameters.Item1, typeParameters.Item2); + isParamValueType = (paramTypeReference != null && paramTypeReference.ArrayRank == 0); // only value type information we can get from CodeTypeReference + } + + if (paramTypeReference == null) + return null; + if (type == typeof(Nullable<>) && !isParamValueType) + return paramTypeReference; + else + codeTypeReference.TypeArguments.Add(paramTypeReference); + } + } + + return codeTypeReference; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal CodeTypeReference GetElementTypeReference(DataContract dataContract, bool isElementTypeNullable) + { + CodeTypeReference elementTypeReference = GetCodeTypeReference(dataContract); + if (dataContract.IsValueType && isElementTypeNullable) + elementTypeReference = WrapNullable(elementTypeReference); + return elementTypeReference; + } + + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Part of a logical set of properties, some of which are not static. May not remain static with future implementation updates.")] + private XmlQualifiedName GenericListName + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return DataContract.GetXmlName(typeof(List<>)); } + } + + private DataContract GenericListContract + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return _dataContractSet.GetDataContract(typeof(List<>)); } + } + + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Part of a logical set of properties, some of which are not static. May not remain static with future implementation updates.")] + private XmlQualifiedName GenericDictionaryName + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return DataContract.GetXmlName(typeof(Dictionary<,>)); } + } + + private DataContract GenericDictionaryContract + { + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + get { return _dataContractSet.GetDataContract(typeof(Dictionary<,>)); } + } + + private ContractCodeDomInfo GetContractCodeDomInfo(DataContract dataContract) + { + ContractCodeDomInfo? contractCodeDomInfo = null; + if (_dataContractSet.ProcessedContracts.TryGetValue(dataContract, out object? info)) + contractCodeDomInfo = info as ContractCodeDomInfo; + if (contractCodeDomInfo == null) + { + contractCodeDomInfo = new ContractCodeDomInfo(); + _dataContractSet.ProcessedContracts.Add(dataContract, contractCodeDomInfo); + } + return contractCodeDomInfo; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void GenerateType(DataContract dataContract, ContractCodeDomInfo contractCodeDomInfo) + { + if (!contractCodeDomInfo.IsProcessed) + { + CodeTypeReference? referencedType = GetReferencedType(dataContract); + if (referencedType != null) + { + contractCodeDomInfo.TypeReference = referencedType; + contractCodeDomInfo.ReferencedTypeExists = true; + } + else + { + CodeTypeDeclaration? type = contractCodeDomInfo.TypeDeclaration; + if (type == null) + { + string clrNamespace = GetClrNamespace(dataContract, contractCodeDomInfo); + CodeNamespace ns = GetCodeNamespace(clrNamespace, dataContract.XmlName.Namespace, contractCodeDomInfo); + type = GetNestedType(dataContract, contractCodeDomInfo); + if (type == null) + { + string typeName = XmlConvert.DecodeName(dataContract.XmlName.Name); + typeName = GetClrIdentifier(typeName, ImportGlobals.DefaultTypeName); + if (NamespaceContainsType(ns, typeName) || GlobalTypeNameConflicts(clrNamespace, typeName)) + { + for (int i = 1;; i++) + { + string uniqueName = AppendToValidClrIdentifier(typeName, i.ToString(NumberFormatInfo.InvariantInfo)); + if (!NamespaceContainsType(ns, uniqueName) && !GlobalTypeNameConflicts(clrNamespace, uniqueName)) + { + typeName = uniqueName; + break; + } + if (i == int.MaxValue) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CannotComputeUniqueName, typeName))); + } + } + + type = CreateTypeDeclaration(typeName, dataContract); + ns.Types.Add(type); + if (string.IsNullOrEmpty(clrNamespace)) + { + AddGlobalTypeName(typeName); + } + contractCodeDomInfo.TypeReference = new CodeTypeReference((clrNamespace == null || clrNamespace.Length == 0) ? typeName : clrNamespace + "." + typeName); + + if (GenerateInternalTypes) + type.TypeAttributes = TypeAttributes.NotPublic; + else + type.TypeAttributes = TypeAttributes.Public; + } + + if (_options?.DataContractSurrogate != null) + type.UserData.Add(s_surrogateDataKey, _dataContractSet.SurrogateData[dataContract]); + + contractCodeDomInfo.TypeDeclaration = type; + } + } + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeDeclaration? GetNestedType(DataContract dataContract, ContractCodeDomInfo contractCodeDomInfo) + { + if (!SupportsNestedTypes) + return null; + string originalName = dataContract.XmlName.Name; + int nestedTypeIndex = originalName.LastIndexOf('.'); + if (nestedTypeIndex <= 0) + return null; + string containingTypeName = originalName.Substring(0, nestedTypeIndex); + DataContract? containingDataContract = _dataContractSet.GetDataContract(new XmlQualifiedName(containingTypeName, dataContract.XmlName.Namespace)); + if (containingDataContract == null) + return null; + string nestedTypeName = XmlConvert.DecodeName(originalName.Substring(nestedTypeIndex + 1)); + nestedTypeName = GetClrIdentifier(nestedTypeName, ImportGlobals.DefaultTypeName); + + ContractCodeDomInfo containingContractCodeDomInfo = GetContractCodeDomInfo(containingDataContract); + GenerateType(containingDataContract, containingContractCodeDomInfo); + if (containingContractCodeDomInfo.ReferencedTypeExists) + return null; + + CodeTypeDeclaration containingType = containingContractCodeDomInfo.TypeDeclaration!; // Nested types by definition have containing types. + if (TypeContainsNestedType(containingType, nestedTypeName)) + { + for (int i = 1;; i++) + { + string uniqueName = AppendToValidClrIdentifier(nestedTypeName, i.ToString(NumberFormatInfo.InvariantInfo)); + if (!TypeContainsNestedType(containingType, uniqueName)) + { + nestedTypeName = uniqueName; + break; + } + } + } + + CodeTypeDeclaration type = CreateTypeDeclaration(nestedTypeName, dataContract); + containingType.Members.Add(type); + contractCodeDomInfo.TypeReference = new CodeTypeReference(containingContractCodeDomInfo.TypeReference!.BaseType + "+" + nestedTypeName); // Again, nested types by definition have containing types. + + if (GenerateInternalTypes) + type.TypeAttributes = TypeAttributes.NestedAssembly; + else + type.TypeAttributes = TypeAttributes.NestedPublic; + return type; + } + + private static CodeTypeDeclaration CreateTypeDeclaration(string typeName, DataContract dataContract) + { + CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeName); + CodeAttributeDeclaration debuggerStepThroughAttribute = new CodeAttributeDeclaration(typeof(System.Diagnostics.DebuggerStepThroughAttribute).FullName!); + CodeAttributeDeclaration generatedCodeAttribute = new CodeAttributeDeclaration(typeof(GeneratedCodeAttribute).FullName!); + + AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName(); + generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Name))); + generatedCodeAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Version?.ToString()))); + + // System.Diagnostics.DebuggerStepThroughAttribute not allowed on enums + // ensure that the attribute is only generated on types that are not enums + if (!dataContract.Is(DataContractType.EnumDataContract)) + { + typeDecl.CustomAttributes.Add(debuggerStepThroughAttribute); + } + typeDecl.CustomAttributes.Add(generatedCodeAttribute); + return typeDecl; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetReferencedType(DataContract dataContract) + { + CodeTypeReference? typeReference = GetSurrogatedTypeReference(dataContract); + if (typeReference != null) + return typeReference; + + Type? type = _dataContractSet.GetReferencedType(dataContract.XmlName, dataContract, out DataContract? referencedContract, out object[]? parameters, SupportsGenericTypeReference); + if (type != null && !type.IsGenericTypeDefinition && !type.ContainsGenericParameters) + { + if (dataContract is XmlDataContract xmlContract) + { + if (typeof(IXmlSerializable).IsAssignableFrom(type)) + { + if (xmlContract.IsTypeDefinedOnImport) + { + if (!xmlContract.Equals(_dataContractSet.GetDataContract(type))) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypeDoesNotMatch, type.AssemblyQualifiedName, dataContract.XmlName.Name, dataContract.XmlName.Namespace))); + } + else + { + xmlContract.IsValueType = type.IsValueType; + xmlContract.IsTypeDefinedOnImport = true; + } + return GetCodeTypeReference(type); + } + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.TypeMustBeIXmlSerializable, GetClrTypeFullName(type), GetClrTypeFullName(typeof(IXmlSerializable)), dataContract.XmlName.Name, dataContract.XmlName.Namespace))); + } + referencedContract = _dataContractSet.GetDataContract(type); + if (referencedContract.Equals(dataContract)) + { + typeReference = GetCodeTypeReference(type); + typeReference.UserData.Add(s_codeUserDataActualTypeKey, type); + return typeReference; + } + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypeDoesNotMatch, type.AssemblyQualifiedName, dataContract.XmlName.Name, dataContract.XmlName.Namespace))); + } + else if (type != null) + { + typeReference = GetCodeTypeReference(type, parameters); + + if (referencedContract != null && !referencedContract.Equals(dataContract)) + { + Debug.Assert(typeReference != null); + type = (Type?)typeReference.UserData[s_codeUserDataActualTypeKey]; + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypeDoesNotMatch, + type?.AssemblyQualifiedName, + referencedContract.XmlName.Name, + referencedContract.XmlName.Namespace))); + } + + return typeReference; + } + else if (referencedContract != null) + { + typeReference = GetCodeTypeReference(referencedContract); + return typeReference; + } + + return GetReferencedCollectionType(dataContract.As(DataContractType.CollectionDataContract)); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetReferencedCollectionType(DataContract? collectionContract) + { + if (collectionContract == null) + return null; + + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + if (HasDefaultCollectionNames(collectionContract)) + { + CodeTypeReference? typeReference; + if (!TryGetReferencedDictionaryType(collectionContract, out typeReference)) + { + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract itemContract = collectionContract.BaseContract!; + if (collectionContract.IsDictionaryLike(out _, out _, out _)) + { + GenerateKeyValueType(itemContract.As(DataContractType.ClassDataContract)); + } + bool isItemTypeNullable = collectionContract.IsItemTypeNullable(); + if (!TryGetReferencedListType(itemContract, isItemTypeNullable, out typeReference)) + { + CodeTypeReference? elementTypeReference = GetElementTypeReference(itemContract, isItemTypeNullable); + if (elementTypeReference != null) + typeReference = new CodeTypeReference(elementTypeReference, 1); + } + } + return typeReference; + } + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private static bool HasDefaultCollectionNames(DataContract collectionContract) + { + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract itemContract = collectionContract.BaseContract!; + bool isDictionary = collectionContract.IsDictionaryLike(out string? keyName, out string? valueName, out string? itemName); + if (itemName != itemContract.XmlName.Name) + return false; + + if (isDictionary && (keyName != ImportGlobals.KeyLocalName || valueName != ImportGlobals.ValueLocalName)) + return false; + + XmlQualifiedName expectedType = itemContract.GetArrayTypeName(collectionContract.IsItemTypeNullable()); + return (collectionContract.XmlName.Name == expectedType.Name && collectionContract.XmlName.Namespace == expectedType.Namespace); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private bool TryGetReferencedDictionaryType(DataContract collectionContract, [NotNullWhen(true)] out CodeTypeReference? typeReference) + { + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + // Check if it is a dictionary and use referenced dictionary type if present + if (collectionContract.IsDictionaryLike(out _, out _, out _) + && SupportsGenericTypeReference) + { + Type? type = _dataContractSet.GetReferencedType(GenericDictionaryName, GenericDictionaryContract, out DataContract? _, out object[]? _) ?? typeof(Dictionary<,>); + + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract? itemContract = collectionContract.BaseContract!.As(DataContractType.ClassDataContract); + + // A dictionary should have a Key/Value item contract that has at least two members: key and value. + Debug.Assert(itemContract != null); + Debug.Assert(itemContract.DataMembers.Count >= 2); + + DataMember keyMember = itemContract.DataMembers[0]; + DataMember valueMember = itemContract.DataMembers[1]; + CodeTypeReference? keyTypeReference = GetElementTypeReference(keyMember.MemberTypeContract, keyMember.IsNullable); + CodeTypeReference? valueTypeReference = GetElementTypeReference(valueMember.MemberTypeContract, valueMember.IsNullable); + if (keyTypeReference != null && valueTypeReference != null) + { + typeReference = GetCodeTypeReference(type); + typeReference.TypeArguments.Add(keyTypeReference); + typeReference.TypeArguments.Add(valueTypeReference); + return true; + } + } + typeReference = null; + return false; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private bool TryGetReferencedListType(DataContract itemContract, bool isItemTypeNullable, out CodeTypeReference? typeReference) + { + if (SupportsGenericTypeReference) + { + Type? type = _dataContractSet.GetReferencedType(GenericListName, GenericListContract, out DataContract? _, out object[]? _); + if (type != null) + { + typeReference = GetCodeTypeReference(type); + typeReference.TypeArguments.Add(GetElementTypeReference(itemContract, isItemTypeNullable)!); // Lists have an item type + return true; + } + } + typeReference = null; + return false; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private CodeTypeReference? GetSurrogatedTypeReference(DataContract dataContract) + { + Type? type = GetReferencedTypeOnImport(dataContract); + if (type != null) + { + CodeTypeReference typeReference = GetCodeTypeReference(type); + typeReference.UserData.Add(s_codeUserDataActualTypeKey, type); + return typeReference; + } + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private Type? GetReferencedTypeOnImport(DataContract dataContract) + { + Type? type = null; + if (_options?.DataContractSurrogate is ISerializationSurrogateProvider2 surrogateProvider) + { + if (DataContract.GetBuiltInDataContract(dataContract.XmlName.Name, dataContract.XmlName.Namespace) == null) + type = surrogateProvider.GetReferencedTypeOnImport(dataContract.XmlName.Name, dataContract.XmlName.Namespace, _dataContractSet.SurrogateData[dataContract]); + } + return type; + } + + private static bool NamespaceContainsType(CodeNamespace ns, string typeName) + { + foreach (CodeTypeDeclaration type in ns.Types) + { + if (string.Equals(typeName, type.Name, StringComparison.OrdinalIgnoreCase)) + return true; + } + return false; + } + + private bool GlobalTypeNameConflicts(string clrNamespace, string typeName) + { + return (string.IsNullOrEmpty(clrNamespace) && _clrNamespaces.ContainsKey(typeName)); + } + + private void AddGlobalTypeName(string typeName) + { + if (!_clrNamespaces.ContainsKey(typeName)) + { + _clrNamespaces.Add(typeName, null); + } + } + + private static bool TypeContainsNestedType(CodeTypeDeclaration containingType, string typeName) + { + foreach (CodeTypeMember member in containingType.Members) + { + if (member is CodeTypeDeclaration declaration) + { + if (string.Equals(typeName, declaration.Name, StringComparison.OrdinalIgnoreCase)) + return true; + } + } + return false; + } + + private static string GetNameForAttribute(string name) + { + string decodedName = XmlConvert.DecodeName(name); + if (string.CompareOrdinal(name, decodedName) == 0) + return name; + string reencodedName = SchemaImportHelper.EncodeLocalName(decodedName); + return (string.CompareOrdinal(name, reencodedName) == 0) ? decodedName : name; + } + + private void AddSerializableAttribute(bool generateSerializable, CodeTypeDeclaration type, ContractCodeDomInfo contractCodeDomInfo) + { + if (generateSerializable) + { + type.CustomAttributes.Add(SerializableAttribute); + AddImportStatement(typeof(SerializableAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportClassDataContract(DataContract classDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + GenerateType(classDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + if (SupportsPartialTypes) + type.IsPartial = true; + if (classDataContract.IsValueType && SupportsDeclareValueTypes) + type.IsStruct = true; + else + type.IsClass = true; + + string dataContractName = GetNameForAttribute(classDataContract.XmlName.Name); + CodeAttributeDeclaration dataContractAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(DataContractAttribute))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataContractName))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NamespaceProperty, new CodePrimitiveExpression(classDataContract.XmlName.Namespace))); + if (classDataContract.IsReference != ImportGlobals.DefaultIsReference) + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.IsReferenceProperty, new CodePrimitiveExpression(classDataContract.IsReference))); + type.CustomAttributes.Add(dataContractAttribute); + AddImportStatement(typeof(DataContractAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + + AddSerializableAttribute(GenerateSerializableTypes, type, contractCodeDomInfo); + + AddKnownTypes(classDataContract, contractCodeDomInfo); + + bool raisePropertyChanged = EnableDataBinding && SupportsDeclareEvents; + if (classDataContract.BaseContract == null) + { + if (!type.IsStruct) + type.BaseTypes.Add(typeof(object)); + AddExtensionData(contractCodeDomInfo); + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + else + { + ContractCodeDomInfo baseContractCodeDomInfo = GetContractCodeDomInfo(classDataContract.BaseContract); + Debug.Assert(baseContractCodeDomInfo.IsProcessed, "Cannot generate code for type if code for base type has not been generated"); + type.BaseTypes.Add(baseContractCodeDomInfo.TypeReference); + AddBaseMemberNames(baseContractCodeDomInfo, contractCodeDomInfo); + if (baseContractCodeDomInfo.ReferencedTypeExists) + { + Type? actualType = (Type?)baseContractCodeDomInfo.TypeReference?.UserData[s_codeUserDataActualTypeKey]; + Debug.Assert(actualType != null); // If we're in this if condition, then we should be able to get a Type + ThrowIfReferencedBaseTypeSealed(actualType, classDataContract); + if (!typeof(IExtensibleDataObject).IsAssignableFrom(actualType)) + AddExtensionData(contractCodeDomInfo); + if (!typeof(INotifyPropertyChanged).IsAssignableFrom(actualType)) + { + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + else + { + raisePropertyChanged = false; + } + } + } + + for (int i = 0; i < classDataContract.DataMembers.Count; i++) + { + DataMember dataMember = classDataContract.DataMembers[i]; + + CodeTypeReference memberType = GetElementTypeReference(dataMember.MemberTypeContract, + (dataMember.IsNullable && dataMember.MemberTypeContract.IsValueType)); + + string dataMemberName = GetNameForAttribute(dataMember.Name); + string propertyName = GetMemberName(dataMemberName, contractCodeDomInfo); + string fieldName = GetMemberName(AppendToValidClrIdentifier(propertyName, ImportGlobals.DefaultFieldSuffix), contractCodeDomInfo); + + CodeMemberField field = new CodeMemberField(); + field.Type = memberType; + field.Name = fieldName; + field.Attributes = MemberAttributes.Private; + + CodeMemberProperty property = CreateProperty(memberType, propertyName, fieldName, dataMember.MemberTypeContract.IsValueType && SupportsDeclareValueTypes, raisePropertyChanged); + object? surrogateData = _dataContractSet.SurrogateData[dataMember]; + if (surrogateData != null) + property.UserData.Add(s_surrogateDataKey, surrogateData); + + CodeAttributeDeclaration dataMemberAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(DataMemberAttribute))); + if (dataMemberName != property.Name) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataMemberName))); + if (dataMember.IsRequired != ImportGlobals.DefaultIsRequired) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.IsRequiredProperty, new CodePrimitiveExpression(dataMember.IsRequired))); + if (dataMember.EmitDefaultValue != ImportGlobals.DefaultEmitDefaultValue) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.EmitDefaultValueProperty, new CodePrimitiveExpression(dataMember.EmitDefaultValue))); + if (dataMember.Order != ImportGlobals.DefaultOrder) + dataMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.OrderProperty, new CodePrimitiveExpression(dataMember.Order))); + property.CustomAttributes.Add(dataMemberAttribute); + + if (GenerateSerializableTypes && !dataMember.IsRequired) + { + CodeAttributeDeclaration optionalFieldAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(OptionalFieldAttribute))); + field.CustomAttributes.Add(optionalFieldAttribute); + } + + type.Members.Add(field); + type.Members.Add(property); + } + } + + private bool CanDeclareAssemblyAttribute(ContractCodeDomInfo contractCodeDomInfo) + { + return SupportsAssemblyAttributes && !contractCodeDomInfo.UsesWildcardNamespace; + } + + private static bool NeedsExplicitNamespace(string dataContractNamespace, string clrNamespace) + { + return (SchemaImportHelper.GetDefaultXmlNamespace(clrNamespace) != dataContractNamespace); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal ICollection? GetKnownTypeReferences(DataContract dataContract) + { + DataContractDictionary? knownTypeDictionary = GetKnownTypeContracts(dataContract); + if (knownTypeDictionary == null) + return null; + + ICollection? knownTypeContracts = knownTypeDictionary.Values; + if (knownTypeContracts == null || knownTypeContracts.Count == 0) + return null; + + List knownTypeReferences = new List(); + foreach (DataContract knownTypeContract in knownTypeContracts) + { + knownTypeReferences.Add(GetCodeTypeReference(knownTypeContract)); + } + return knownTypeReferences; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private DataContractDictionary? GetKnownTypeContracts(DataContract dataContract) + { + if (_dataContractSet.KnownTypesForObject != null && IsObjectContract(dataContract)) + { + return _dataContractSet.KnownTypesForObject; + } + else if (dataContract.Is(DataContractType.ClassDataContract)) + { + ContractCodeDomInfo contractCodeDomInfo = GetContractCodeDomInfo(dataContract); + if (!contractCodeDomInfo.IsProcessed) + GenerateType(dataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return GetKnownTypeContracts(dataContract, new Dictionary()); + } + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private DataContractDictionary? GetKnownTypeContracts(DataContract classDataContract, Dictionary handledContracts) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + if (handledContracts.ContainsKey(classDataContract)) + return classDataContract.KnownDataContracts; + + handledContracts.Add(classDataContract, null); + bool objectMemberHandled = false; + foreach (DataMember dataMember in classDataContract.DataMembers) + { + DataContract memberContract = dataMember.MemberTypeContract; + if (!objectMemberHandled && _dataContractSet.KnownTypesForObject != null && IsObjectContract(memberContract)) + { + AddKnownTypeContracts(classDataContract, _dataContractSet.KnownTypesForObject); + objectMemberHandled = true; + } + else if (memberContract.Is(DataContractType.ClassDataContract)) + { + ContractCodeDomInfo memberCodeDomInfo = GetContractCodeDomInfo(memberContract); + if (!memberCodeDomInfo.IsProcessed) + GenerateType(memberContract, memberCodeDomInfo); + if (memberCodeDomInfo.ReferencedTypeExists) + { + AddKnownTypeContracts(classDataContract, GetKnownTypeContracts(memberContract, handledContracts)); + } + } + } + + return classDataContract.KnownDataContracts; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private static void AddKnownTypeContracts(DataContract classDataContract, DataContractDictionary? knownContracts) + { + if (knownContracts == null || knownContracts.Count == 0) + return; + + // This is a ClassDataContract. As such, it's KnownDataContracts collection is always non-null. + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + Debug.Assert(classDataContract.KnownDataContracts != null); + + foreach (KeyValuePair pair in knownContracts) + { + if (classDataContract.XmlName != pair.Key && !classDataContract.KnownDataContracts.ContainsKey(pair.Key) && !pair.Value.IsBuiltInDataContract) + classDataContract.KnownDataContracts.Add(pair.Key, pair.Value); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void AddKnownTypes(DataContract classDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + DataContractDictionary? knownContractDictionary = GetKnownTypeContracts(classDataContract, new Dictionary()); + if (knownContractDictionary == null || knownContractDictionary.Count == 0) + return; + + ICollection knownTypeContracts = knownContractDictionary.Values; + foreach (DataContract knownTypeContract in knownTypeContracts) + { + // This is only called from methods that first call GenerateType to fill in this info. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeAttributeDeclaration knownTypeAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(KnownTypeAttribute))); + knownTypeAttribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(GetCodeTypeReference(knownTypeContract)))); + contractCodeDomInfo.TypeDeclaration.CustomAttributes.Add(knownTypeAttribute); + } + AddImportStatement(typeof(KnownTypeAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + } + + private CodeTypeReference WrapNullable(CodeTypeReference memberType) + { + if (!SupportsGenericTypeReference) + return memberType; + + CodeTypeReference nullableOfMemberType = GetCodeTypeReference(typeof(Nullable<>)); + nullableOfMemberType.TypeArguments.Add(memberType); + return nullableOfMemberType; + } + + private void AddExtensionData(ContractCodeDomInfo contractCodeDomInfo) + { + if (contractCodeDomInfo.TypeDeclaration != null) + { + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + type.BaseTypes.Add(GetClrTypeFullName(typeof(IExtensibleDataObject))); + CodeMemberField extensionDataObjectField = ExtensionDataObjectField; + if (GenerateSerializableTypes) + { + CodeAttributeDeclaration nonSerializedAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(NonSerializedAttribute))); + extensionDataObjectField.CustomAttributes.Add(nonSerializedAttribute); + } + type.Members.Add(extensionDataObjectField); + contractCodeDomInfo.AddMemberName(extensionDataObjectField.Name); + CodeMemberProperty extensionDataObjectProperty = ExtensionDataObjectProperty; + type.Members.Add(extensionDataObjectProperty); + contractCodeDomInfo.AddMemberName(extensionDataObjectProperty.Name); + } + } + + private void AddPropertyChangedNotifier(ContractCodeDomInfo contractCodeDomInfo, bool isValueType) + { + if (EnableDataBinding && SupportsDeclareEvents && contractCodeDomInfo.TypeDeclaration != null) + { + CodeTypeDeclaration codeTypeDeclaration = contractCodeDomInfo.TypeDeclaration; + codeTypeDeclaration.BaseTypes.Add(CodeTypeIPropertyChange); + CodeMemberEvent memberEvent = PropertyChangedEvent; + codeTypeDeclaration.Members.Add(memberEvent); + CodeMemberMethod raisePropertyChangedEventMethod = RaisePropertyChangedEventMethod; + if (!isValueType) + raisePropertyChangedEventMethod.Attributes |= MemberAttributes.Family; + codeTypeDeclaration.Members.Add(raisePropertyChangedEventMethod); + contractCodeDomInfo.AddMemberName(memberEvent.Name); + contractCodeDomInfo.AddMemberName(raisePropertyChangedEventMethod.Name); + } + } + + private static void ThrowIfReferencedBaseTypeSealed(Type baseType, DataContract dataContract) + { + if (baseType.IsSealed) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotDeriveFromSealedReferenceType, dataContract.XmlName.Name, dataContract.XmlName.Namespace, GetClrTypeFullName(baseType)))); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportEnumDataContract(DataContract enumDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(enumDataContract.Is(DataContractType.EnumDataContract)); + + GenerateType(enumDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + // BaseContract is never null for EnumDataContract + Type baseType = enumDataContract.BaseContract!.UnderlyingType; + type.IsEnum = true; + type.BaseTypes.Add(baseType); + if (baseType.IsDefined(typeof(FlagsAttribute), false)) + { + type.CustomAttributes.Add(new CodeAttributeDeclaration(GetClrTypeFullName(typeof(FlagsAttribute)))); + AddImportStatement(typeof(FlagsAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + } + + string dataContractName = GetNameForAttribute(enumDataContract.XmlName.Name); + CodeAttributeDeclaration dataContractAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(DataContractAttribute))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataContractName))); + dataContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NamespaceProperty, new CodePrimitiveExpression(enumDataContract.XmlName.Namespace))); + type.CustomAttributes.Add(dataContractAttribute); + AddImportStatement(typeof(DataContractAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + + for (int i = 0; i < enumDataContract.DataMembers.Count; i++) + { + string stringValue = enumDataContract.DataMembers[i].Name; + long longValue = enumDataContract.DataMembers[i].Order; // Members[] and Values[] go hand in hand. + + CodeMemberField enumMember = new CodeMemberField(); + if (baseType == typeof(ulong)) + enumMember.InitExpression = new CodeSnippetExpression(XmlConvert.ToString((ulong)longValue)); + else + enumMember.InitExpression = new CodePrimitiveExpression(longValue); + enumMember.Name = GetMemberName(stringValue, contractCodeDomInfo); + CodeAttributeDeclaration enumMemberAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(EnumMemberAttribute))); + if (enumMember.Name != stringValue) + enumMemberAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ValueProperty, new CodePrimitiveExpression(stringValue))); + enumMember.CustomAttributes.Add(enumMemberAttribute); + type.Members.Add(enumMember); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportISerializableDataContract(DataContract classDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(classDataContract.Is(DataContractType.ClassDataContract)); + + GenerateType(classDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + if (SchemaImportHelper.GetDefaultXmlNamespace(contractCodeDomInfo.ClrNamespace) != classDataContract.XmlName.Namespace) + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidClrNamespaceGeneratedForISerializable, classDataContract.XmlName.Name, classDataContract.XmlName.Namespace, SchemaImportHelper.GetDataContractNamespaceFromUri(classDataContract.XmlName.Namespace), contractCodeDomInfo.ClrNamespace))); + + string dataContractName = GetNameForAttribute(classDataContract.XmlName.Name); + int nestedTypeIndex = dataContractName.LastIndexOf('.'); + string expectedName = (nestedTypeIndex <= 0 || nestedTypeIndex == dataContractName.Length - 1) ? dataContractName : dataContractName.Substring(nestedTypeIndex + 1); + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + if (contractCodeDomInfo.TypeDeclaration.Name != expectedName) + throw ExceptionUtil.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.InvalidClrNameGeneratedForISerializable, classDataContract.XmlName.Name, classDataContract.XmlName.Namespace, contractCodeDomInfo.TypeDeclaration.Name))); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + if (SupportsPartialTypes) + type.IsPartial = true; + if (classDataContract.IsValueType && SupportsDeclareValueTypes) + type.IsStruct = true; + else + type.IsClass = true; + + AddSerializableAttribute(true /*generateSerializable*/, type, contractCodeDomInfo); + + AddKnownTypes(classDataContract, contractCodeDomInfo); + + if (classDataContract.BaseContract == null) + { + if (!type.IsStruct) + type.BaseTypes.Add(typeof(object)); + type.BaseTypes.Add(GetClrTypeFullName(typeof(ISerializable))); + type.Members.Add(ISerializableBaseConstructor); + type.Members.Add(SerializationInfoField); + type.Members.Add(SerializationInfoProperty); + type.Members.Add(GetObjectDataMethod); + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + else + { + ContractCodeDomInfo baseContractCodeDomInfo = GetContractCodeDomInfo(classDataContract.BaseContract); + GenerateType(classDataContract.BaseContract, baseContractCodeDomInfo); + type.BaseTypes.Add(baseContractCodeDomInfo.TypeReference); + if (baseContractCodeDomInfo.ReferencedTypeExists) + { + Type? actualType = (Type?)baseContractCodeDomInfo.TypeReference?.UserData[s_codeUserDataActualTypeKey]; + Debug.Assert(actualType != null); // If we're in this if condition, then we should be able to get a Type + ThrowIfReferencedBaseTypeSealed(actualType, classDataContract); + } + type.Members.Add(ISerializableDerivedConstructor); + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void GenerateKeyValueType(DataContract? keyValueContract) + { + Debug.Assert(keyValueContract == null || keyValueContract.Is(DataContractType.ClassDataContract)); + + // Add code for KeyValue item type in the case where its usage is limited to dictionary + // and dictionary is not found in referenced types + if (keyValueContract != null && _dataContractSet.GetDataContract(keyValueContract.XmlName) == null) + { + ContractCodeDomInfo? contractCodeDomInfo = null; + if (_dataContractSet.ProcessedContracts.TryGetValue(keyValueContract, out object? info)) + contractCodeDomInfo = info as ContractCodeDomInfo; + if (contractCodeDomInfo == null) + { + contractCodeDomInfo = new ContractCodeDomInfo(); + _dataContractSet.ProcessedContracts.Add(keyValueContract, contractCodeDomInfo); + ExportClassDataContract(keyValueContract, contractCodeDomInfo); + contractCodeDomInfo.IsProcessed = true; + } + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportCollectionDataContract(DataContract collectionContract, ContractCodeDomInfo contractCodeDomInfo) + { + Debug.Assert(collectionContract.Is(DataContractType.CollectionDataContract)); + + GenerateType(collectionContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + string dataContractName = GetNameForAttribute(collectionContract.XmlName.Name); + + // If type name is not expected, generate collection type that derives from referenced list type and uses [CollectionDataContract] + if (!SupportsGenericTypeReference) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.CannotUseGenericTypeAsBase, dataContractName, + collectionContract.XmlName.Namespace))); + + // ItemContract - aka BaseContract - is never null for CollectionDataContract + DataContract itemContract = collectionContract.BaseContract!; + bool isItemTypeNullable = collectionContract.IsItemTypeNullable(); + bool isDictionary = collectionContract.IsDictionaryLike(out string? keyName, out string? valueName, out string? itemName); + + CodeTypeReference? baseTypeReference; + bool foundDictionaryBase = TryGetReferencedDictionaryType(collectionContract, out baseTypeReference); + if (!foundDictionaryBase) + { + if (isDictionary) + { + GenerateKeyValueType(itemContract.As(DataContractType.ClassDataContract)); + } + if (!TryGetReferencedListType(itemContract, isItemTypeNullable, out baseTypeReference)) + { + if (SupportsGenericTypeReference) + { + baseTypeReference = GetCodeTypeReference(typeof(List<>)); + baseTypeReference.TypeArguments.Add(GetElementTypeReference(itemContract, isItemTypeNullable)); + } + else + { + string expectedTypeName = ImportGlobals.ArrayPrefix + itemContract.XmlName.Name; + string expectedTypeNs = SchemaImportHelper.GetCollectionNamespace(itemContract.XmlName.Namespace); + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedBaseTypeDoesNotExist, + dataContractName, collectionContract.XmlName.Namespace, + expectedTypeName, expectedTypeNs, GetClrTypeFullName(typeof(IList<>)), GetClrTypeFullName(typeof(ICollection<>))))); + } + } + } + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration generatedType = contractCodeDomInfo.TypeDeclaration; + generatedType.BaseTypes.Add(baseTypeReference); + CodeAttributeDeclaration collectionContractAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(CollectionDataContractAttribute))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NameProperty, new CodePrimitiveExpression(dataContractName))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.NamespaceProperty, new CodePrimitiveExpression(collectionContract.XmlName.Namespace))); + if (collectionContract.IsReference != ImportGlobals.DefaultIsReference) + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.IsReferenceProperty, new CodePrimitiveExpression(collectionContract.IsReference))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ItemNameProperty, new CodePrimitiveExpression(GetNameForAttribute(itemName!)))); // ItemName is never null for Collection contracts. + if (foundDictionaryBase) + { + // These are not null if we are working with a dictionary. See CollectionDataContract.IsDictionary + Debug.Assert(keyName != null); + Debug.Assert(valueName != null); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.KeyNameProperty, new CodePrimitiveExpression(GetNameForAttribute(keyName)))); + collectionContractAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ValueNameProperty, new CodePrimitiveExpression(GetNameForAttribute(valueName)))); + } + generatedType.CustomAttributes.Add(collectionContractAttribute); + AddImportStatement(typeof(CollectionDataContractAttribute).Namespace, contractCodeDomInfo.CodeNamespace); + AddSerializableAttribute(GenerateSerializableTypes, generatedType, contractCodeDomInfo); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private void ExportXmlDataContract(XmlDataContract xmlDataContract, ContractCodeDomInfo contractCodeDomInfo) + { + GenerateType(xmlDataContract, contractCodeDomInfo); + if (contractCodeDomInfo.ReferencedTypeExists) + return; + + // This is supposed to be set by GenerateType. If it wasn't, there is a problem. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + CodeTypeDeclaration type = contractCodeDomInfo.TypeDeclaration; + if (SupportsPartialTypes) + type.IsPartial = true; + if (xmlDataContract.IsValueType) + type.IsStruct = true; + else + { + type.IsClass = true; + type.BaseTypes.Add(typeof(object)); + } + AddSerializableAttribute(GenerateSerializableTypes, type, contractCodeDomInfo); + + type.BaseTypes.Add(GetClrTypeFullName(typeof(IXmlSerializable))); + + type.Members.Add(NodeArrayField); + type.Members.Add(NodeArrayProperty); + type.Members.Add(ReadXmlMethod); + type.Members.Add(WriteXmlMethod); + type.Members.Add(GetSchemaMethod); + if (xmlDataContract.IsAnonymous && !xmlDataContract.HasRoot) + { + type.CustomAttributes.Add(new CodeAttributeDeclaration( + GetClrTypeFullName(ImportGlobals.TypeOfXmlSchemaProviderAttribute), + new CodeAttributeArgument(NullReference), + new CodeAttributeArgument(ImportGlobals.IsAnyProperty, new CodePrimitiveExpression(true))) + ); + } + else + { + type.CustomAttributes.Add(new CodeAttributeDeclaration( + GetClrTypeFullName(ImportGlobals.TypeOfXmlSchemaProviderAttribute), + new CodeAttributeArgument(new CodePrimitiveExpression(ImportGlobals.ExportSchemaMethod))) + ); + + CodeMemberField typeNameField = new CodeMemberField(ImportGlobals.TypeOfXmlQualifiedName, TypeNameFieldName); + typeNameField.Attributes |= MemberAttributes.Static | MemberAttributes.Private; + XmlQualifiedName typeName = xmlDataContract.IsAnonymous + ? XsdDataContractImporter.ImportActualType(xmlDataContract.XsdType?.Annotation, xmlDataContract.XmlName, xmlDataContract.XmlName) + : xmlDataContract.XmlName; + typeNameField.InitExpression = new CodeObjectCreateExpression(ImportGlobals.TypeOfXmlQualifiedName, new CodePrimitiveExpression(typeName.Name), new CodePrimitiveExpression(typeName.Namespace)); + type.Members.Add(typeNameField); + + type.Members.Add(GetSchemaStaticMethod); + + bool isElementNameDifferent = + (xmlDataContract.TopLevelElementName != null && xmlDataContract.TopLevelElementName.Value != xmlDataContract.XmlName.Name) || + (xmlDataContract.TopLevelElementNamespace != null && xmlDataContract.TopLevelElementNamespace.Value != xmlDataContract.XmlName.Namespace); + if (isElementNameDifferent || xmlDataContract.IsTopLevelElementNullable == false) + { + CodeAttributeDeclaration xmlRootAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(XmlRootAttribute))); + if (isElementNameDifferent) + { + if (xmlDataContract.TopLevelElementName != null) + { + xmlRootAttribute.Arguments.Add(new CodeAttributeArgument("ElementName", new CodePrimitiveExpression(xmlDataContract.TopLevelElementName.Value))); + } + if (xmlDataContract.TopLevelElementNamespace != null) + { + xmlRootAttribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(xmlDataContract.TopLevelElementNamespace.Value))); + } + } + if (xmlDataContract.IsTopLevelElementNullable == false) + xmlRootAttribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression(false))); + type.CustomAttributes.Add(xmlRootAttribute); + } + } + AddPropertyChangedNotifier(contractCodeDomInfo, type.IsStruct); + } + + private CodeNamespace GetCodeNamespace(string clrNamespace, string dataContractNamespace, ContractCodeDomInfo contractCodeDomInfo) + { + if (contractCodeDomInfo.CodeNamespace != null) + return contractCodeDomInfo.CodeNamespace; + + CodeNamespaceCollection codeNamespaceCollection = _codeCompileUnit.Namespaces; + foreach (CodeNamespace ns in codeNamespaceCollection) + { + if (ns.Name == clrNamespace) + { + contractCodeDomInfo.CodeNamespace = ns; + return ns; + } + } + + CodeNamespace codeNamespace = new CodeNamespace(clrNamespace); + codeNamespaceCollection.Add(codeNamespace); + + if (CanDeclareAssemblyAttribute(contractCodeDomInfo) + && NeedsExplicitNamespace(dataContractNamespace, clrNamespace)) + { + CodeAttributeDeclaration namespaceAttribute = new CodeAttributeDeclaration(GetClrTypeFullName(typeof(ContractNamespaceAttribute))); + namespaceAttribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(dataContractNamespace))); + namespaceAttribute.Arguments.Add(new CodeAttributeArgument(ImportGlobals.ClrNamespaceProperty, new CodePrimitiveExpression(clrNamespace))); + _codeCompileUnit.AssemblyCustomAttributes.Add(namespaceAttribute); + } + contractCodeDomInfo.CodeNamespace = codeNamespace; + return codeNamespace; + } + + private static string GetMemberName(string memberName, ContractCodeDomInfo contractCodeDomInfo) + { + memberName = GetClrIdentifier(memberName, ImportGlobals.DefaultGeneratedMember); + + // This is only called from Export* methods which have already called GenerateType to fill in this info. + Debug.Assert(contractCodeDomInfo.TypeDeclaration != null); + + if (memberName == contractCodeDomInfo.TypeDeclaration.Name) + memberName = AppendToValidClrIdentifier(memberName, ImportGlobals.DefaultMemberSuffix); + + if (contractCodeDomInfo.GetMemberNames().Contains(memberName)) + { + string uniqueMemberName; + for (int i = 1;; i++) + { + uniqueMemberName = AppendToValidClrIdentifier(memberName, i.ToString(NumberFormatInfo.InvariantInfo)); + if (!contractCodeDomInfo.GetMemberNames().Contains(uniqueMemberName)) + { + memberName = uniqueMemberName; + break; + } + } + } + + contractCodeDomInfo.AddMemberName(memberName); + return memberName; + } + + private static void AddBaseMemberNames(ContractCodeDomInfo baseContractCodeDomInfo, ContractCodeDomInfo contractCodeDomInfo) + { + if (!baseContractCodeDomInfo.ReferencedTypeExists) + { + HashSet baseMemberNames = baseContractCodeDomInfo.GetMemberNames(); + HashSet memberNames = contractCodeDomInfo.GetMemberNames(); + foreach (string name in baseMemberNames) + { + memberNames.Add(name); + } + } + } + + private static string GetClrIdentifier(string identifier, string defaultIdentifier) + { + if (identifier.Length <= MaxIdentifierLength && CodeGenerator.IsValidLanguageIndependentIdentifier(identifier)) + return identifier; + + bool isStart = true; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < identifier.Length && builder.Length < MaxIdentifierLength; i++) + { + char c = identifier[i]; + if (IsValid(c)) + { + if (isStart && !IsValidStart(c)) + builder.Append('_'); + builder.Append(c); + isStart = false; + } + } + if (builder.Length == 0) + return defaultIdentifier; + + return builder.ToString(); + } + + internal static string GetClrTypeFullName(Type type) + { + return !type.IsGenericTypeDefinition && type.ContainsGenericParameters ? type.Namespace + "." + type.Name : type.FullName!; + } + + private static string AppendToValidClrIdentifier(string identifier, string appendString) + { + int availableLength = MaxIdentifierLength - identifier.Length; + int requiredLength = appendString.Length; + if (availableLength < requiredLength) + identifier = identifier.Substring(0, MaxIdentifierLength - requiredLength); + identifier += appendString; + return identifier; + } + + private string GetClrNamespace(DataContract dataContract, ContractCodeDomInfo contractCodeDomInfo) + { + string? clrNamespace = contractCodeDomInfo.ClrNamespace; + bool usesWildcardNamespace = false; + if (clrNamespace == null) + { + if (!Namespaces.TryGetValue(dataContract.XmlName.Namespace, out clrNamespace)) + { + if (Namespaces.TryGetValue(WildcardNamespaceMapping, out clrNamespace)) + { + usesWildcardNamespace = true; + } + else + { + clrNamespace = GetClrNamespace(dataContract.XmlName.Namespace); + if (ClrNamespaces.ContainsKey(clrNamespace)) + { + string uniqueNamespace; + for (int i = 1;; i++) + { + uniqueNamespace = ((clrNamespace.Length == 0) ? ImportGlobals.DefaultClrNamespace : clrNamespace) + i.ToString(NumberFormatInfo.InvariantInfo); + if (!ClrNamespaces.ContainsKey(uniqueNamespace)) + { + clrNamespace = uniqueNamespace; + break; + } + } + } + AddNamespacePair(dataContract.XmlName.Namespace, clrNamespace); + } + } + contractCodeDomInfo.ClrNamespace = clrNamespace; + contractCodeDomInfo.UsesWildcardNamespace = usesWildcardNamespace; + } + return clrNamespace; + } + + private void AddNamespacePair(string dataContractNamespace, string clrNamespace) + { + Namespaces.Add(dataContractNamespace, clrNamespace); + ClrNamespaces.Add(clrNamespace, dataContractNamespace); + } + + private static void AddImportStatement(string? clrNamespace, CodeNamespace? codeNamespace) + { + // We don't expect these to be null when passed in, but they are usually properties on larger classes which declare their types as nullable and we can't control, so we allow nullable parameters. + Debug.Assert(clrNamespace != null); + Debug.Assert(codeNamespace != null); + + if (clrNamespace == codeNamespace.Name) + return; + + CodeNamespaceImportCollection importCollection = codeNamespace.Imports; + foreach (CodeNamespaceImport import in importCollection) + { + if (import.Namespace == clrNamespace) + return; + } + + importCollection.Add(new CodeNamespaceImport(clrNamespace)); + } + + private static string GetClrNamespace(string? dataContractNamespace) + { + if (dataContractNamespace == null || dataContractNamespace.Length == 0) + return string.Empty; + + StringBuilder builder = new StringBuilder(); + if (Uri.TryCreate(dataContractNamespace, UriKind.RelativeOrAbsolute, out Uri? uri)) + { + Dictionary fragments = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (!uri.IsAbsoluteUri) + AddToNamespace(builder, uri.OriginalString, fragments); + else + { + string uriString = uri.AbsoluteUri; + if (uriString.StartsWith(ImportGlobals.DataContractXsdBaseNamespace, StringComparison.Ordinal)) + AddToNamespace(builder, uriString.Substring(ImportGlobals.DataContractXsdBaseNamespace.Length), fragments); + else + { + string host = uri.Host; + if (host != null) + AddToNamespace(builder, host, fragments); + string path = uri.PathAndQuery; + if (path != null) + AddToNamespace(builder, path, fragments); + } + } + } + + if (builder.Length == 0) + return string.Empty; + + int length = builder.Length; + if (builder[builder.Length - 1] == '.') + length--; + length = Math.Min(MaxIdentifierLength, length); + + return builder.ToString(0, length); + } + + private static void AddToNamespace(StringBuilder builder, string? fragment, Dictionary fragments) + { + if (fragment == null) + return; + bool isStart = true; + int fragmentOffset = builder.Length; + int fragmentLength = 0; + + for (int i = 0; i < fragment.Length && builder.Length < MaxIdentifierLength; i++) + { + char c = fragment[i]; + + if (IsValid(c)) + { + if (isStart && !IsValidStart(c)) + builder.Append('_'); + builder.Append(c); + fragmentLength++; + isStart = false; + } + else if ((c == '.' || c == '/' || c == ':') && (builder.Length == 1 + || (builder.Length > 1 && builder[builder.Length - 1] != '.'))) + { + AddNamespaceFragment(builder, fragmentOffset, fragmentLength, fragments); + builder.Append('.'); + fragmentOffset = builder.Length; + fragmentLength = 0; + isStart = true; + } + } + AddNamespaceFragment(builder, fragmentOffset, fragmentLength, fragments); + } + + private static void AddNamespaceFragment(StringBuilder builder, int fragmentOffset, + int fragmentLength, Dictionary fragments) + { + if (fragmentLength == 0) + return; + + string nsFragment = builder.ToString(fragmentOffset, fragmentLength); + if (fragments.ContainsKey(nsFragment)) + { + for (int i = 1;; i++) + { + string uniquifier = i.ToString(NumberFormatInfo.InvariantInfo); + string uniqueNsFragment = AppendToValidClrIdentifier(nsFragment, uniquifier); + if (!fragments.ContainsKey(uniqueNsFragment)) + { + builder.Append(uniquifier); + nsFragment = uniqueNsFragment; + break; + } + if (i == int.MaxValue) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.CannotComputeUniqueName, nsFragment))); + } + } + fragments.Add(nsFragment, null); + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal static bool IsObjectContract(DataContract? dataContract) + { + Dictionary previousCollectionTypes = new Dictionary(); + while (dataContract != null && dataContract.Is(DataContractType.CollectionDataContract)) + { + if (dataContract.OriginalUnderlyingType == null) + { + dataContract = dataContract.BaseContract; + continue; + } + + if (!previousCollectionTypes.ContainsKey(dataContract.OriginalUnderlyingType)) + { + previousCollectionTypes.Add(dataContract.OriginalUnderlyingType, dataContract.OriginalUnderlyingType); + dataContract = dataContract.BaseContract; + } + else + { + break; + } + } + + return dataContract != null && dataContract.Is(DataContractType.PrimitiveDataContract) && dataContract.UnderlyingType == typeof(object); + } + + private static bool IsValidStart(char c) + { + return (char.GetUnicodeCategory(c) != UnicodeCategory.DecimalDigitNumber); + } + + private static bool IsValid(char c) + { + UnicodeCategory uc = char.GetUnicodeCategory(c); + + // each char must be Lu, Ll, Lt, Lm, Lo, Nd, Mn, Mc, Pc + + switch (uc) + { + case UnicodeCategory.UppercaseLetter: // Lu + case UnicodeCategory.LowercaseLetter: // Ll + case UnicodeCategory.TitlecaseLetter: // Lt + case UnicodeCategory.ModifierLetter: // Lm + case UnicodeCategory.OtherLetter: // Lo + case UnicodeCategory.DecimalDigitNumber: // Nd + case UnicodeCategory.NonSpacingMark: // Mn + case UnicodeCategory.SpacingCombiningMark: // Mc + case UnicodeCategory.ConnectorPunctuation: // Pc + return true; + default: + return false; + } + } + + private CodeTypeReference CodeTypeIPropertyChange + { + get { return GetCodeTypeReference(typeof(System.ComponentModel.INotifyPropertyChanged)); } + } + + private static CodeThisReferenceExpression ThisReference + { + get { return new CodeThisReferenceExpression(); } + } + + private static CodePrimitiveExpression NullReference + { + get { return new CodePrimitiveExpression(null); } + } + + private CodeParameterDeclarationExpression SerializationInfoParameter + { + get { return new CodeParameterDeclarationExpression(GetCodeTypeReference(typeof(SerializationInfo)), ImportGlobals.SerializationInfoFieldName); } + } + + private CodeParameterDeclarationExpression StreamingContextParameter + { + get { return new CodeParameterDeclarationExpression(GetCodeTypeReference(typeof(StreamingContext)), ImportGlobals.ContextFieldName); } + } + + private CodeAttributeDeclaration SerializableAttribute + { + get { return new CodeAttributeDeclaration(GetCodeTypeReference(typeof(SerializableAttribute))); } + } + + private CodeMemberProperty NodeArrayProperty + { + get + { + return CreateProperty(GetCodeTypeReference(ImportGlobals.TypeOfXmlNodeArray), ImportGlobals.NodeArrayPropertyName, ImportGlobals.NodeArrayFieldName, false/*isValueType*/); + } + } + + private CodeMemberField NodeArrayField + { + get + { + CodeMemberField nodeArrayField = new CodeMemberField(); + nodeArrayField.Type = GetCodeTypeReference(ImportGlobals.TypeOfXmlNodeArray); + nodeArrayField.Name = ImportGlobals.NodeArrayFieldName; + nodeArrayField.Attributes = MemberAttributes.Private; + return nodeArrayField; + } + } + + private CodeMemberMethod ReadXmlMethod + { + get + { + CodeMemberMethod readXmlMethod = new CodeMemberMethod(); + readXmlMethod.Name = "ReadXml"; + CodeParameterDeclarationExpression readerArg = new CodeParameterDeclarationExpression(typeof(XmlReader), "reader"); + readXmlMethod.Parameters.Add(readerArg); + readXmlMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + readXmlMethod.ImplementationTypes.Add(typeof(IXmlSerializable)); + CodeAssignStatement setNode = new CodeAssignStatement(); + setNode.Left = new CodeFieldReferenceExpression(ThisReference, ImportGlobals.NodeArrayFieldName); + setNode.Right = new CodeMethodInvokeExpression( + new CodeTypeReferenceExpression(GetCodeTypeReference(typeof(XmlSerializableServices))), + nameof(XmlSerializableServices.ReadNodes), + new CodeArgumentReferenceExpression(readerArg.Name) + ); + readXmlMethod.Statements.Add(setNode); + return readXmlMethod; + } + } + + private CodeMemberMethod WriteXmlMethod + { + get + { + CodeMemberMethod writeXmlMethod = new CodeMemberMethod(); + writeXmlMethod.Name = "WriteXml"; + CodeParameterDeclarationExpression writerArg = new CodeParameterDeclarationExpression(typeof(XmlWriter), "writer"); + writeXmlMethod.Parameters.Add(writerArg); + writeXmlMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + writeXmlMethod.ImplementationTypes.Add(typeof(IXmlSerializable)); + writeXmlMethod.Statements.Add( + new CodeMethodInvokeExpression( + new CodeTypeReferenceExpression(GetCodeTypeReference(typeof(XmlSerializableServices))), + nameof(XmlSerializableServices.WriteNodes), + new CodeArgumentReferenceExpression(writerArg.Name), + new CodePropertyReferenceExpression(ThisReference, ImportGlobals.NodeArrayPropertyName) + ) + ); + return writeXmlMethod; + } + } + + private CodeMemberMethod GetSchemaMethod + { + get + { + CodeMemberMethod getSchemaMethod = new CodeMemberMethod(); + getSchemaMethod.Name = "GetSchema"; + getSchemaMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + getSchemaMethod.ImplementationTypes.Add(typeof(IXmlSerializable)); + getSchemaMethod.ReturnType = GetCodeTypeReference(typeof(XmlSchema)); + getSchemaMethod.Statements.Add(new CodeMethodReturnStatement(NullReference)); + return getSchemaMethod; + } + } + + private CodeMemberMethod GetSchemaStaticMethod + { + get + { + CodeMemberMethod getSchemaStaticMethod = new CodeMemberMethod(); + getSchemaStaticMethod.Name = ImportGlobals.ExportSchemaMethod; + getSchemaStaticMethod.ReturnType = GetCodeTypeReference(ImportGlobals.TypeOfXmlQualifiedName); + CodeParameterDeclarationExpression paramDeclaration = new CodeParameterDeclarationExpression(typeof(XmlSchemaSet), "schemas"); + getSchemaStaticMethod.Parameters.Add(paramDeclaration); + getSchemaStaticMethod.Attributes = MemberAttributes.Static | MemberAttributes.Public; + getSchemaStaticMethod.Statements.Add( + new CodeMethodInvokeExpression( + new CodeTypeReferenceExpression(GetCodeTypeReference(typeof(XmlSerializableServices))), + nameof(XmlSerializableServices.AddDefaultSchema), + new CodeArgumentReferenceExpression(paramDeclaration.Name), + new CodeFieldReferenceExpression(null, TypeNameFieldName) + ) + ); + getSchemaStaticMethod.Statements.Add( + new CodeMethodReturnStatement( + new CodeFieldReferenceExpression(null, TypeNameFieldName) + ) + ); + return getSchemaStaticMethod; + } + } + + private CodeConstructor ISerializableBaseConstructor + { + get + { + CodeConstructor baseConstructor = new CodeConstructor(); + baseConstructor.Attributes = MemberAttributes.Public; + baseConstructor.Parameters.Add(SerializationInfoParameter); + baseConstructor.Parameters.Add(StreamingContextParameter); + CodeAssignStatement setObjectData = new CodeAssignStatement(); + setObjectData.Left = new CodePropertyReferenceExpression(ThisReference, ImportGlobals.SerializationInfoFieldName); + setObjectData.Right = new CodeArgumentReferenceExpression(ImportGlobals.SerializationInfoFieldName); + baseConstructor.Statements.Add(setObjectData); + // Special-cased check for vb here since CodeGeneratorOptions does not provide information indicating that VB cannot initialize event member + if (EnableDataBinding && SupportsDeclareEvents && string.CompareOrdinal(FileExtension, "vb") != 0) + { + baseConstructor.Statements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(ThisReference, PropertyChangedEvent.Name), NullReference)); + } + return baseConstructor; + } + } + + private CodeConstructor ISerializableDerivedConstructor + { + get + { + CodeConstructor derivedConstructor = new CodeConstructor(); + derivedConstructor.Attributes = MemberAttributes.Public; + derivedConstructor.Parameters.Add(SerializationInfoParameter); + derivedConstructor.Parameters.Add(StreamingContextParameter); + derivedConstructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(ImportGlobals.SerializationInfoFieldName)); + derivedConstructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(ImportGlobals.ContextFieldName)); + return derivedConstructor; + } + } + + private CodeMemberField SerializationInfoField + { + get + { + CodeMemberField serializationInfoField = new CodeMemberField(); + serializationInfoField.Type = GetCodeTypeReference(typeof(SerializationInfo)); + serializationInfoField.Name = ImportGlobals.SerializationInfoFieldName; + serializationInfoField.Attributes = MemberAttributes.Private; + return serializationInfoField; + } + } + + private CodeMemberProperty SerializationInfoProperty + { + get + { + return CreateProperty(GetCodeTypeReference(typeof(SerializationInfo)), ImportGlobals.SerializationInfoPropertyName, ImportGlobals.SerializationInfoFieldName, false/*isValueType*/); + } + } + + private CodeMemberMethod GetObjectDataMethod + { + get + { + CodeMemberMethod getObjectDataMethod = new CodeMemberMethod(); + getObjectDataMethod.Name = ImportGlobals.GetObjectDataMethodName; + getObjectDataMethod.Parameters.Add(SerializationInfoParameter); + getObjectDataMethod.Parameters.Add(StreamingContextParameter); + getObjectDataMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; + getObjectDataMethod.ImplementationTypes.Add(typeof(ISerializable)); + + // Generates: if (this.SerializationInfo == null) return; + CodeConditionStatement returnIfNull = new CodeConditionStatement(); + returnIfNull.Condition = new CodeBinaryOperatorExpression( + new CodePropertyReferenceExpression(ThisReference, ImportGlobals.SerializationInfoPropertyName), + CodeBinaryOperatorType.IdentityEquality, + NullReference); + returnIfNull.TrueStatements.Add(new CodeMethodReturnStatement()); + + // Generates: SerializationInfoEnumerator enumerator = this.SerializationInfo.GetEnumerator(); + CodeVariableDeclarationStatement getEnumerator = new CodeVariableDeclarationStatement(); + getEnumerator.Type = GetCodeTypeReference(typeof(SerializationInfoEnumerator)); + getEnumerator.Name = ImportGlobals.EnumeratorFieldName; + getEnumerator.InitExpression = new CodeMethodInvokeExpression( + new CodePropertyReferenceExpression(ThisReference, ImportGlobals.SerializationInfoPropertyName), + ImportGlobals.GetEnumeratorMethodName); + + //Generates: SerializationEntry entry = enumerator.Current; + CodeVariableDeclarationStatement getCurrent = new CodeVariableDeclarationStatement(); + getCurrent.Type = GetCodeTypeReference(typeof(SerializationEntry)); + getCurrent.Name = ImportGlobals.SerializationEntryFieldName; + getCurrent.InitExpression = new CodePropertyReferenceExpression( + new CodeVariableReferenceExpression(ImportGlobals.EnumeratorFieldName), + ImportGlobals.CurrentPropertyName); + + //Generates: info.AddValue(entry.Name, entry.Value); + CodeExpressionStatement addValue = new CodeExpressionStatement(); + CodePropertyReferenceExpression getCurrentName = new CodePropertyReferenceExpression( + new CodeVariableReferenceExpression(ImportGlobals.SerializationEntryFieldName), + ImportGlobals.NameProperty); + CodePropertyReferenceExpression getCurrentValue = new CodePropertyReferenceExpression( + new CodeVariableReferenceExpression(ImportGlobals.SerializationEntryFieldName), + ImportGlobals.ValueProperty); + addValue.Expression = new CodeMethodInvokeExpression( + new CodeArgumentReferenceExpression(ImportGlobals.SerializationInfoFieldName), + ImportGlobals.AddValueMethodName, + new CodeExpression[] { getCurrentName, getCurrentValue }); + + //Generates: for (; enumerator.MoveNext(); ) + CodeIterationStatement loop = new CodeIterationStatement(); + loop.TestExpression = new CodeMethodInvokeExpression( + new CodeVariableReferenceExpression(ImportGlobals.EnumeratorFieldName), + ImportGlobals.MoveNextMethodName); + loop.InitStatement = loop.IncrementStatement = new CodeSnippetStatement(string.Empty); + loop.Statements.Add(getCurrent); + loop.Statements.Add(addValue); + + getObjectDataMethod.Statements.Add(returnIfNull); + getObjectDataMethod.Statements.Add(getEnumerator); + getObjectDataMethod.Statements.Add(loop); + + return getObjectDataMethod; + } + } + + private CodeMemberField ExtensionDataObjectField + { + get + { + CodeMemberField extensionDataObjectField = new CodeMemberField(); + extensionDataObjectField.Type = GetCodeTypeReference(typeof(ExtensionDataObject)); + extensionDataObjectField.Name = ImportGlobals.ExtensionDataObjectFieldName; + extensionDataObjectField.Attributes = MemberAttributes.Private; + return extensionDataObjectField; + } + } + + private CodeMemberProperty ExtensionDataObjectProperty + { + get + { + CodeMemberProperty extensionDataObjectProperty = new CodeMemberProperty(); + extensionDataObjectProperty.Type = GetCodeTypeReference(typeof(ExtensionDataObject)); + extensionDataObjectProperty.Name = ImportGlobals.ExtensionDataObjectPropertyName; + extensionDataObjectProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final; + extensionDataObjectProperty.ImplementationTypes.Add(typeof(IExtensibleDataObject)); + + CodeMethodReturnStatement propertyGet = new CodeMethodReturnStatement(); + propertyGet.Expression = new CodeFieldReferenceExpression(ThisReference, ImportGlobals.ExtensionDataObjectFieldName); + extensionDataObjectProperty.GetStatements.Add(propertyGet); + + CodeAssignStatement propertySet = new CodeAssignStatement(); + propertySet.Left = new CodeFieldReferenceExpression(ThisReference, ImportGlobals.ExtensionDataObjectFieldName); + propertySet.Right = new CodePropertySetValueReferenceExpression(); + extensionDataObjectProperty.SetStatements.Add(propertySet); + + return extensionDataObjectProperty; + } + } + + private CodeMemberMethod RaisePropertyChangedEventMethod + { + get + { + CodeMemberMethod raisePropertyChangedEventMethod = new CodeMemberMethod(); + raisePropertyChangedEventMethod.Name = "RaisePropertyChanged"; + raisePropertyChangedEventMethod.Attributes = MemberAttributes.Final; + CodeArgumentReferenceExpression propertyName = new CodeArgumentReferenceExpression("propertyName"); + raisePropertyChangedEventMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), propertyName.ParameterName)); + CodeVariableReferenceExpression propertyChanged = new CodeVariableReferenceExpression("propertyChanged"); + raisePropertyChangedEventMethod.Statements.Add(new CodeVariableDeclarationStatement(typeof(PropertyChangedEventHandler), propertyChanged.VariableName, new CodeEventReferenceExpression(ThisReference, PropertyChangedEvent.Name))); + CodeConditionStatement ifStatement = new CodeConditionStatement(new CodeBinaryOperatorExpression(propertyChanged, CodeBinaryOperatorType.IdentityInequality, NullReference)); + raisePropertyChangedEventMethod.Statements.Add(ifStatement); + ifStatement.TrueStatements.Add(new CodeDelegateInvokeExpression(propertyChanged, ThisReference, new CodeObjectCreateExpression(typeof(PropertyChangedEventArgs), propertyName))); + return raisePropertyChangedEventMethod; + } + } + + private CodeMemberEvent PropertyChangedEvent + { + get + { + CodeMemberEvent propertyChangedEvent = new CodeMemberEvent(); + propertyChangedEvent.Attributes = MemberAttributes.Public; + propertyChangedEvent.Name = "PropertyChanged"; + propertyChangedEvent.Type = GetCodeTypeReference(typeof(PropertyChangedEventHandler)); + propertyChangedEvent.ImplementationTypes.Add(typeof(INotifyPropertyChanged)); + return propertyChangedEvent; + } + } + + private CodeMemberProperty CreateProperty(CodeTypeReference type, string propertyName, string fieldName, bool isValueType) + { + return CreateProperty(type, propertyName, fieldName, isValueType, EnableDataBinding && SupportsDeclareEvents); + } + + private CodeMemberProperty CreateProperty(CodeTypeReference type, string propertyName, string fieldName, bool isValueType, bool raisePropertyChanged) + { + CodeMemberProperty property = new CodeMemberProperty(); + property.Type = type; + property.Name = propertyName; + property.Attributes = MemberAttributes.Final; + if (GenerateInternalTypes) + property.Attributes |= MemberAttributes.Assembly; + else + property.Attributes |= MemberAttributes.Public; + + CodeMethodReturnStatement propertyGet = new CodeMethodReturnStatement(); + propertyGet.Expression = new CodeFieldReferenceExpression(ThisReference, fieldName); + property.GetStatements.Add(propertyGet); + + CodeAssignStatement propertySet = new CodeAssignStatement(); + propertySet.Left = new CodeFieldReferenceExpression(ThisReference, fieldName); + propertySet.Right = new CodePropertySetValueReferenceExpression(); + if (raisePropertyChanged) + { + CodeConditionStatement ifStatement = new CodeConditionStatement(); + CodeExpression left = new CodeFieldReferenceExpression(ThisReference, fieldName); + CodeExpression right = new CodePropertySetValueReferenceExpression(); + if (!isValueType) + { + left = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(object)), + "ReferenceEquals", new CodeExpression[] { left, right }); + } + else + { + left = new CodeMethodInvokeExpression(left, "Equals", new CodeExpression[] { right }); + } + right = new CodePrimitiveExpression(true); + ifStatement.Condition = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityInequality, right); + ifStatement.TrueStatements.Add(propertySet); + ifStatement.TrueStatements.Add(new CodeMethodInvokeExpression(ThisReference, RaisePropertyChangedEventMethod.Name, new CodePrimitiveExpression(propertyName))); + property.SetStatements.Add(ifStatement); + } + else + property.SetStatements.Add(propertySet); + return property; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ContractCodeDomInfo.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ContractCodeDomInfo.cs new file mode 100644 index 0000000000000..809978d33b29b --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ContractCodeDomInfo.cs @@ -0,0 +1,64 @@ +// 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.Xml; +using System.Xml.Schema; + +using ExceptionUtil = System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility; + +namespace System.Runtime.Serialization +{ + internal sealed class ContractCodeDomInfo + { + private string? _clrNamespace; + // NOTE TODO smolloy - This was a Dictionary previously, so adding a duplicate entry would throw an exception. + // HashSet does not allow duplicates either, but it just returns false instead of throwing. I think it's safe to not + // throw in that case here, so long as we don't add duplicates. It's just a string list. + private HashSet? _memberNames; + + internal string? ClrNamespace + { + get { return ReferencedTypeExists ? null : _clrNamespace; } + set + { + if (ReferencedTypeExists) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotSetNamespaceForReferencedType, TypeReference?.BaseType))); + else + _clrNamespace = value; + } + } + + internal CodeNamespace? CodeNamespace { get; set; } + + internal bool IsProcessed { get; set; } + + internal bool ReferencedTypeExists { get; set; } + + internal CodeTypeDeclaration? TypeDeclaration { get; set; } + + internal CodeTypeReference? TypeReference { get; set; } + + internal bool UsesWildcardNamespace { get; set; } + + internal HashSet GetMemberNames() + { + if (ReferencedTypeExists) + throw ExceptionUtil.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotSetMembersForReferencedType, TypeReference?.BaseType))); + else + return _memberNames ??= new HashSet(StringComparer.OrdinalIgnoreCase); + } + + internal bool AddMemberName(string memberName) + { + HashSet names = GetMemberNames(); + + // If the name already exists, 4.8 threw an exception because the backing collection type was Dictionary + Debug.Assert(!names.Contains(memberName)); + return names.Add(memberName); + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/DiagnosticUtility.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/DiagnosticUtility.cs new file mode 100644 index 0000000000000..46b0dde85c686 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/DiagnosticUtility.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Threading; + +namespace System.Runtime.Serialization +{ + internal static class Fx + { + public static bool IsFatal(Exception exception) + { + while (exception != null) + { + // NetFx checked for FatalException and FatalInternalException as well, which were ServiceModel constructs. + if ((exception is OutOfMemoryException && !(exception is InsufficientMemoryException)) || + exception is ThreadAbortException) + { + return true; + } + + // These exceptions aren't themselves fatal, but since the CLR uses them to wrap other exceptions, + // we want to check to see whether they've been used to wrap a fatal exception. If so, then they + // count as fatal. + if (exception is TypeInitializationException || + exception is TargetInvocationException) + { + exception = exception.InnerException!; + } + else if (exception is AggregateException) + { + // AggregateExceptions have a collection of inner exceptions, which may themselves be other + // wrapping exceptions (including nested AggregateExceptions). Recursively walk this + // hierarchy. The (singular) InnerException is included in the collection. + var innerExceptions = ((AggregateException)exception).InnerExceptions; + foreach (Exception innerException in innerExceptions) + { + if (IsFatal(innerException)) + { + return true; + } + } + + break; + } + else + { + break; + } + } + + return false; + } + } + + internal static class DiagnosticUtility + { + [Conditional("DEBUG")] + [DoesNotReturn] + public static void DebugAssert(string message) + { + DebugAssert(false, message); + } + + [Conditional("DEBUG")] + public static void DebugAssert([DoesNotReturnIf(false)] bool condition, string message) + { + Debug.Assert(condition, message); + } + + internal static class ExceptionUtility + { + public static Exception ThrowHelperArgumentNull(string message) + { + return new ArgumentNullException(message); + } + + public static Exception ThrowHelperError(Exception e) + { + return e; + } + + public static Exception ThrowHelperArgument(string message) + { + return new ArgumentException(message); + } + + internal static Exception ThrowHelperFatal(string message, Exception innerException) + { + return ThrowHelperError(new Exception(message, innerException)); + } + internal static Exception ThrowHelperCallback(Exception e) + { + return ThrowHelperError(e); + } + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ISerializationCodeDomSurrogateProvider.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ISerializationCodeDomSurrogateProvider.cs new file mode 100644 index 0000000000000..a69a18711d0b4 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ISerializationCodeDomSurrogateProvider.cs @@ -0,0 +1,24 @@ +// 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.CodeDom.Compiler; +using System.Collections.Generic; + +namespace System.Runtime.Serialization +{ + /// + /// Represents a DataContract surrogate provider that is capable of modifying generated type code using . + /// + public interface ISerializationCodeDomSurrogateProvider + { + /// + /// Processes the type that has been generated from the imported schema. + /// + /// A to process that represents the type declaration generated during schema import. + /// The that contains the other code generated during schema import. + /// A that contains the processed type. + CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit); + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportGlobals.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportGlobals.cs new file mode 100644 index 0000000000000..59392aed8b04f --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportGlobals.cs @@ -0,0 +1,156 @@ +// 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.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +namespace System.Runtime.Serialization +{ + internal static class ImportGlobals + { + internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the " + + "required types are preserved."; + + public const string ActualTypeLocalName = "ActualType"; + public const string ActualTypeNameAttribute = "Name"; + public const string ActualTypeNamespaceAttribute = "Namespace"; + public const string AddValueMethodName = "AddValue"; + public const string AnyTypeLocalName = "anyType"; + public const string ArrayPrefix = "ArrayOf"; + public const string ClrNamespaceProperty = "ClrNamespace"; + public const string CollectionsNamespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"; + public const string ContextFieldName = "context"; + public const string CurrentPropertyName = "Current"; + public const string DataContractXsdBaseNamespace = "http://schemas.datacontract.org/2004/07/"; + public const string DefaultClrNamespace = "GeneratedNamespace"; + public const bool DefaultEmitDefaultValue = true; + public const string DefaultGeneratedMember = "GeneratedMember"; + public const string DefaultFieldSuffix = "Field"; + public const bool DefaultIsReference = false; + public const bool DefaultIsRequired = false; + public const string DefaultMemberSuffix = "Member"; + public const int DefaultOrder = 0; + public const string DefaultTypeName = "GeneratedType"; + public const string DefaultValueLocalName = "DefaultValue"; + public const string EmitDefaultValueAttribute = "EmitDefaultValue"; + public const string EmitDefaultValueProperty = "EmitDefaultValue"; + public const string EnumerationValueLocalName = "EnumerationValue"; + public const string EnumeratorFieldName = "enumerator"; + public const string ExportSchemaMethod = "ExportSchema"; + public const string ExtensionDataObjectFieldName = "extensionDataField"; + public const string ExtensionDataObjectPropertyName = "ExtensionData"; + public const string False = "false"; + public const string GenericNameAttribute = "Name"; + public const string GenericNamespaceAttribute = "Namespace"; + public const string GenericParameterLocalName = "GenericParameter"; + public const string GenericParameterNestedLevelAttribute = "NestedLevel"; + public const string GenericTypeLocalName = "GenericType"; + public const string GetEnumeratorMethodName = "GetEnumerator"; + public const string GetObjectDataMethodName = "GetObjectData"; + public const string IdLocalName = "Id"; + public const string IntLocalName = "int"; + public const string IsAnyProperty = "IsAny"; + public const string IsDictionaryLocalName = "IsDictionary"; + public const string ISerializableFactoryTypeLocalName = "FactoryType"; + public const string IsReferenceProperty = "IsReference"; + public const string IsRequiredProperty = "IsRequired"; + public const string IsValueTypeLocalName = "IsValueType"; + public const string ItemNameProperty = "ItemName"; + public const string KeyLocalName = "Key"; + public const string KeyNameProperty = "KeyName"; + public const string MoveNextMethodName = "MoveNext"; + public const string NameProperty = "Name"; + public const string NamespaceProperty = "Namespace"; + public const string NodeArrayFieldName = "nodesField"; + public const string NodeArrayPropertyName = "Nodes"; + public const string OccursUnbounded = "unbounded"; + public const string OrderProperty = "Order"; + public const string RefLocalName = "Ref"; + public const string SchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + public const string SchemaLocalName = "schema"; + public const string SchemaNamespace = "http://www.w3.org/2001/XMLSchema"; + public const string SerializationEntryFieldName = "entry"; + public const string SerializationInfoFieldName = "info"; + public const string SerializationInfoPropertyName = "SerializationInfo"; + public const string SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/"; + public const string SerPrefixForSchema = "ser"; + public const string StringLocalName = "string"; + public const string SurrogateDataLocalName = "Surrogate"; + public const string TnsPrefix = "tns"; + public const string True = "true"; + public const string ValueLocalName = "Value"; + public const string ValueNameProperty = "ValueName"; + public const string ValueProperty = "Value"; + + private static Uri? s_dataContractXsdBaseNamespaceUri; + internal static Uri DataContractXsdBaseNamespaceUri => s_dataContractXsdBaseNamespaceUri ??= new Uri(DataContractXsdBaseNamespace); + + private static XmlQualifiedName? s_idQualifiedName; + internal static XmlQualifiedName IdQualifiedName => s_idQualifiedName ??= new XmlQualifiedName(ImportGlobals.IdLocalName, ImportGlobals.SerializationNamespace); + + private static XmlQualifiedName? s_refQualifiedName; + internal static XmlQualifiedName RefQualifiedName => s_refQualifiedName ??= new XmlQualifiedName(ImportGlobals.RefLocalName, ImportGlobals.SerializationNamespace); + + private static Type? s_typeOfXmlElement; + internal static Type TypeOfXmlElement => s_typeOfXmlElement ??= typeof(XmlElement); + + private static Type? s_typeOfXmlNodeArray; + internal static Type TypeOfXmlNodeArray => s_typeOfXmlNodeArray ??= typeof(XmlNode[]); + + private static Type? s_typeOfXmlQualifiedName; + internal static Type TypeOfXmlQualifiedName => s_typeOfXmlQualifiedName ??= typeof(XmlQualifiedName); + + private static Type? s_typeOfXmlSchemaProviderAttribute; + internal static Type TypeOfXmlSchemaProviderAttribute => s_typeOfXmlSchemaProviderAttribute ??= typeof(XmlSchemaProviderAttribute); + + private static Type? s_typeOfXmlSchemaType; + internal static Type TypeOfXmlSchemaType => s_typeOfXmlSchemaType ??= typeof(XmlSchemaType); + + + public const string SerializationSchema = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportOptions.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportOptions.cs new file mode 100644 index 0000000000000..0e8fab9d0df5b --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/ImportOptions.cs @@ -0,0 +1,72 @@ +// 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.CodeDom.Compiler; +using System.Collections.Generic; + +namespace System.Runtime.Serialization +{ + /// + /// Represents the options that can be set on an . + /// + /// + /// The is used to generate code from XML schema using the .NET CodeDOM. To generate an XML schema from an assembly, use the . + /// + public class ImportOptions + { + private ICollection? _referencedTypes; + private ICollection? _referencedCollectionTypes; + private IDictionary? _namespaces; + + /// + /// Gets or sets a instance that provides the means to check whether particular options for a target language are supported. + /// + public CodeDomProvider? CodeProvider { get; set; } + + /// + /// Gets or sets a value that specifies whether types in generated code should implement the interface. + /// + public bool EnableDataBinding { get; set; } + + /// + /// Gets or sets a data contract surrogate provider that can be used to modify the code generated during an import operation. + /// + /// + /// The interface type for this option is ISerializationSurrogateProvider, but to take full advantage of the imported code modification + /// abilities, using an ISerializationSurrogateProvider2 that also implements is recommended. + /// + public ISerializationSurrogateProvider? DataContractSurrogate { get; set; } + + /// + /// Gets or sets a value that specifies whether generated code will be marked internal or public. + /// + public bool GenerateInternal { get; set; } + + /// + /// Gets or sets a value that specifies whether generated data contract classes will be marked with the attribute in addition to the attribute. + /// + public bool GenerateSerializable { get; set; } + + /// + /// Gets or sets a value that determines whether all XML schema types, even those that do not conform to a data contract schema, will be imported. + /// + public bool ImportXmlType { get; set; } + + /// + /// Gets a dictionary that contains the mapping of data contract namespaces to the CLR namespaces that must be used to generate code during an import operation. + /// + public IDictionary Namespaces => _namespaces ??= new Dictionary(); + + /// + /// Gets a collection of types that represents data contract collections that should be referenced when generating code for collections, such as lists or dictionaries of items. + /// + public ICollection ReferencedCollectionTypes => _referencedCollectionTypes ??= new List(); + + /// + /// Gets a containing types referenced in generated code. + /// + public ICollection ReferencedTypes => _referencedTypes ??= new List(); + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/SchemaImportHelper.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/SchemaImportHelper.cs new file mode 100644 index 0000000000000..a268f8ef79266 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/SchemaImportHelper.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.DataContracts; +using System.Xml; + +namespace System.Runtime.Serialization +{ + internal enum DataContractType + { + ClassDataContract, + CollectionDataContract, + EnumDataContract, + PrimitiveDataContract, + XmlDataContract, + Unknown = -1 + } + + internal static class DataContractExtensions + { + internal static DataContractType GetContractType(this DataContract dataContract) => dataContract.ContractType switch + { + "ClassDataContract" => DataContractType.ClassDataContract, + "CollectionDataContract" => DataContractType.CollectionDataContract, + "EnumDataContract" => DataContractType.EnumDataContract, + "PrimitiveDataContract" => DataContractType.PrimitiveDataContract, + "XmlDataContract" => DataContractType.XmlDataContract, + _ => DataContractType.Unknown + }; + + internal static bool Is(this DataContract dataContract, DataContractType dcType) + { + return (dataContract.GetContractType() == dcType); + } + + internal static DataContract? As(this DataContract dataContract, DataContractType dcType) + { + if (dataContract.GetContractType() == dcType) + return dataContract; + return null; + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + internal static bool IsItemTypeNullable(this DataContract collectionDataContract) + { + if (collectionDataContract.GetContractType() == DataContractType.CollectionDataContract) + { + // ItemContract - aka BaseContract - is never null for CollectionDataContract + return SchemaImportHelper.IsTypeNullable(collectionDataContract.BaseContract!.UnderlyingType); + } + + return false; + } + } + + internal static class SchemaImportHelper + { + internal static bool IsTypeNullable(Type type) + { + return !type.IsValueType || + (type.IsGenericType && + type.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + + internal static string GetCollectionNamespace(string elementNs) + { + return IsBuiltInNamespace(elementNs) ? ImportGlobals.CollectionsNamespace : elementNs; + } + + internal static string GetDataContractNamespaceFromUri(string uriString) + { + return uriString.StartsWith(ImportGlobals.DataContractXsdBaseNamespace, StringComparison.Ordinal) ? uriString.Substring(ImportGlobals.DataContractXsdBaseNamespace.Length) : uriString; + } + + internal static string GetDefaultXmlNamespace(string? clrNs) + { + if (clrNs == null) clrNs = string.Empty; + return new Uri(ImportGlobals.DataContractXsdBaseNamespaceUri, clrNs).AbsoluteUri; + } + + internal static bool IsBuiltInNamespace(string ns) + { + return (ns == ImportGlobals.SchemaNamespace || ns == ImportGlobals.SerializationNamespace); + } + + // This should match the behavior of DataContract.EncodeLocalName + internal static string EncodeLocalName(string localName) + { + if (IsAsciiLocalName(localName)) + return localName; + + if (IsValidNCName(localName)) + return localName; + + return XmlConvert.EncodeLocalName(localName); + } + + private static bool IsAsciiLocalName(string localName) + { + if (localName.Length == 0) + return false; + if (!char.IsAsciiLetter(localName[0])) + return false; + for (int i = 1; i < localName.Length; i++) + { + char ch = localName[i]; + if (!char.IsAsciiLetterOrDigit(ch)) + return false; + } + return true; + } + + private static bool IsValidNCName(string name) + { + try + { + XmlConvert.VerifyNCName(name); + return true; + } + catch (XmlException) + { + return false; + } + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/XsdDataContractImporter.cs b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/XsdDataContractImporter.cs new file mode 100644 index 0000000000000..00f54a684a059 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/System/Runtime/Serialization/Schema/XsdDataContractImporter.cs @@ -0,0 +1,406 @@ +// 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 +{ + /// + /// Allows the transformation of a set of XML schema files (.xsd) into common language runtime (CLR) types. + /// + /// + /// Use the if you are creating a Web service that must interoperate with an existing + /// Web service, or to create data contract types from XML schemas. 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 namespace. + /// + /// Conversely, use the 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, transforms a set of CLR types into a set of XML schemas. + /// + 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(); + 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. + + /// + /// Initializes a new instance of the class. + /// + public XsdDataContractImporter() + { + } + + /// + /// Initializes a new instance of the class with the that will be used to generate CLR code. + /// + /// The that will be used to store the code. + public XsdDataContractImporter(CodeCompileUnit codeCompileUnit) + { + _codeCompileUnit = codeCompileUnit; + } + + /// + /// Gets or sets an that contains settable options for the import operation. + /// + public ImportOptions? Options { get; set; } + + /// + /// Gets a used for storing the CLR types generated. + /// + public CodeCompileUnit CodeCompileUnit => _codeCompileUnit ??= new CodeCompileUnit(); + + private DataContractSet DataContractSet + { + get + { + if (_dataContractSet == null) + { + _dataContractSet = Options == null ? new DataContractSet(null, null, null) : + new DataContractSet(Options.DataContractSurrogate, Options.ReferencedTypes, Options.ReferencedCollectionTypes); + } + return _dataContractSet; + } + } + + /// + /// Transforms the specified set of XML schemas contained in an into a . + /// + /// A that contains the schema representations to generate CLR types for. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public void Import(XmlSchemaSet schemas) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + InternalImport(schemas, null, null); + } + + /// + /// Transforms the specified set of schema types contained in an into CLR types generated into a . + /// + /// A that contains the schema representations. + /// A (of ) that represents the set of schema types to import. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public void Import(XmlSchemaSet schemas, ICollection 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); + } + + /// + /// Transforms the specified XML schema type contained in an into a . + /// + /// A that contains the schema representations. + /// A that represents a specific schema type to import. + [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); + } + + /// + /// Transforms the specified schema element in the set of specified XML schemas into a and + /// returns an that represents the data contract name for the specified element. + /// + /// An that contains the schemas to transform. + /// An that represents the specific schema element to transform. + /// An that represents the specified element. + [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? elementNames = InternalImport(schemas, s_emptyTypeNameArray, SingleElementArray); + Debug.Assert(elementNames != null && elementNames.Count > 0); + return elementNames[0]; + } + + /// + /// Gets a value that indicates whether the schemas contained in an can be transformed into a . + /// + /// A that contains the schemas to transform. + /// true if the schemas can be transformed to data contract types; otherwise, false. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public bool CanImport(XmlSchemaSet schemas) + { + if (schemas == null) + throw ExceptionUtil.ThrowHelperError(new ArgumentNullException(nameof(schemas))); + + return InternalCanImport(schemas, null, null); + } + + /// + /// Gets a value that indicates whether the specified set of types contained in an can be transformed into CLR types generated into a . + /// + /// A that contains the schemas to transform. + /// An of that represents the set of schema types to import. + /// true if the schemas can be transformed; otherwise, false. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public bool CanImport(XmlSchemaSet schemas, ICollection 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); + } + + /// + /// Gets a value that indicates whether the schemas contained in an can be transformed into a . + /// + /// A that contains the schema representations. + /// An that specifies the names of the schema types that need to be imported from the . + /// true if the schemas can be transformed to data contract types; otherwise, false. + [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); + } + + /// + /// Gets a value that indicates whether a specific schema element contained in an can be imported. + /// + /// An to import. + /// A specific to check in the set of schemas. + /// true if the element can be imported; otherwise, false. + [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); + } + + /// + /// Returns a to the CLR type generated for the schema type with the specified . + /// + /// The that specifies the schema type to look up. + /// A reference to the CLR type generated for the schema type with the typeName specified. + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public CodeTypeReference GetCodeTypeReference(XmlQualifiedName typeName) + { + DataContract dataContract = FindDataContract(typeName); + CodeExporter codeExporter = new CodeExporter(DataContractSet, Options, CodeCompileUnit); + return codeExporter.GetCodeTypeReference(dataContract); + } + + /// + /// Returns a for the specified XML qualified element and schema element. + /// + /// An that specifies the XML qualified name of the schema type to look up. + /// An that specifies an element in an XML schema. + /// A that represents the type that was generated for the specified schema type. + [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; + } + + /// + /// Returns a list of objects that represents the known types generated when generating code for the specified schema type. + /// + /// An that represents the schema type to look up known types for. + /// A collection of type . + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + public ICollection? 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 + { + if (_singleTypeNameArray == null) + _singleTypeNameArray = new XmlQualifiedName[1]; + return _singleTypeNameArray; + } + } + + private XmlSchemaElement[] SingleElementArray + { + get + { + if (_singleElementArray == null) + _singleElementArray = new XmlSchemaElement[1]; + return _singleElementArray; + } + } + + [RequiresUnreferencedCode(ImportGlobals.SerializerTrimmerWarning)] + private IList? InternalImport(XmlSchemaSet schemas, ICollection? typeNames, ICollection? elements) + { + DataContractSet? oldValue = (_dataContractSet == null) ? null : new DataContractSet(_dataContractSet); + IList? 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? typeNames, ICollection? 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; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System.Runtime.Serialization.Schema.Tests.csproj b/src/libraries/System.Runtime.Serialization.Schema/tests/System.Runtime.Serialization.Schema.Tests.csproj new file mode 100644 index 0000000000000..6515ccf393647 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System.Runtime.Serialization.Schema.Tests.csproj @@ -0,0 +1,19 @@ + + + $(NetCoreAppCurrent) + true + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/DataContracts.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/DataContracts.cs new file mode 100644 index 0000000000000..77d12cf9e6d4c --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/DataContracts.cs @@ -0,0 +1,160 @@ +using System.Runtime.Serialization; +using System.Xml.Serialization; + +[assembly: ContractNamespace("http://special1.tempuri.org", ClrNamespace = "System.Runtime.Serialization.Schema.Tests.DataContracts")] + +namespace System.Runtime.Serialization.Schema.Tests.DataContracts +{ + [DataContract(Namespace = "http://basic")] + public class Point + { + [DataMember] + public int X = 42; + [DataMember] + public int Y = 43; + } + + [DataContract(Namespace = "http://shapes")] + public class Circle + { + [DataMember] + public Point Center = new Point(); + [DataMember] + public int Radius = 5; + } + + [DataContract(Namespace = "http://shapes")] + public class Square + { + [DataMember] + public Point BottomLeft = new Point(); + [DataMember] + public int Side = 5; + } + + public struct NonAttributedPersonStruct + { + public string firstName; + public string lastName; + } + + public class NonAttributedPersonClass + { + public string firstName = "John"; + public string lastName = "Smith"; + + internal NonAttributedPersonClass() + { + } + } + + public class ExtendedSquare : Square + { + public string lineColor = "black"; + } + + [DataContract(Name = "AnotherValidType", Namespace = "http://schemas.datacontract.org/2004/07/barNs")] + public class AnotherValidType + { + [DataMember] + public string member; + } + + [DataContract(Name = "AnotherValidType", Namespace = "http://schemas.datacontract.org/2004/07/barNs")] + public class ConflictingAnotherValidType + { + [DataMember] + public string member; + } + + public class NonAttributedType + { + public NonAttributedSquare Length; + } + + public class NonAttributedSquare + { + public int Length; + } + + [DataContract(IsReference = true)] + public class RefType1 + { + } + + [DataContract] + public class NonRefType + { + } + + [Serializable] + public class ISerializableFormatClass : ISerializable + { + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + } + } + +#pragma warning disable CS0169, IDE0051, IDE1006 + #region SurrogateTests + [XmlRoot("XmlSerializerPersonElement")] + public class XmlSerializerPerson + { + public XmlSerializerPerson() { } + [XmlAttribute] + public string Name; + [XmlAttribute] + public int Age; + } + + [DataContract] + public class CircleContainer + { + [DataMember] + public SerializableCircle Circle { get { return null; } set { } } + [DataMember] + SerializableCircle[] circles; + } + + [Serializable] + public class SerializableCircle + { + public int Radius; + } + + [Serializable] + public class SerializableSquare + { + public int Side; + } + + public class Node + { + Node next; + } + + [Serializable] + public class SerializableNode + { + SerializableNode next; + } + #endregion + + [DataContract] + public class SerializableClass + { + [DataMember] + string member; + + [DataMember(Order = 3)] + string v3member; + } + + [DataContract] + public class DerivedClass : SerializableClass + { + [DataMember] + SerializableClass[] derivedMember; + } +#pragma warning restore CS0169, IDE0051, IDE1006 +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImportOptionsTests.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImportOptionsTests.cs new file mode 100644 index 0000000000000..49867cc07ba88 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImportOptionsTests.cs @@ -0,0 +1,160 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Runtime.Serialization.Schema; +using System.Runtime.Serialization.Schema.Tests.DataContracts; +using System.Xml; +using System.Xml.Schema; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + public class ImportOptionsTests + { + private readonly ITestOutputHelper _output; + + public ImportOptionsTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void DefaultOptions() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + Assert.NotNull(importer); + Assert.NotNull(importer.Options); + Assert.False(importer.Options.EnableDataBinding); + Assert.False(importer.Options.GenerateInternal); + Assert.False(importer.Options.GenerateSerializable); + Assert.False(importer.Options.ImportXmlType); + Assert.Null(importer.Options.CodeProvider); + Assert.NotNull(importer.Options.Namespaces); + Assert.Empty(importer.Options.Namespaces); + Assert.NotNull(importer.Options.ReferencedCollectionTypes); + Assert.Empty(importer.Options.ReferencedCollectionTypes); + Assert.NotNull(importer.Options.ReferencedTypes); + Assert.Empty(importer.Options.ReferencedTypes); + Assert.Null(importer.Options.DataContractSurrogate); + } + + [Fact] + public void GetImportOptions() + { + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + Assert.NotNull(importer.Options); + } + + [Fact] + public void SetImportOptions() + { + XsdDataContractImporter e = new XsdDataContractImporter(); + e.Options = new ImportOptions(); + e.Options.Namespaces.Add("Test", "http://schemas.datacontract.org/2004/07/fooNs"); + Assert.Single(e.Options.Namespaces); + } + + [Fact] + public void GenerateInternal() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.GenerateInternal = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.GenerateInternal); + } + + [Fact] + public void EnableDataBinding() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.EnableDataBinding = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.EnableDataBinding); + } + + [Fact] + public void GenerateSerializable() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.GenerateSerializable = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.GenerateSerializable); + } + + [Fact] + public void ImportXmlType() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + importer.Options.ImportXmlType = true; + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.True(importer.Options.ImportXmlType); + } + + [Fact] + public void CodeProvider() + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("csharp"); + importer.Options.CodeProvider = codeProvider; + Console.WriteLine(importer.Options.CodeProvider.GetType().FullName); + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + Assert.Equal(codeProvider, importer.Options.CodeProvider); + } + + [Theory] + [InlineData("http://schemas.datacontract.org/2004/07/fooNs", "customizedNamespace")] + [InlineData("*", "customizedNamespace")] + [InlineData("null", "customizedNamespace")] + public void Namespaces(string dcns, string clrns) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + Assert.NotNull(importer.Options.Namespaces); + Assert.Empty(importer.Options.Namespaces); + + importer.Options.Namespaces.Add(dcns, clrns); + importer.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + } + + [Theory] + [MemberData(nameof(ReferencedTypes_MemberData))] + public void ReferencedTypes(XmlSchemaSet schemas, XmlQualifiedName qname, Type[] referencedTypes, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + for (int i = 0; i < referencedTypes.Length; i++) + importer.Options.ReferencedTypes.Add(referencedTypes[i]); + + if (expectedExceptionType == null) + { + importer.Import(schemas, qname); + _output.WriteLine(SchemaUtils.DumpCode(importer.CodeCompileUnit)); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => importer.Import(schemas, qname)); + + if (!string.IsNullOrEmpty(msg)) + Assert.StartsWith(msg, ex.Message); + } + } + public static IEnumerable ReferencedTypes_MemberData() + { + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[1], new Type[] { typeof(AnotherValidType) } }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[2], new Type[] { typeof(NonAttributedSquare) } }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[1], new Type[] { typeof(AnotherValidType), typeof(ConflictingAnotherValidType) }, + typeof(InvalidOperationException), @"List of referenced types contains more than one type with data contract name 'AnotherValidType' in namespace 'http://schemas.datacontract.org/2004/07/barNs'. Need to exclude all but one of the following types. Only matching types can be valid references:"}; + // These last two are described as "negative" in the original NetFx XsdDCImporterApi test code... but they don't fail here or there. + yield return new object[] { SchemaUtils.IsReferenceSchemas, SchemaUtils.ValidTypeNames[3], new Type[] { typeof(NonRefType) } }; + yield return new object[] { SchemaUtils.IsReferenceSchemas, SchemaUtils.ValidTypeNames[4], new Type[] { typeof(RefType1) } }; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImporterTests.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImporterTests.cs new file mode 100644 index 0000000000000..79d9c1f65078e --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/ImporterTests.cs @@ -0,0 +1,417 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization.Schema; +using System.Runtime.Serialization.Schema.Tests.DataContracts; +using System.Xml; +using System.Xml.Schema; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + public class ImporterTests + { + private readonly ITestOutputHelper _output; + public ImporterTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void Ctor_Default() + { + XsdDataContractImporter xci = new XsdDataContractImporter(); + Assert.NotNull(xci); + Assert.Null(xci.Options); + } + + [Fact] + public void Ctor_CCU() + { + CodeCompileUnit ccu = new CodeCompileUnit(); + XsdDataContractImporter xci = new XsdDataContractImporter(ccu); + Assert.NotNull(xci); + Assert.Equal(ccu, xci.CodeCompileUnit); + } + + [Theory] + [MemberData(nameof(CanImport_MemberData))] + public void CanImport(bool expectedResult, Func canImport, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + if (expectedExceptionType == null) + { + Assert.Equal(expectedResult, canImport(importer)); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => canImport(importer)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable CanImport_MemberData() + { + // CanImport(XmlSchemaSet) + yield return new object[] { true, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.PositiveSchemas) }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas) }; + + // CanImport(XmlSchemaSet, ICollection) + yield return new object[] { true, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.PositiveSchemas, new XmlQualifiedName[] { SchemaUtils.ValidTypeNames[0] }) }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(null, SchemaUtils.InvalidTypeNames), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, (ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeNames')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, new XmlQualifiedName[] { null }), typeof(ArgumentException), @"Cannot import type for null XmlQualifiedName specified via parameter." }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames) }; + + // CanImport(XmlSchemaSet, XmlQualifiedName) + yield return new object[] { true, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]) }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(null, SchemaUtils.InvalidTypeNames[0]), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, (XmlQualifiedName)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { false, (XsdDataContractImporter imp) => imp.CanImport(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames[0]) }; + + // CanImport(XmlSchemaSet, XmlSchemaElement) + // TODO + + // CanImportTests.cs + foreach (var citArgs in SchemaUtils.CanImportTests) + { + XmlSchemaSet schemaSet = SchemaUtils.ReadStringsIntoSchemaSet(citArgs.schemaString); + if (citArgs.qnames == null) + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet) }; + else if (citArgs.qnames.Length == 1 && citArgs.isElement) + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet, SchemaUtils.GetSchemaElement(schemaSet, citArgs.qnames[0])) }; + else if (citArgs.qnames.Length == 1) + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet, citArgs.qnames[0]) }; + else + yield return new object[] { citArgs.expectedResult, (XsdDataContractImporter imp) => imp.CanImport(schemaSet, citArgs.qnames) }; + } + } + + [Theory] + [MemberData(nameof(Import_MemberData))] + public void Import(Action import, int codeLength = -1) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + import(importer); + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + if (codeLength >= 0) + Assert.Equal(codeLength, code.Length); + } + public static IEnumerable Import_MemberData() + { + int newlineSize = Environment.NewLine.Length; + + // Import(XmlSchemaSet) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.PositiveSchemas), 5060 + (168 * newlineSize) }; // 168 lines + + // Import(XmlSchemaSet, ICollection) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.PositiveSchemas, new XmlQualifiedName[] { SchemaUtils.ValidTypeNames[0] }), 1515 + (50 * newlineSize) }; // 50 lines + + // Import(XmlSchemaSet, XmlQualifiedName) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0]), 1515 + (50 * newlineSize) }; // 50 lines + + // Import(XmlSchemaSet, XmlSchemaElement) + // TODO + + // From CanImportTests.cs + foreach (var citArgs in SchemaUtils.CanImportTests) + { + if (citArgs.expectedResult) + { + XmlSchemaSet schemaSet = SchemaUtils.ReadStringsIntoSchemaSet(citArgs.schemaString); + if (citArgs.qnames == null) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet) }; + else if (citArgs.qnames.Length == 1 && citArgs.isElement) + yield return new object[] { (XsdDataContractImporter imp) => { imp.Import(schemaSet, SchemaUtils.GetSchemaElement(schemaSet, citArgs.qnames[0])); } }; + else if (citArgs.qnames.Length == 1 && !citArgs.isElement) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, citArgs.qnames[0]) }; + else + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, citArgs.qnames) }; + } + } + + // From FormatVersioning.cs : Positive tests + (string msg, Type type, string xpath, string xmlFrag)[] formatVersioningArgs = new (string, Type, string, string)[] { + ("Optional Serialization Attribute in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence", @""), + ("Optional Serialization Attribute in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence", @""), + ("Optional Serialization Attribute in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence", @""), + ("Optional Serialization Element in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @""), + ("Optional Serialization Element in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence/xs:any", @""), + ("Optional Serialization Element in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @""), + }; + foreach (var fvArg in formatVersioningArgs) + { + (XmlSchemaSet schemaSet, XmlQualifiedName typeName) = PrepareFormatVersioningTest(fvArg.type, fvArg.xpath, fvArg.xmlFrag); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, typeName) }; + } + } + static (XmlSchemaSet, XmlQualifiedName) PrepareFormatVersioningTest(Type type, string xpath, string xmlFrag) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(type); + XmlSchemaSet schemaSet = exporter.Schemas; + XmlQualifiedName typeName = exporter.GetSchemaTypeName(type); + string schemaString = SchemaUtils.GetSchemaString(schemaSet, typeName.Namespace); + schemaString = SchemaUtils.InsertElement(schemaString, xpath, xmlFrag, true); + schemaString = SchemaUtils.InsertElement(schemaString, @"//xs:schema/xs:complexType", @"", false); + schemaString = SchemaUtils.InsertElement(schemaString, @"//xs:schema/xs:complexType", @"", false); + schemaString = SchemaUtils.InsertAttribute(schemaString, @"//xs:schema", "xmlns", @"ser", @"http://www.w3.org/2000/xmlns/", "http://schemas.microsoft.com/2003/10/Serialization/"); + SchemaUtils.SetSchemaString(schemaSet, typeName.Namespace, schemaString); + schemaSet.Add(XmlSchema.Read(new StringReader(SchemaUtils.GlobalSchema), null)); + + XmlSchema v2SerializationSchema = SchemaUtils.GetSchema(schemaSet, "http://schemas.microsoft.com/2003/10/Serialization/"); + XmlSchemaElement v2Element = new XmlSchemaElement(); + v2Element.Name = "V2Element"; + v2SerializationSchema.Items.Add(v2Element); + XmlSchemaAttribute v2Attribute = new XmlSchemaAttribute(); + v2Attribute.Name = "V2Attribute"; + v2SerializationSchema.Items.Add(v2Attribute); + schemaSet.Reprocess(v2SerializationSchema); + + return (schemaSet, typeName); + } + + [Theory] + [MemberData(nameof(Import_NegativeCases_MemberData))] + public void Import_NegativeCases(Action import, Type expectedExceptionType, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + var ex = Assert.Throws(expectedExceptionType, () => import(importer)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + public static IEnumerable Import_NegativeCases_MemberData() + { + // Import(XmlSchemaSet) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas), typeof(InvalidDataContractException), @"Type 'InvalidType' in namespace 'http://schemas.datacontract.org/2004/07/fooNs' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // Import(XmlSchemaSet, ICollection) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(null, SchemaUtils.InvalidTypeNames), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, (ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeNames')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, new XmlQualifiedName[] { null }), typeof(ArgumentException), @"Cannot import type for null XmlQualifiedName specified via parameter." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames), typeof(InvalidDataContractException), @"Type 'InvalidType' in namespace 'http://schemas.datacontract.org/2004/07/fooNs' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // Import(XmlSchemaSet, XmlQualifiedName) + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(null, SchemaUtils.InvalidTypeNames[0]), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'schemas')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, (XmlQualifiedName)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.MixedSchemas, SchemaUtils.InvalidTypeNames[0]), typeof(InvalidDataContractException), @"Type 'InvalidType' in namespace 'http://schemas.datacontract.org/2004/07/fooNs' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // Import(XmlSchemaSet, XmlSchemaElement) + // TODO + + // NegativeTests.cs, part 1 : Bad schema + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[1]), SchemaUtils.NegativeTypeNames[1]), + typeof(InvalidDataContractException), @"Invalid type specified. Type with name 'FooType' not found in schema with namespace 'http://EmptySchema'." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[2]), SchemaUtils.NegativeTypeNames[2]), + typeof(InvalidDataContractException), @"Invalid type specified. Type with name 'FooType' not found in schema with namespace 'http://NonExistantSchema'." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[3])), + typeof(InvalidDataContractException), @"Type 'InvalidTopLevelElementType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The global element found in the schema with same name references a different type 'int' in namespace 'http://www.w3.org/2001/XMLSchema'. Data contract types must have the same name as their root element name. Consider removing the global element or changing its type. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[4])), + typeof(InvalidDataContractException), @"Type 'ExtraAttributesType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[5]), SchemaUtils.NegativeTypeNames[5]), + typeof(InvalidDataContractException), @"Type 'ExtraAttributeWildcardType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. 'anyAttribute' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[6])), + typeof(InvalidDataContractException), @"Type 'InvalidRootParticleType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The root particle must be a sequence. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[7])), + typeof(InvalidDataContractException), @"Type 'InvalidTopLevelElement' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The global element found in the schema with same name references a different type 'string' in namespace 'http://www.w3.org/2001/XMLSchema'. Data contract types must have the same name as their root element name. Consider removing the global element or changing its type. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[8])), + typeof(InvalidDataContractException), @"Type 'TypeWithElementsOfSameName' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. The type contains two elements with the same name 'DuplicatedName'. Multiple elements with the same name in one type are not supported because members marked with DataMemberAttribute attribute must have unique names. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[9])), + typeof(InvalidDataContractException), @"Type 'SimpleTypeUnion' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Simple types with content are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[10])), + typeof(InvalidDataContractException), @"Enum type 'EnumOnlyList' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Simple type list must contain an anonymous type specifying enumeration facets. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[11])), + typeof(InvalidDataContractException), @"Enum type 'EnumNonStringBaseType' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Anonymous type with cannot be used to create Flags enumeration because it is not a valid enum type. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[12])), + typeof(InvalidDataContractException), @"Type 'ComplexTypeWithSimpleContent' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Complex types with simple content extension are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[13])), + typeof(InvalidDataContractException), @"Array type 'ArrayOfBar' in namespace 'http://schemas.datacontract.org/2004/07/foo' cannot be imported. Form for element 'Bar' must be qualified. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(SchemaUtils.ReadStringsIntoSchemaSet(SchemaUtils.NegativeSchemaStrings[14])), + typeof(InvalidDataContractException), @"Type 'DataSet.datasetType' in namespace 'http://tempuri.org/' cannot be imported. The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer." }; + + // NegativeTests.cs, part 2 : Bad attribute + (Type type, string xpath, string prefix, string localName, string ns, string value, string exMsg)[] badAttributeCases = new (Type, string, string, string, string, string, string)[] { + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']", "", @"abstract", "", @"true", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. The type cannot have 'abstract' set to 'true'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']", null, @"mixed", "", @"true", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Complex type with mixed content is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']/xs:sequence/xs:element[@name='member']", "", @"fixed", "", @"xxx", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Fixed value on element 'member' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']/xs:sequence/xs:element[@name='member']", "", @"default", "", @"yyy", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Default value on element 'member' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:element[@name='SerializableClass']", "", @"abstract", "", @"true", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. The element cannot have 'abstract' set to 'true'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(SerializableClass), @"//xs:schema/xs:element[@name='SerializableClass']", "", @"substitutionGroup", "", @"tns:Head", @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Substitution group on element 'SerializableClass' is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + }; + foreach (var bac in badAttributeCases) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(bac.type); + XmlSchemaSet schemaSet = exporter.Schemas; + XmlQualifiedName typeName = exporter.GetSchemaTypeName(bac.type); + string schemaString = SchemaUtils.GetSchemaString(schemaSet, typeName.Namespace); + schemaString = SchemaUtils.InsertElement(schemaString, @"//xs:schema/xs:element[@name='SerializableClass']", @"", true); + schemaString = SchemaUtils.InsertAttribute(schemaString, bac.xpath, bac.prefix, bac.localName, bac.ns, bac.value); + XmlSchemaSet schema = SchemaUtils.ReadStringsIntoSchemaSet(schemaString); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schema), typeof(InvalidDataContractException), bac.exMsg }; + } + + // NegativeTests.cs, part 3 : Bad element + (Type type, string xpath, string xmlFrag, bool insertAfter, string exMsg)[] badElementCases = new(Type, string, string, bool, string)[] { + (typeof(SerializableClass), @"//xs:schema/xs:complexType[@name='SerializableClass']/xs:sequence/xs:element", @"", true, @"Type 'SerializableClass' in namespace 'http://special1.tempuri.org' cannot be imported. Ref to element 'SerializableClass' in 'http://special1.tempuri.org' namespace is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(DerivedClass), @"//xs:schema/xs:complexType[@name='DerivedClass']/xs:complexContent/xs:extension/xs:sequence/xs:element", @"", false, @"Type 'DerivedClass' in namespace 'http://special1.tempuri.org' cannot be imported. The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + (typeof(DerivedClass), @"//xs:schema/xs:complexType[@name='DerivedClass']/xs:complexContent/xs:extension/xs:sequence/xs:element", @"", false, @"Type 'DerivedClass' in namespace 'http://special1.tempuri.org' cannot be imported. Ref to element 'int' in 'http://schemas.microsoft.com/2003/10/Serialization/' namespace is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + }; + foreach (var bec in badElementCases) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(bec.type); + XmlSchemaSet schemaSet = exporter.Schemas; + XmlQualifiedName typeName = exporter.GetSchemaTypeName(bec.type); + string schemaString = SchemaUtils.GetSchemaString(schemaSet, typeName.Namespace); + schemaString = SchemaUtils.InsertElement(schemaString, bec.xpath, bec.xmlFrag, bec.insertAfter); + XmlSchemaSet schema = SchemaUtils.ReadStringsIntoSchemaSet(schemaString); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schema), typeof(InvalidDataContractException), bec.exMsg }; + } + + // FormatVersioning.cs : Negative tests + (string msg, Type type, string xpath, string xmlFrag, string exMsg)[] formatVersioningNegativeArgs = new (string, Type, string, string, string)[] { + ("Required Serialization Attribute in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Attribute in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence", @"", + @"Type 'ISerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests.DataContracts' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Attribute in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ArrayOfImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Element in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @"", + @"Type 'ImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Ref to element 'V2Element' in 'http://schemas.microsoft.com/2003/10/Serialization/' namespace is not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Element in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence/xs:any", @"", + @"Type 'ISerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests.DataContracts' cannot be imported. The root sequence must contain only local elements. Group ref, choice, any and nested sequences are not supported. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Required Serialization Element in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence/xs:element", @"", + @"Type 'ArrayOfImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. 'maxOccurs' on element 'ImporterTests.SerializableFormatClass' must be 1. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Optional Global Attribute in class", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Optional Global Attribute in ISerializable", + typeof(ISerializableFormatClass), @"//xs:schema/xs:complexType[@name='ISerializableFormatClass']/xs:sequence", @"", + @"Type 'ISerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests.DataContracts' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + ("Optional Global Attribute in Array", + typeof(SerializableFormatClass), @"//xs:schema/xs:complexType[@name='ArrayOfImporterTests.SerializableFormatClass']/xs:sequence", @"", + @"Type 'ArrayOfImporterTests.SerializableFormatClass' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests' cannot be imported. Attributes must be optional and from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'. Either change the schema so that the types can map to data contract types or use ImportXmlType or use a different serializer."), + }; + foreach (var fvArg in formatVersioningNegativeArgs) + { + (XmlSchemaSet schemaSet, XmlQualifiedName typeName) = PrepareFormatVersioningTest(fvArg.type, fvArg.xpath, fvArg.xmlFrag); + yield return new object[] { (XsdDataContractImporter imp) => imp.Import(schemaSet, typeName), typeof(InvalidDataContractException), fvArg.exMsg }; + } + } +#pragma warning disable CS0169, IDE0051, IDE1006 + [Serializable] + public class SerializableFormatClass + { + SerializableFormatClass[] array; + } +#pragma warning restore CS0169, IDE0051, IDE1006 + + [Theory] + [MemberData(nameof(GetCodeTypeReference_MemberData))] + public void GetCodeTypeReference(XmlSchemaSet schemas, XmlQualifiedName qname, string exptectedType, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + + if (schemas != null) + importer.Import(schemas); + + if (expectedExceptionType == null) + { + CodeTypeReference ctr = importer.GetCodeTypeReference(qname); + Assert.NotNull(ctr); + + string typeString = SchemaUtils.GetString(ctr); + _output.WriteLine(typeString); + Assert.Equal(exptectedType, typeString); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => importer.GetCodeTypeReference(qname)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetCodeTypeReference_MemberData() + { + // GetCodeTypeReference(XmlQualifiedName) + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0], "fooNs.ValidType" }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[2], "Suites.SchemaImport.NonAttributedType" }; + yield return new object[] { null, null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { null, SchemaUtils.InvalidTypeNames[0], null, typeof(InvalidOperationException), @"Type 'InvalidType' from namespace 'http://schemas.datacontract.org/2004/07/fooNs' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter." }; + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.InvalidTypeNames[0], null, typeof(InvalidOperationException), @"Type 'InvalidType' from namespace 'http://schemas.datacontract.org/2004/07/fooNs' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter." }; + + // GetCodeTypeReference(XmlQualifiedName, XmlSchemaElement) + // TODO + } + + + [Theory] + [MemberData(nameof(GetKnownTypeReferences_MemberData))] + public void GetKnownTypeReferences(XmlSchemaSet schemas, XmlQualifiedName qname, int expectedRefCount, Type expectedExceptionType = null, string msg = null) + { + XsdDataContractImporter importer = SchemaUtils.CreateImporterWithDefaultOptions(); + + if (schemas != null) + importer.Import(schemas); + + if (expectedExceptionType == null) + { + ICollection knownTypeReferences = importer.GetKnownTypeReferences(qname); + + if (knownTypeReferences == null) + { + _output.WriteLine("KnownType count: null"); + Assert.Equal(0, expectedRefCount); + } + else + { + _output.WriteLine("KnownType count: {0}", knownTypeReferences.Count); + foreach (CodeTypeReference knownTypeReference in knownTypeReferences) + _output.WriteLine(SchemaUtils.GetString(knownTypeReference)); + Assert.Equal(expectedRefCount, knownTypeReferences.Count); + } + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => importer.GetKnownTypeReferences(qname)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetKnownTypeReferences_MemberData() + { + // GetKnownTypeReferences(XmlQualifiedName) + yield return new object[] { SchemaUtils.PositiveSchemas, SchemaUtils.ValidTypeNames[0], 0 }; + yield return new object[] { null, null, -1, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'typeName')" }; + yield return new object[] { null, SchemaUtils.ValidTypeNames[0], -1, typeof(InvalidOperationException), @"Type 'ValidType' from namespace 'http://schemas.datacontract.org/2004/07/fooNs' has not been imported from schema. Consider first importing this type by calling one of the Import methods on XsdDataContractImporter." }; + // TODO - a positive case with non-zero ref count. + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs new file mode 100644 index 0000000000000..35d64bde333ba --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.Serialization.Schema; +using System.Runtime.Serialization.Schema.Tests.DataContracts; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + // TODO - Add a test covering 'ISerializationCodeDomSurrogateProvider'/ProcessImportedType - There was nothing in NetFx test suites for this. + public class SurrogateTests + { + static Type[] testTypes = new Type[] + { + typeof(CircleContainer), + typeof(Node), + typeof(XmlSerializerPerson), + }; + + private readonly ITestOutputHelper _output; + public SurrogateTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void DefaultScenario() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = new SurrogateProvider(false); + for (int i = 0; i < testTypes.Length; i++) + exporter.Export((Type)testTypes[i]); + + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.DataContractSurrogate = exporter.Options.DataContractSurrogate; + importer.Options.ImportXmlType = true; + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute(""http://special1.tempuri.org"", ClrNamespace=""special1.tempuri.org"")]", code); + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("""", ClrNamespace="""")]", code); + + Assert.Contains(@"namespace special1.tempuri.org", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name=""CircleContainer"", Namespace=""http://special1.tempuri.org""\)\]\s*public partial class CircleContainer : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare[] circlesField;", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare CircleField;", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataMemberAttribute\(\)\]\s*public System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare Circle", code); + Assert.Contains(@"public partial class SerializableSquare : object, System.Runtime.Serialization.IExtensibleDataObject", code); + + Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Matches(@"\[System.Xml.Serialization.XmlSchemaProviderAttribute\(""ExportSchema""\)\]\s*\[System.Xml.Serialization.XmlRootAttribute\(ElementName\s*=\s*""XmlSerializerPersonElement"", Namespace\s*=\s*""""\)\]\s*public partial class XmlSerializerPerson : object, System.Xml.Serialization.IXmlSerializable", code); + } + + [Fact] + public void WithReferencedType() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = new SurrogateProvider(false); + for (int i = 0; i < testTypes.Length; i++) + exporter.Export((Type)testTypes[i]); + + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.DataContractSurrogate = exporter.Options.DataContractSurrogate; + importer.Options.ImportXmlType = true; + importer.Options.ReferencedTypes.Add(typeof(SerializableCircle)); + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute(""http://special1.tempuri.org"", ClrNamespace=""special1.tempuri.org"")]", code); + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("""", ClrNamespace="""")]", code); + + Assert.Contains(@"namespace special1.tempuri.org", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name=""CircleContainer"", Namespace=""http://special1.tempuri.org""\)\]\s*public partial class CircleContainer : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableSquare[] circlesField;", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle CircleField;", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataMemberAttribute\(\)\]\s*public System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle Circle", code); + + Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Matches(@"\[System.Xml.Serialization.XmlSchemaProviderAttribute\(""ExportSchema""\)\]\s*\[System.Xml.Serialization.XmlRootAttribute\(ElementName\s*=\s*""XmlSerializerPersonElement"", Namespace\s*=\s*""""\)\]\s*public partial class XmlSerializerPerson : object, System.Xml.Serialization.IXmlSerializable", code); + Assert.DoesNotContain(@"public partial class SerializableSquare : object, System.Runtime.Serialization.IExtensibleDataObject", code); + } + + [Fact] + public void WithSurrogateBinding() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = new SurrogateProvider(true); + for (int i = 0; i < testTypes.Length; i++) + exporter.Export((Type)testTypes[i]); + + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.DataContractSurrogate = exporter.Options.DataContractSurrogate; + importer.Options.ImportXmlType = true; + importer.Options.ReferencedTypes.Add(typeof(Circle)); + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute(""http://special1.tempuri.org"", ClrNamespace=""special1.tempuri.org"")]", code); + Assert.DoesNotContain(@"[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("""", ClrNamespace="""")]", code); + + Assert.Contains(@"namespace special1.tempuri.org", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name=""CircleContainer"", Namespace=""http://special1.tempuri.org""\)\]\s*public partial class CircleContainer : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle[] circlesField;", code); + Assert.Contains(@"private System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle CircleField;", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataMemberAttribute\(\)\]\s*public System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle Circle", code); + + Assert.DoesNotContain(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); + Assert.DoesNotContain(@"class SerializableSquare", code); + Assert.DoesNotContain(@"class SerializableNode", code); + Assert.DoesNotContain(@"class XmlSerializerPerson", code); + } + } + + internal class SurrogateProvider : ISerializationSurrogateProvider2 + { + static XmlQualifiedName s_circleList = new XsdDataContractExporter().GetSchemaTypeName(typeof(SerializableCircle[])); + static XmlQualifiedName s_square = new XsdDataContractExporter().GetSchemaTypeName(typeof(SerializableSquare)); + static XmlQualifiedName s_serializableNode = new XsdDataContractExporter().GetSchemaTypeName(typeof(SerializableNode)); + static XmlQualifiedName s_xmlSerializerPersonAdapter = new XsdDataContractExporter().GetSchemaTypeName(typeof(XmlSerializerAdapter)); + + bool _surrogateBinding; + public SurrogateProvider(bool surrogateBinding) { _surrogateBinding = surrogateBinding; } + + public object? GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) => memberInfo.MemberType.ToString(); + public object? GetCustomDataToExport(Type runtimeType, Type dataContractType) => runtimeType.Name; + public object GetDeserializedObject(object obj, Type targetType) => throw new NotImplementedException(); + public void GetKnownCustomDataTypes(Collection customDataTypes) { } + public object GetObjectToSerialize(object obj, Type targetType) => throw new NotImplementedException(); + public Type GetSurrogateType(Type type) // Formerly Known As GetDataContractType(Type)... but this was in the first interface, so we can't change this name. + { + if (type == typeof(Node)) + return typeof(SerializableNode); + if (type == typeof(SerializableCircle)) + return typeof(SerializableSquare); + if (type == typeof(XmlSerializerPerson)) + return typeof(XmlSerializerAdapter); + return type; + } + public Type? GetReferencedTypeOnImport(string name, string ns, object? customData) + { + if (!_surrogateBinding) + { + // Collection item and type name mismatch must be handled by surrogate to avoid exception + return (name == s_circleList.Name && ns == s_circleList.Namespace) ? typeof(SerializableSquare[]) : null; + } + + if (name == s_square.Name && ns == s_square.Namespace) + return typeof(SerializableCircle); + if (name == s_circleList.Name && ns == s_circleList.Namespace) + return typeof(SerializableCircle[]); + if (name == s_serializableNode.Name && ns == s_serializableNode.Namespace) + return typeof(Node); + if (name == s_xmlSerializerPersonAdapter.Name && ns == s_xmlSerializerPersonAdapter.Namespace) + return typeof(XmlSerializerPerson); + return null; + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XmlSerializerAdapter : IXmlSerializable + { + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemaSet) + { + XmlReflectionImporter importer = new XmlReflectionImporter(); + XmlTypeMapping xmlTypeMapping = importer.ImportTypeMapping(typeof(T)); + XmlSchemas schemas = new XmlSchemas(); + XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); + exporter.ExportTypeMapping(xmlTypeMapping); + schemas.Compile(new ValidationEventHandler(ValidationCallbackWithErrorCode), true); + for (int i = 0; i < schemas.Count; i++) + { + XmlSchema schema = schemas[i]; + schemaSet.Add(schema); + } + return new XmlQualifiedName(xmlTypeMapping.TypeName, xmlTypeMapping.Namespace); + } + + private static void ValidationCallbackWithErrorCode(object sender, ValidationEventArgs args) + { + Console.WriteLine("Schema warning: " + args.Message); + } + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/RoundTripTest.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/RoundTripTest.cs new file mode 100644 index 0000000000000..67c31622ace95 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/RoundTripTest.cs @@ -0,0 +1,349 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Data; +using System.Runtime.Serialization.Schema; +using System.Xml; +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Schema.Tests +{ + public class RoundTripTest + { + private readonly ITestOutputHelper _output; + + public RoundTripTest(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void RountTripTest() + { + // AppContext SetSwitch seems to be unreliable in the unit test case. So let's not rely on it + // for test coverage. But let's do look at the app switch to get our verification correct. + AppContext.TryGetSwitch("Switch.System.Runtime.Serialization.DataContracts.Auto_Import_KVP", out bool autoImportKVP); + + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(typeof(RootClass)); + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.ImportXmlType = true; + importer.Options.ReferencedTypes.Add(typeof(DBNull)); + importer.Options.ReferencedTypes.Add(typeof(DateTimeOffset)); + importer.Import(exporter.Schemas); + + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.Contains(@"This code was generated by a tool.", code); + Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests", code); + Assert.Contains(@"public partial class RoundTripTestRootClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Xml.Serialization.XmlRootAttribute(ElementName=""SchemaDefinedType"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization"")]", code); + Assert.Contains(@"public partial class dataSetType : object, System.Xml.Serialization.IXmlSerializable", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.DataContractClass"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial class RoundTripTestDataContractClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.DataContractStruct"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial struct RoundTripTestDataContractStruct : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.EmitDefaultClass"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial class RoundTripTestEmitDefaultClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"public System.Nullable NullableDataContractStruct2", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""RoundTripTest.EncodingMismatchClass"", Namespace=""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""", code); + Assert.Contains(@"public partial class RoundTripTestEncodingMismatchClass : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"public enum RoundTripTestMyEnum : int", code); + Assert.Contains(@"TwoHundred = 200", code); + Assert.Contains(@"public enum RoundTripTestMyFlagsEnum : int", code); + Assert.Contains(@"Four = 4,", code); + Assert.Contains(@"public class ArrayOfNullableOfRoundTripTestMyEnumho3BZmza : System.Collections.Generic.List", code); + Assert.Contains(@"namespace schemas.microsoft.com._2003._10.Serialization.Arrays", code); + Assert.Contains(@"public partial class ArrayOfKeyValueOfintArrayOfstringty7Ep6D1 : object, System.Xml.Serialization.IXmlSerializable", code); + Assert.Contains(@"private static System.Xml.XmlQualifiedName typeName = new System.Xml.XmlQualifiedName(""ArrayOfKeyValueOfintArrayOfstringty7Ep6D1"", ""http://schemas.microsoft.com/2003/10/Serialization/Arrays"");", code); + Assert.Contains(@"public partial class ArrayOfKeyValueOfNullableOfunsignedByteNullableOfunsignedByte_ShTDFhl_P : object, System.Xml.Serialization.IXmlSerializable", code); + + if (autoImportKVP) + { + Assert.Contains(@"public partial struct KeyValuePairOfintArrayOfstringty7Ep6D1 : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"public partial struct KeyValuePairOfNullableOfunsignedByteNullableOfunsignedByte_ShTDFhl_P : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Contains(@"[System.Runtime.Serialization.DataContractAttribute(Name=""KeyValuePairOfstringNullableOfintU6ho3Bhd"", Namespace=""http://schemas.datacontract.org/2004/07/System.Collections.Generic"")]", code); + } + else + { + Assert.DoesNotContain(@"public partial struct KeyValuePairOfintArrayOfstringty7Ep6D1 : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.DoesNotContain(@"public partial struct KeyValuePairOfNullableOfunsignedByteNullableOfunsignedByte_ShTDFhl_P : System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.DoesNotContain(@"[System.Runtime.Serialization.DataContractAttribute(Name=""KeyValuePairOfstringNullableOfintU6ho3Bhd"", Namespace=""http://schemas.datacontract.org/2004/07/System.Collections.Generic"")]", code); + } + } + + [Fact] + public void IsReferenceType() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(typeof(RootIsReferenceContainer)); + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + importer.Options.ImportXmlType = true; + importer.Import(exporter.Schemas); + string code = SchemaUtils.DumpCode(importer.CodeCompileUnit); + _output.WriteLine(code); + + Assert.True(code.Length > 616); + } + + +#pragma warning disable CS0169, CS0414, IDE0051, IDE1006 + #region RoundTripTest DataContracts + [DataContract] + public class RootClass + { + [DataMember] MyEnum myEnum; + [DataMember] MyEnum[] arrayOfMyEnum; + [DataMember] MyEnum? nullableOfMyEnum; + [DataMember] MyEnum?[] arrayOfNullableOfMyEnum; + [DataMember] MyFlagsEnum myFlagsEnum; + [DataMember] XmlNode[] xmlNodes; + [DataMember] XmlElement xmlElement; + [DataMember] DataContractClass dataContractClass; + [DataMember] DataContractClass[] arrayOfDataContractClass; + [DataMember] DataContractStruct dataContractStruct; + [DataMember] DataContractStruct[] arrayOfDataContractStruct; + [DataMember] DataContractStruct? nullableOfDataContractStruct; + [DataMember] DataContractStruct?[] arrayOfNullableOfDataContractStruct; + [DataMember] DataSet dataSet; + [DataMember] IList> intLists; + [DataMember] IList>> dictionaries; + [DataMember] IDictionary nullableValues; + [DataMember] IDictionary nullableKeyAndValues; + [DataMember] EmitDefaultClass emitDefaultClass; + [DataMember] EncodingMismatchClass encodingMismatchClass; + [DataMember] DBNull dbnull; + } + + public enum MyEnum { Hundred = 100, TwoHundred = 200 }; + [Flags] + public enum MyFlagsEnum { Four = 4, Eight = 8 }; + + [DataContract] + public class DataContractClass + { + [DataMember] public int IntValue; + [DataMember] public Guid GuidValue; + [DataMember] TimeSpan timeSpanValue; + } + + [DataContract] + public struct DataContractStruct + { + [DataMember] public char CharValue; + [DataMember] Decimal decimalValue; + [DataMember] XmlQualifiedName qname; + } + + + [DataContract] + public class EmitDefaultClass + { + [DataMember(EmitDefaultValue = false)] + public string Name1; + [DataMember(EmitDefaultValue = false)] + public int Age1; + [DataMember(EmitDefaultValue = false)] + public int? Salary1; + [DataMember(EmitDefaultValue = false)] + public DataContractStruct DataContractStruct1; + [DataMember(EmitDefaultValue = false)] + public DataContractStruct? NullableDataContractStruct1; + [DataMember(EmitDefaultValue = false)] + public DateTime DateTime1; + [DataMember(EmitDefaultValue = false)] + public DateTimeOffset DateTimeOffset1; + [DataMember(EmitDefaultValue = false)] + public Guid Guid1; + [DataMember(EmitDefaultValue = false)] + public Decimal Decimal1; + [DataMember(EmitDefaultValue = false)] + public TimeSpan TimeSpan1; + + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public string Name2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public int Age2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public int? Salary2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DataContractStruct DataContractStruct2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DataContractStruct? NullableDataContractStruct2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DateTime DateTime2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public DateTimeOffset DateTimeOffset2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public Guid Guid2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public Decimal Decimal2; + [DataMember(IsRequired = true, EmitDefaultValue = false)] + public TimeSpan TimeSpan2; + + } + + [DataContract] + public class EncodingMismatchClass + { + [DataMember(Name = "a:b")] + public int a; + [DataMember(Name = "a_x003A_bc_x003C__x003B_")] + public int b; + [DataMember(Name = "a_x003a_bc_x003b__x003c_")] + public int c; + } + #endregion + + #region IsReferenceType DataContracts + [DataContract] + class RootIsReferenceContainer + { + [DataMember] + RefEdibleItem r = new RefEdibleItem(); + [DataMember] + Fruit w = new Fruit(); + [DataMember] + RefApple x = new RefApple(); + [DataMember] + public RefCustomer customer = RefCustomer.CreateInstance(); + [DataMember] + RefGrades grades = new RefGrades(); + [DataMember] + CircularLinkedList_ContainsBackpointingRef clcb = new CircularLinkedList_ContainsBackpointingRef(); + [DataMember] + RefCircularLinks_ContainsBackpointer rccb = new RefCircularLinks_ContainsBackpointer(); + [DataMember] + RefCircularNodeA_ContainsRefWithBackpointer rcnacr = new RefCircularNodeA_ContainsRefWithBackpointer(); + } + + [DataContract(IsReference = true)] + class RefEdibleItem + { + } + + [DataContract] + class Fruit : RefEdibleItem + { + } + + [DataContract(IsReference = true)] + class RefApple : Fruit + { + } + + [CollectionDataContract(IsReference = true)] + public class RefGrades : List + { + } + + [DataContract(IsReference = true)] + class RefCustomer + { + [DataMember] + string Name; + [DataMember] + int ZipCode; + + internal static RefCustomer CreateInstance() + { + RefCustomer x = new RefCustomer(); + x.Name = "Bill Gates"; + x.ZipCode = 98052; + return x; + } + } + + + [DataContract] + public class CircularLinkedList_ContainsBackpointingRef + { + [DataMember] + RefNode start; + + [DataMember] + int numberOfNodes; + + public CircularLinkedList_ContainsBackpointingRef() + { + numberOfNodes = 4; + RefNode currentNode = null, prevNode = null; + start = null; + for (int i = 0; i < numberOfNodes; i++) + { + currentNode = new RefNode(i, "Hello World"); + if (i == 0) + start = currentNode; + if (prevNode != null) + prevNode.Next = currentNode; + prevNode = currentNode; + } + currentNode.Next = start; + } + } + + [DataContract(IsReference = true)] + public class RefNode + { + [DataMember] + public RefNode Next; + + [DataMember] + int id; + + [DataMember] + string name; + + public RefNode(int id, string name) + { + this.id = id; + this.name = name; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularLinks_ContainsBackpointer + { + [DataMember] + RefCircularLinks_ContainsBackpointer link; + + public RefCircularLinks_ContainsBackpointer() + { + link = this; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularNodeA_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeB_ContainsRefWithBackpointer linkToB; + + public RefCircularNodeA_ContainsRefWithBackpointer() + { + linkToB = new RefCircularNodeB_ContainsRefWithBackpointer(this); + } + } + + [DataContract(IsReference = true)] + public class RefCircularNodeB_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeA_ContainsRefWithBackpointer linkToA; + + public RefCircularNodeB_ContainsRefWithBackpointer(RefCircularNodeA_ContainsRefWithBackpointer nodeA) + { + linkToA = nodeA; + } + } + #endregion +#pragma warning restore CS0169, CS0414, IDE0051, IDE1006 + } +} diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/SchemaUtils.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/SchemaUtils.cs new file mode 100644 index 0000000000000..cea8d7333ccb7 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/SchemaUtils.cs @@ -0,0 +1,370 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.CodeDom; +using System.CodeDom.Compiler; +using System.IO; +using System.Xml; +using System.Xml.Schema; + +namespace System.Runtime.Serialization.Schema.Tests +{ + internal class SchemaUtils + { + static XmlWriterSettings writerSettings = new XmlWriterSettings() { Indent = true }; + + #region Test Data + internal static XmlSchemaSet PositiveSchemas = SchemaUtils.ReadStringsIntoSchemaSet( + new string[] { + @" + + + ", + @" + + + ", + @" + + + + + ", + }); + + internal static XmlSchemaSet IsReferenceSchemas = SchemaUtils.ReadStringsIntoSchemaSet( + new string[] { + @" + + + + + + + + + ", + @" + + + + + + + + ", + }); + + internal static XmlSchemaSet MixedSchemas = SchemaUtils.ReadStringsIntoSchemaSet( + new string[] { + @" + + + + + ", + @" + + + ", + }); + + internal static string[] NegativeSchemaStrings = + new string[] { + @"", // null + @" + ", // new XmlQualifiedName("FooType", "http://EmptySchema"), + @" + ", // new XmlQualifiedName("FooType", "http://NonExistantSchema"), + @" + + + ", // null + @" + + + ", // null + @" + + + ", // new XmlQualifiedName("ExtraAttributeWildcardType", "http://schemas.datacontract.org/2004/07/foo"), + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + ", // null + @" + + + + + ", // null + @" + + ", // null + }; + + internal static (bool expectedResult, bool isElement, XmlQualifiedName[] qnames, string schemaString)[] CanImportTests = new (bool, bool, XmlQualifiedName[], string)[] { + (false, false, new XmlQualifiedName[] { new XmlQualifiedName("InvalidTopLevelElementType", "http://schemas.datacontract.org/2004/07/foo") }, + @" + + + + "), + (true, false, new XmlQualifiedName[] { new XmlQualifiedName("ValidType", "http://schemas.datacontract.org/2004/07/foo") }, + @" + + + + "), + (true, false, new XmlQualifiedName[] { + new XmlQualifiedName("Address", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + new XmlQualifiedName("Person", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + new XmlQualifiedName("ArrayOfAddress", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + new XmlQualifiedName("ArrayOfArrayOfAddress", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes"), + }, + @" + + + + + + + + + "), + (true, false, null, + @" + + + + "), + (true, false, null, + @" + + + "), + (false, false, new XmlQualifiedName[] { new XmlQualifiedName("TypeWithExtraAttributes", "http://schemas.datacontract.org/2004/07/foo") }, + @" + + + "), + (true, true, new XmlQualifiedName[] { new XmlQualifiedName("Address", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.Classes") }, + @" + + + + + + + + "), + }; + + internal static XmlQualifiedName[] ValidTypeNames = new XmlQualifiedName[] { + new XmlQualifiedName("ValidType", "http://schemas.datacontract.org/2004/07/fooNs"), + new XmlQualifiedName("AnotherValidType", "http://schemas.datacontract.org/2004/07/barNs"), + new XmlQualifiedName("NonAttributedType", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport"), + new XmlQualifiedName("NonRefType", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.ReferencedTypes"), + new XmlQualifiedName("RefType1", "http://schemas.datacontract.org/2004/07/Suites.SchemaImport.ReferencedTypes"), + }; + + internal static XmlQualifiedName[] InvalidTypeNames = new XmlQualifiedName[] { + new XmlQualifiedName("InvalidType", "http://schemas.datacontract.org/2004/07/fooNs"), + }; + + // These correspond with the set in 'NegativeSchemaStrings' + internal static XmlQualifiedName[] NegativeTypeNames = new XmlQualifiedName[] { + null, + new XmlQualifiedName("FooType", "http://EmptySchema"), + new XmlQualifiedName("FooType", "http://NonExistantSchema"), + null, + null, + new XmlQualifiedName("ExtraAttributeWildcardType", "http://schemas.datacontract.org/2004/07/foo"), + null, + null, + null, + null, + null, + null, + null, + null, + null, + }; + + internal static string GlobalSchema = @" + + + "; + #endregion + + + internal static XsdDataContractImporter CreateImporterWithDefaultOptions() + { + XsdDataContractImporter importer = new XsdDataContractImporter(); + importer.Options = new ImportOptions(); + return importer; + } + + internal static string DumpCode(CodeCompileUnit ccu, CodeDomProvider provider = null) + { + provider ??= CodeDomProvider.CreateProvider("csharp"); + + CodeGeneratorOptions options = new CodeGeneratorOptions() + { + BlankLinesBetweenMembers = true, + BracingStyle = "C", + }; + + StringWriter sw = new StringWriter(); + provider.GenerateCodeFromCompileUnit(ccu, sw, options); + return sw.ToString(); + } + + internal static XmlSchema GetSchema(XmlSchemaSet schemaSet, string targetNs) + { + XmlSchema schema = null; + foreach (XmlSchema ctSchema in schemaSet.Schemas()) + { + if (ctSchema.TargetNamespace == targetNs) + { + schema = ctSchema; + break; + } + } + return schema; + } + + internal static XmlSchemaElement GetSchemaElement(XmlSchemaSet schemaSet, XmlQualifiedName qname) + { + foreach (XmlSchema schema in schemaSet.Schemas(qname.Namespace)) + { + XmlSchemaElement schemaElement = (XmlSchemaElement)schema.Elements[qname]; + if (schemaElement != null) + return schemaElement; + } + throw new Exception(String.Format("Element {0} is not found", qname)); + } + + internal static string GetSchemaString(XmlSchemaSet schemaSet, string targetNs) + { + XmlSchema schema = GetSchema(schemaSet, targetNs); + StringWriter stringWriter = new StringWriter(); + XmlWriter xmlWriter = XmlWriter.Create(stringWriter, writerSettings); + schema.Write(xmlWriter); + xmlWriter.Flush(); + return stringWriter.ToString(); + } + + internal static void SetSchemaString(XmlSchemaSet schemaSet, string targetNs, string schemaString) + { + XmlSchema schema = null; + foreach (XmlSchema ctSchema in schemaSet.Schemas()) + { + if (ctSchema.TargetNamespace == targetNs) + { + schema = ctSchema; + break; + } + } + schemaSet.Remove(schema); + schema = XmlSchema.Read(new StringReader(schemaString), null); + schemaSet.Add(schema); + } + + internal static string GetString(CodeTypeReference typeReference) + { + if (typeReference.ArrayRank > 0) + { + CodeTypeReference arrayType = typeReference; + string arrayString = String.Empty; + for (; ; ) + { + int rank = typeReference.ArrayRank; + arrayString += "["; + for (int r = 1; r < rank; r++) + arrayString += ","; + arrayString += "]"; + + typeReference = typeReference.ArrayElementType; + if (typeReference.ArrayRank == 0) + break; + } + return String.Format("Array of {0}{1}", typeReference.BaseType, arrayString); + } + else + return typeReference.BaseType; + } + + internal static string InsertAttribute(string xml, string xpath, string prefix, string localName, string ns, string value) + { + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(XmlReader.Create(new StringReader(xml))); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); + nsMgr.AddNamespace("xs", XmlSchema.Namespace); + XmlElement xmlElement = (XmlElement)xmlDoc.SelectSingleNode(xpath, nsMgr); + XmlAttribute xmlAttribute = xmlDoc.CreateAttribute(prefix, localName, ns); + xmlAttribute.Value = value; + xmlElement.Attributes.Append(xmlAttribute); + + StringWriter stringWriter = new StringWriter(); + xmlDoc.Save(XmlWriter.Create(stringWriter, writerSettings)); + return stringWriter.ToString(); + } + + internal static string InsertElement(string xml, string xpath, string xmlFrag, bool insertAfter) + { + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(XmlReader.Create(new StringReader(xml))); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); + nsMgr.AddNamespace("xs", XmlSchema.Namespace); + XmlNode xmlNode = xmlDoc.SelectSingleNode(xpath, nsMgr); + if (insertAfter) + xmlNode.ParentNode.InsertAfter(xmlDoc.ReadNode(XmlReader.Create(new StringReader(xmlFrag))), xmlNode); + else + xmlNode.ParentNode.InsertBefore(xmlDoc.ReadNode(XmlReader.Create(new StringReader(xmlFrag))), xmlNode); + + StringWriter stringWriter = new StringWriter(); + xmlDoc.Save(XmlWriter.Create(stringWriter, writerSettings)); + return stringWriter.ToString(); + } + + + internal static XmlSchemaSet ReadStringsIntoSchemaSet(params string[] schemaStrings) + { + XmlSchemaSet schemaSet = new XmlSchemaSet(); + foreach (string schemaString in schemaStrings) + { + StringReader reader = new StringReader(schemaString); + XmlSchema schema = XmlSchema.Read(reader, null); + if (schema == null) + throw new Exception("Could not read schema"); + schemaSet.Add(schema); + } + return schemaSet; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs index 6a3d406d81026..e1f9618e94dc6 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/ref/System.Runtime.Serialization.Xml.cs @@ -79,6 +79,7 @@ public partial class ExportOptions { public ExportOptions() { } public System.Collections.ObjectModel.Collection KnownTypes { get { throw null; } } + public ISerializationSurrogateProvider? DataContractSurrogate { get { throw null; } set { throw null; } } } public sealed partial class ExtensionDataObject { @@ -470,3 +471,87 @@ public virtual void WriteXmlnsAttribute(string? prefix, string namespaceUri) { } public virtual void WriteXmlnsAttribute(string? prefix, System.Xml.XmlDictionaryString namespaceUri) { } } } +namespace System.Runtime.Serialization.DataContracts +{ + public abstract partial class DataContract + { + internal const System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes DataContractPreserveMemberTypes = + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | + System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties; + + internal DataContract(DataContractCriticalHelper helper) { } + + public virtual DataContract? BaseContract { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] get { throw null; } } + public virtual string? ContractType { get { throw null; } } + public virtual bool IsBuiltInDataContract { get { throw null; } } + public virtual bool IsISerializable { get { throw null; } } + public virtual bool IsReference { get { throw null; } } + public virtual bool IsValueType { get { throw null; } } + public virtual System.Collections.Generic.Dictionary? KnownDataContracts { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] get { throw null; } } + public virtual System.Collections.ObjectModel.ReadOnlyCollection DataMembers { get { throw null; } } + public virtual Type OriginalUnderlyingType { get { throw null; } } + public virtual System.Xml.XmlQualifiedName XmlName { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(DataContract.DataContractPreserveMemberTypes)] + public virtual Type UnderlyingType { get { throw null; } } + public virtual System.Xml.XmlDictionaryString? TopLevelElementName { get { throw null; } } + public virtual System.Xml.XmlDictionaryString? TopLevelElementNamespace { get { throw null; } } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public static DataContract? GetBuiltInDataContract(string name, string ns) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public static System.Xml.XmlQualifiedName GetXmlName(Type type) { throw null; } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public virtual System.Xml.XmlQualifiedName GetArrayTypeName(bool isNullable) { throw null; } + public virtual bool IsDictionaryLike([Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? keyName, [Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? valueName, [Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? itemName) { throw null; } + } + internal abstract partial class DataContractCriticalHelper { } + public sealed partial class DataContractSet + { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public DataContractSet(DataContractSet dataContractSet) { throw null; } + public DataContractSet(ISerializationSurrogateProvider? dataContractSurrogate, System.Collections.Generic.IEnumerable? referencedTypes, System.Collections.Generic.IEnumerable? referencedCollectionTypes) { throw null; } + + public System.Collections.Generic.Dictionary Contracts { get { throw null; } } + public System.Collections.Generic.Dictionary? KnownTypesForObject { get { throw null; } } + public System.Collections.Generic.Dictionary ProcessedContracts { get { throw null; } } + public System.Collections.Hashtable SurrogateData { get { throw null; } } + + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public DataContract GetDataContract(Type type) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public DataContract? GetDataContract(System.Xml.XmlQualifiedName key) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public Type? GetReferencedType(System.Xml.XmlQualifiedName xmlName, DataContract dataContract, out DataContract? referencedContract, out object[]? genericParameters, bool? supportGenericTypes = null) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public void ImportSchemaSet(System.Xml.Schema.XmlSchemaSet schemaSet, System.Collections.Generic.IEnumerable? typeNames, bool importXmlDataType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] + public System.Collections.Generic.List ImportSchemaSet(System.Xml.Schema.XmlSchemaSet schemaSet, System.Collections.Generic.IEnumerable elements, bool importXmlDataType) { throw null; } + } + public sealed partial class DataMember + { + internal DataMember() { } + + public bool EmitDefaultValue { get { throw null; } } + public bool IsNullable { get { throw null; } } + public bool IsRequired { get { throw null; } } + public DataContract MemberTypeContract { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] get { throw null; } } + public string Name { get { throw null; } } + public long Order { get { throw null; } } + } + public sealed partial class XmlDataContract : DataContract + { + internal XmlDataContract(Type type) : base(default) { } + + public bool HasRoot { get { throw null; } } + public bool IsAnonymous { get { throw null; } } + public bool IsTopLevelElementNullable { get { throw null; } } + public bool IsTypeDefinedOnImport { get { throw null; } set { throw null; } } + public new bool IsValueType { get { throw null; } set { throw null; } } + public System.Xml.Schema.XmlSchemaType? XsdType { get { throw null; } } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index f88b0e308e53f..9b0f071947d33 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -112,6 +112,18 @@ public static void DCS_DateTimeAsRoot() Assert.StrictEqual(DataContractSerializerHelper.SerializeAndDeserialize(DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc), @"9999-12-31T23:59:59.9999999Z"), DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc)); } + [Fact] + public static void DCS_BinarySerializationOfDateTime() + { + DateTime dateTime = DateTime.Parse("2021-01-01"); + MemoryStream ms = new(); + DataContractSerializer dcs = new(dateTime.GetType()); + using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(ms, null, null, ownsStream: true)) + dcs.WriteObject(writer, dateTime); + var serializedBytes = ms.ToArray(); + Assert.Equal(72, serializedBytes.Length); + } + [Fact] public static void DCS_DecimalAsRoot() { @@ -2727,14 +2739,6 @@ static string GenerateaAndGetXPath(Type t, MemberInfo[] mi) t, mi, out xname); } - [Fact] - public static void XsdDataContractExporterTest() - { - XsdDataContractExporter exporter = new XsdDataContractExporter(); - Assert.Throws(() => exporter.CanExport(typeof(Employee))); - Assert.Throws(() => exporter.Export(typeof(Employee))); - } - [Fact] public static void DCS_MyISerializableType() { @@ -3867,6 +3871,135 @@ public static void DCS_BasicPerSerializerRoundTripAndCompare_EnumStruct_NotNetFr #endregion + [Fact] + public static void DCS_KnownSerializableTypes_KeyValuePair_2() + { + KeyValuePair kvp = new KeyValuePair("the_key", 42); + Assert.StrictEqual(kvp, DataContractSerializerHelper.SerializeAndDeserialize>(kvp, "the_key42")); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Queue_1() + { + Queue q = new Queue(); + q.Enqueue("first"); + q.Enqueue("second"); + Queue q2 = DataContractSerializerHelper.SerializeAndDeserialize>(q, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstsecond<_head>0<_size>2<_tail>2<_version>3"); + Assert.Equal(q, q2); + Assert.StrictEqual(q.Count, q2.Count); + Assert.Equal(q.Dequeue(), q2.Dequeue()); + Assert.Equal(q.Dequeue(), q2.Dequeue()); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Stack_1() + { + Stack stk = new Stack(); + stk.Push("first"); + stk.Push("last"); + Stack result = DataContractSerializerHelper.SerializeAndDeserialize>(stk, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstlast<_size>2<_version>2"); + Assert.Equal(stk, result); + Assert.StrictEqual(stk.Count, result.Count); + Assert.Equal(stk.Pop(), result.Pop()); + Assert.Equal(stk.Pop(), result.Pop()); + } + + [Fact] + public static void DCS_KnownSerializableTypes_ReadOnlyCollection_1() + { + ReadOnlyCollection roc = new ReadOnlyCollection(new string[] { "one", "two", "three", "four" }); + ReadOnlyCollection result = DataContractSerializerHelper.SerializeAndDeserialize>(roc, "onetwothreefour"); + Assert.Equal(roc, result); + Assert.StrictEqual(roc.Count, result.Count); + + for (int i = 0; i < roc.Count; i++) + Assert.Equal(roc[i], result[i]); + } + + [Fact] + public static void DCS_KnownSerializableTypes_ReadOnlyDictionary_2() + { + ReadOnlyDictionary rod = new ReadOnlyDictionary(new Dictionary { { "one", 1 }, { "two", 22 }, { "three", 333 }, { "four", 4444 } }); + ReadOnlyDictionary result = DataContractSerializerHelper.SerializeAndDeserialize>(rod, "one1two22three333four4444"); + Assert.Equal(rod, result); + Assert.StrictEqual(rod.Count, result.Count); + + foreach (var kvp in rod) + { + Assert.True(result.ContainsKey(kvp.Key)); + Assert.Equal(kvp.Value, result[kvp.Key]); + } + } + + [Fact] + public static void DCS_KnownSerializableTypes_Queue() + { + Queue q = new Queue(); + q.Enqueue("first"); + q.Enqueue("second"); + Queue q2 = DataContractSerializerHelper.SerializeAndDeserialize(q, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstsecond<_growFactor>200<_head>0<_size>2<_tail>2<_version>2"); + Assert.Equal(q, q2); + Assert.StrictEqual(q.Count, q2.Count); + Assert.StrictEqual(q.Dequeue(), q2.Dequeue()); + Assert.StrictEqual(q.Dequeue(), q2.Dequeue()); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Stack() + { + Stack stk = new Stack(); + stk.Push("first"); + stk.Push("last"); + Stack result = DataContractSerializerHelper.SerializeAndDeserialize(stk, "<_array xmlns:a=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">firstlast<_size>2<_version>2"); + Assert.Equal(stk, result); + Assert.StrictEqual(stk.Count, result.Count); + Assert.StrictEqual(stk.Pop(), result.Pop()); + Assert.StrictEqual(stk.Pop(), result.Pop()); + } + + [Fact] + [ActiveIssue("No issue filed yet. Turns out, CultureInfo is not serialzable, even if it is included in s_knownSerializableTypeInfos")] + public static void DCS_KnownSerializableTypes_CultureInfo() + { + CultureInfo ci = new CultureInfo("pl"); + Assert.StrictEqual(ci, DataContractSerializerHelper.SerializeAndDeserialize(ci, "", null, null, true)); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Version() + { + Version ver = new Version(5, 4, 3); + Assert.StrictEqual(ver, DataContractSerializerHelper.SerializeAndDeserialize(ver, "<_Build>3<_Major>5<_Minor>4<_Revision>-1")); + } + + [Fact] + public static void DCS_KnownSerializableTypes_Tuples() + { + Tuple t1 = new Tuple("first"); + Assert.StrictEqual(t1, DataContractSerializerHelper.SerializeAndDeserialize>(t1, "first")); + + Tuple t2 = new Tuple("first", "second"); + Assert.StrictEqual(t2, DataContractSerializerHelper.SerializeAndDeserialize>(t2, "firstsecond")); + + Tuple t3 = new Tuple("first", "second", "third"); + Assert.StrictEqual(t3, DataContractSerializerHelper.SerializeAndDeserialize>(t3, "firstsecondthird")); + + Tuple t4 = new Tuple("first", "second", "third", "fourth"); + Assert.StrictEqual(t4, DataContractSerializerHelper.SerializeAndDeserialize>(t4, "firstsecondthirdfourth")); + + Tuple t5 = new Tuple("first", "second", "third", "fourth", "fifth"); + Assert.StrictEqual(t5, DataContractSerializerHelper.SerializeAndDeserialize>(t5, "firstsecondthirdfourthfifth")); + + Tuple t6 = new Tuple("first", "second", "third", "fourth", "fifth", "sixth"); + Assert.StrictEqual(t6, DataContractSerializerHelper.SerializeAndDeserialize>(t6, "firstsecondthirdfourthfifthsixth")); + + Tuple t7 = new Tuple("first", "second", "third", "fourth", "fifth", "sixth", "seventh"); + Assert.StrictEqual(t7, DataContractSerializerHelper.SerializeAndDeserialize>(t7, "firstsecondthirdfourthfifthsixthseventh")); + + Tuple> t8 = new Tuple>("first", "second", "third", "fourth", "fifth", "sixth", "seventh", new Tuple(8, 9, "tenth")); + Assert.StrictEqual(t8, DataContractSerializerHelper.SerializeAndDeserialize>>(t8, "firstsecondthirdfourthfifthsixthseventh89tenth")); + } + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/60462", TestPlatforms.iOS | TestPlatforms.tvOS)] public static void DCS_TypeWithVirtualGenericProperty() diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj index 88c8927cea01e..ce77b694a8833 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj @@ -28,10 +28,29 @@ + + + + + + + + + + + + + + + + + + + - + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExportOptionsTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExportOptionsTests.cs new file mode 100644 index 0000000000000..7f33e82eb3b26 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExportOptionsTests.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using Xunit.Abstractions; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class ExportOptionsTests + { + private readonly ITestOutputHelper _output; + + public ExportOptionsTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void DefaultOptions() + { + ExportOptions options = new ExportOptions(); + Assert.NotNull(options); + Assert.Null(options.DataContractSurrogate); + Assert.NotNull(options.KnownTypes); + Assert.Empty(options.KnownTypes); + } + + [Fact] + public void GetImportOptions() + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var options = new ExportOptions(); + exporter.Options = options; + Assert.NotNull(exporter.Options); + Assert.Equal(options, exporter.Options); + } + + [Fact] + public void SetImportOptions() + { + XsdDataContractExporter e = new XsdDataContractExporter(); + e.Options = new ExportOptions(); + Assert.Empty(e.Options.KnownTypes); + e.Options.KnownTypes.Add(typeof(Types.Point)); + Assert.Single(e.Options.KnownTypes); + } + + [Fact] + public void KnownTypes_Negative() + { + XsdDataContractExporter e = new XsdDataContractExporter(); + e.Options = new ExportOptions(); + e.Options.KnownTypes.Add(null); + var ex = Assert.Throws(() => e.Export(typeof(Types.Point))); + Assert.Equal(@"Cannot export null type provided via KnownTypesCollection.", ex.Message); + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterApiTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterApiTests.cs new file mode 100644 index 0000000000000..03e2c5f5a918b --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterApiTests.cs @@ -0,0 +1,336 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Reflection; +using System.Xml; +using System.Xml.Schema; +using Xunit; +using Xunit.Abstractions; + +using SerializableTypes.XsdDataContractExporterTests; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class ExporterApiTests + { + private readonly ITestOutputHelper _output; + public ExporterApiTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void Ctor_Default() + { + XsdDataContractExporter xce = new XsdDataContractExporter(); + Assert.NotNull(xce); + Assert.Null(xce.Options); + } + + [Fact] + public void Ctor_Schemas() + { + XmlSchemaSet schemaSet = new XmlSchemaSet(); + XsdDataContractExporter xce = new XsdDataContractExporter(schemaSet); + Assert.NotNull(xce); + Assert.Null(xce.Options); + Assert.Equal(schemaSet, xce.Schemas); + } + + [Theory] + [MemberData(nameof(CanExport_MemberData))] + public void CanExport(bool expectedResult, string testname, Func canExport, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter importer = new XsdDataContractExporter(); + if (expectedExceptionType == null) + { + Assert.Equal(expectedResult, canExport(importer)); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => canExport(importer)); + + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable CanExport_MemberData() + { + //yield return new object[] { true, "", (XsdDataContractExporter exp) => exp.CanExport() }; + //yield return new object[] { false, "", (XsdDataContractExporter exp) => exp.CanExport(), typeof(), @"" }; + + // CanExport(Type) + yield return new object[] { true, "t1+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.Point)) }; + yield return new object[] { false, "t2-", (XsdDataContractExporter exp) => exp.CanExport((Type)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { false, "t3-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.NonSerializableSquare)) }; + yield return new object[] { true, "t4+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.NonAttributedPersonStruct)) }; + yield return new object[] { true, "t5+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.NonAttributedPersonClass)) }; + yield return new object[] { true, "t6+", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.ExtendedSquare)) }; + yield return new object[] { false, "t7-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection1)) }; + yield return new object[] { false, "t8-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection2)) }; + yield return new object[] { false, "t9-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection3)) }; + yield return new object[] { false, "t10-", (XsdDataContractExporter exp) => exp.CanExport(typeof(Types.RecursiveCollection4)) }; + + // CanExport(ICollection) + yield return new object[] { true, "ca1+", (XsdDataContractExporter exp) => exp.CanExport(new Assembly[] { typeof(DataContractTypes).Assembly }) }; + yield return new object[] { false, "ca2-", (XsdDataContractExporter exp) => exp.CanExport((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'assemblies')" }; + yield return new object[] { false, "ca3-", (XsdDataContractExporter exp) => exp.CanExport(new Assembly[] { null }), typeof(ArgumentException), @"Cannot export null assembly provided via 'assemblies' parameter." }; + yield return new object[] { false, "ca4-", (XsdDataContractExporter exp) => exp.CanExport(new Assembly[] { typeof(ExporterApiTests).Assembly }) }; + + // CanExport(ICollection) + yield return new object[] { true, "ct1+", (XsdDataContractExporter exp) => exp.CanExport(new Type[] { typeof(Types.Point), typeof(Types.Circle) }) }; + yield return new object[] { false, "ct2-", (XsdDataContractExporter exp) => exp.CanExport((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'types')" }; + yield return new object[] { false, "ct3-", (XsdDataContractExporter exp) => exp.CanExport(new Type[] { null }), typeof(ArgumentException), @"Cannot export null type provided via 'types' parameter." }; + yield return new object[] { false, "ct4-", (XsdDataContractExporter exp) => exp.CanExport(new Type[] { typeof(Types.Point), typeof(Types.NonSerializableSquare) }) }; + } + + [Theory] + [MemberData(nameof(Export_MemberData))] + public void Export(string testname, Action export, Action schemaCheck = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + export(exporter); + + string schemas = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine("Count = " + exporter.Schemas.Count); + _output.WriteLine(schemas); + + // When checking schema count, be sure to include the "Serialization" schema - which is omitted from 'DumpSchema' - as + // well as the XmlSchema, both of which are the base from which all further schemas build. + if (schemaCheck != null) + schemaCheck(schemas, exporter.Schemas); + + Assert.True(schemas.Length > 0); + } + public static IEnumerable Export_MemberData() + { + // Export(Type) + yield return new object[] { "Exp1", (XsdDataContractExporter exp) => exp.Export(typeof(Types.Point)), (string s, XmlSchemaSet ss) => { + Assert.Equal(3, ss.Count); + // *basic* + // Point + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + + // Export(ICollection) + // AppContext SetSwitch seems to be unreliable in the unit test case. So let's not rely on it + // for test coverage. But let's do look at the app switch to get our verification correct. + AppContext.TryGetSwitch("Switch.System.Runtime.Serialization.DataContracts.Auto_Import_KVP", out bool autoImportKVP); + yield return new object[] { "Exp2", (XsdDataContractExporter exp) => exp.Export(new Assembly[] { typeof(DataContractTypes).Assembly }), (string s, XmlSchemaSet ss) => { + Assert.Equal(autoImportKVP ? 21 : 20, ss.Count); + Assert.Equal(autoImportKVP ? 171 : 163, ss.GlobalTypes.Count); + Assert.Equal(autoImportKVP ? 204 : 196, ss.GlobalElements.Count); + } }; + + // Export(ICollection) + yield return new object[] { "Exp3", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(Types.Point), typeof(Types.Circle) }), (string s, XmlSchemaSet ss) => { + Assert.Equal(4, ss.Count); + // *basic* + // Point + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *shapes* + // Circle + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { "Exp4", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(Types.NonAttributedPersonStruct), typeof(Types.NonAttributedPersonClass), typeof(Types.ExtendedSquare) }), (string s, XmlSchemaSet ss) => { + Assert.Equal(5, ss.Count); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *Types* + // NonAttributedPersonStruct + SchemaUtils.OrderedContains(@"", ref s); + Assert.Matches(@"\s*true", s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // NonAttributedPersonClass + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // ExtendedSquare + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *shapes* + // Square + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + // *basic* + // Point + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + + // EnumsTest - from Enums.cs + yield return new object[] { "ExpEnum", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(System.Reflection.TypeAttributes) }), (string s, XmlSchemaSet ss) => { + Assert.Equal(3, ss.Count); + //Assert.Equal(3, ss.GlobalAttributes.Count); + Assert.Equal(5, ss.GlobalTypes.Count); + Assert.Equal(23, ss.GlobalElements.Count); + } }; + } + + [Theory] + [MemberData(nameof(Export_NegativeCases_MemberData))] + public void Export_NegativeCases(string testname, Action export, Type expectedExceptionType, string exMsg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(expectedExceptionType, () => export(exporter)); + _output.WriteLine(ex.Message); + + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + public static IEnumerable Export_NegativeCases_MemberData() + { + // Export(Type) + yield return new object[] { "tn", (XsdDataContractExporter exp) => exp.Export((Type)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "tinv", (XsdDataContractExporter exp) => exp.Export(typeof(Types.NonSerializableSquare)), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + + // Export(ICollection) + yield return new object[] { "can", (XsdDataContractExporter exp) => exp.Export((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'assemblies')" }; + yield return new object[] { "canv", (XsdDataContractExporter exp) => exp.Export(new Assembly[] { null }), typeof(ArgumentException), @"Cannot export null assembly provided via 'assemblies' parameter." }; + // This exception message might change with updates to this test assembly. Right now, 'NonSerializablePerson' is the non-serializable type that gets found first. If this becomes an issue, consider not verifying the exception message. + yield return new object[] { "cainv", (XsdDataContractExporter exp) => exp.Export(new Assembly[] { typeof(ExporterApiTests).Assembly }), typeof(InvalidDataContractException), @"Type 'NonSerializablePerson' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + + // Export(ICollection) + yield return new object[] { "ctn", (XsdDataContractExporter exp) => exp.Export((ICollection)null), typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'types')" }; + yield return new object[] { "ctnv", (XsdDataContractExporter exp) => exp.Export(new Type[] { null }), typeof(ArgumentException), @"Cannot export null type provided via 'types' parameter." }; + yield return new object[] { "ctinv", (XsdDataContractExporter exp) => exp.Export(new Type[] { typeof(Types.Point), typeof(Types.NonSerializableSquare) }), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + } + + [Theory] + [MemberData(nameof(GetSchemaTypeName_MemberData))] + public void GetSchemaTypeName(string testname, Type t, XmlQualifiedName qname, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + + if (expectedExceptionType == null) + { + XmlQualifiedName schemaTypeName = exporter.GetSchemaTypeName(t); + Assert.Equal(qname, schemaTypeName); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => exporter.GetSchemaTypeName(t)); + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetSchemaTypeName_MemberData() + { + // GetSchemaTypeName(Type) + yield return new object[] { "GSTN_Point", typeof(Types.Point), new XmlQualifiedName("Point", "http://basic") }; + yield return new object[] { "GSTN_null", null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "GSTN_invalid", typeof(Types.NonSerializableSquare), null, typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + yield return new object[] { "GSTN_Square", typeof(Types.Square), new XmlQualifiedName("Square", "http://shapes") }; + yield return new object[] { "GSTN_ExtSq", typeof(Types.ExtendedSquare), new XmlQualifiedName("ExtendedSquare", "http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types") }; + + // From DataContractTypesTest.cs + yield return new object[] { "DCTT_Addr2", typeof(DataContractTypes.Address2), new XmlQualifiedName("Address", "http://schemas.datacontract.org/2004/07/schemaexport.suites") }; + } + + [Theory] + [MemberData(nameof(GetSchemaType_MemberData))] + public void GetSchemaType(string testname, Type t, XmlSchemaType stName, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + + if (expectedExceptionType == null) + { + XmlSchemaType schemaType = exporter.GetSchemaType(t); + Assert.Equal(stName, schemaType); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => exporter.GetSchemaType(t)); + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetSchemaType_MemberData() + { + // GetSchemaTypeName(Type) + yield return new object[] { "GST_Point", typeof(Types.Point), null }; // Per the docs - "types for which the GetSchemaTypeName method returns a valid name, this method returns null." + yield return new object[] { "GST_null", null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "GST_invalid", typeof(Types.NonSerializableSquare), null, typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + } + + [Theory] + [MemberData(nameof(GetRootElementName_MemberData))] + public void GetRootElementName(string testname, Type t, XmlQualifiedName rName, Type expectedExceptionType = null, string msg = null) + { + _output.WriteLine($"=============== {testname} ==============="); + XsdDataContractExporter exporter = new XsdDataContractExporter(); + + if (expectedExceptionType == null) + { + XmlQualifiedName rootTypeName = exporter.GetRootElementName(t); + Assert.Equal(rName, rootTypeName); + } + else + { + var ex = Assert.Throws(expectedExceptionType, () => exporter.GetSchemaTypeName(t)); + if (!string.IsNullOrEmpty(msg)) + Assert.Equal(msg, ex.Message); + } + } + public static IEnumerable GetRootElementName_MemberData() + { + // GetSchemaTypeName(Type) + yield return new object[] { "GREN_Point", typeof(Types.Point), new XmlQualifiedName("Point", "http://basic") }; + yield return new object[] { "GREN_null", null, null, typeof(ArgumentNullException), @"Value cannot be null. (Parameter 'type')" }; + yield return new object[] { "GREN_invalid", typeof(Types.NonSerializableSquare), null, typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types.NonSerializableSquare' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required." }; + yield return new object[] { "GREN_Square", typeof(Types.Square), new XmlQualifiedName("Square", "http://shapes") }; + yield return new object[] { "GREN_ExtSq", typeof(Types.ExtendedSquare), new XmlQualifiedName("ExtendedSquare", "http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types") }; + } + + [Fact] + public void get_Schemas_Bug() + { + // Bug 23200 from who knows which ancient bug database + // I believe the gist of this is that modifying the XmlSchemaSet provided by XsdDataContractExporter.get_Schemas + // can result in that same property throwing an exception? I'm not really sure what this bug is, or if this really + // is a bug. Neither the code in NetFx nor here actually throws an exception without the newly added lines. + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Export(typeof(Types.Circle)); + XmlSchemaSet schemaSet = exporter.Schemas; // added - exception + foreach (XmlSchema schema in exporter.Schemas.Schemas("http://basic")) // original - Still no exception + exporter.Schemas.Remove(schema); + var ex = Assert.Throws(() => exporter.Schemas); // added + Assert.Equal(@"Type 'http://basic:Point' is not declared.", ex.Message); // added + exporter.Export(typeof(Types.Square)); + ex = Assert.Throws(() => exporter.Schemas); // added + Assert.Equal(@"Type 'http://basic:Point' is not declared.", ex.Message); // added + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterTypesTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterTypesTests.cs new file mode 100644 index 0000000000000..211d201329659 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/ExporterTypesTests.cs @@ -0,0 +1,843 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + +using SerializableTypes.XsdDataContractExporterTests; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class ExporterTypesTests + { + private readonly ITestOutputHelper _output; + public ExporterTypesTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void TypesTest() + { + var types = new List() + { + typeof(DataContractTypes.Person1), + typeof(DataContractTypes.Person2), + typeof(ExporterTypesTests.Group), + typeof(ExporterTypesTests.NoDataContract), + typeof(ExporterTypesTests.DataContractWithValidMember), + typeof(ExporterTypesTests.DataContractWithValidMember), + typeof(ExporterTypesTests.PersonInfo), + }; + + XsdDataContractExporter exporter = new XsdDataContractExporter(); + ExportOptions options = new ExportOptions(); + options.KnownTypes.Add(typeof(ArrayList)); + options.KnownTypes.Add(typeof(Guid)); + exporter.Options = options; + + exporter.Export(types); + exporter.Export(types); // Run twice, to ensure that types are not re-exported + + string schemas = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine(schemas); + _output.WriteLine($"----------------- {exporter.Schemas.Count}, {exporter.Schemas.GlobalElements.Count}, {exporter.Schemas.GlobalTypes.Count}"); + + Assert.Equal(5, exporter.Schemas.Count); + Assert.Equal(36, exporter.Schemas.GlobalElements.Count); + Assert.Equal(18, exporter.Schemas.GlobalTypes.Count); + + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + } + + [Theory] + [SkipOnPlatform(TestPlatforms.Browser, "Inconsistent and unpredictable results.")] // TODO - Why does 'TypeWithReadWriteCollectionAndNoCtorOnCollection' only cause an exception sometimes, but not all the time? What's special about wasm here? + [InlineData(typeof(NoDataContractWithoutParameterlessConstructor), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+NoDataContractWithoutParameterlessConstructor' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.")] + [InlineData(typeof(DataContractWithInvalidMember), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+NoDataContractWithoutParameterlessConstructor' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.")] + [InlineData(typeof(SerializableWithInvalidMember), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+NoDataContractWithoutParameterlessConstructor' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.")] + [InlineData(typeof(TypeWithReadWriteCollectionAndNoCtorOnCollection), typeof(InvalidDataContractException), @"System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+CollectionWithoutParameterlessCtor`1[[System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Person, System.Runtime.Serialization.Xml.Tests, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51]] does not have a default constructor.")] + // Yes, the exception type for this next one is different. It was different in NetFx as well. + [InlineData(typeof(ArrayContainer), typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayA' with the same data contract name 'Array' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(typeof(KeyValueNameSame), typeof(InvalidDataContractException), @"The collection data contract type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+KeyValueNameSame' specifies the same value 'MyName' for both the KeyName and the ValueName properties. This is not allowed. Consider changing either the KeyName or the ValueName property.")] + [InlineData(typeof(AnyWithRoot), typeof(InvalidDataContractException), @"Type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AnyWithRoot' cannot specify an XmlRootAttribute attribute because its IsAny setting is 'true'. This type must write all its contents including the root element. Verify that the IXmlSerializable implementation is correct.")] + public void TypesTest_Negative(Type badType, Type exType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(exType, () => exporter.Export(badType)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + + [Theory] + [InlineData(new Type[] { typeof(AddressA), typeof(AddressB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressA' with the same data contract name 'Address' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(AddressA), typeof(AddressC) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressC' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+AddressA' with the same data contract name 'Address' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(OrderA), typeof(OrderB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+OrderB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+OrderA' with the same data contract name 'Order' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(ArrayA), typeof(ArrayB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+ArrayA' with the same data contract name 'Array' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(EnumA), typeof(EnumB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumA' with the same data contract name 'Enum' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(EnumB), typeof(EnumC) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumC' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumB' with the same data contract name 'Enum' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(EnumContainerA), typeof(EnumContainerB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumContainerB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+EnumContainerA' with the same data contract name 'EnumContainer' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(CollectionA), typeof(CollectionB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+CollectionB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+CollectionA' with the same data contract name 'MyCollection' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + [InlineData(new Type[] { typeof(DictionaryA), typeof(DictionaryB) }, typeof(InvalidOperationException), @"DataContract for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+DictionaryB' cannot be added to DataContractSet since type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+DictionaryA' with the same data contract name 'MyDictionary' in namespace 'http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Xml.XsdDataContractExporterTests' is already present and the contracts are not equivalent.")] + public void TypeArrayTest_Negative(Type[] badTypes, Type exType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(exType, () => exporter.Export(badTypes)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + + [Fact] + public void ReferenceTypes() // From IsReferenceTypes.cs + { + List types = new List() + { + typeof(ExporterTypesTests.Order_ContainsRef), + typeof(ExporterTypesTests.Customers_ContainsDuplicateRefs), + typeof(ExporterTypesTests.Student_ContainsDuplicateCollectionRefs), + typeof(ExporterTypesTests.CircularLinkedList_ContainsBackpointingRef), + typeof(ExporterTypesTests.RefCircularLinks_ContainsBackpointer), + typeof(ExporterTypesTests.RefCircularNodeA_ContainsRefWithBackpointer), + typeof(ExporterTypesTests.RefNestedNode_ContainsBackpointer), + typeof(ExporterTypesTests.RefSimpleDataContractCycle_ContainsRefWithBackpointer), + typeof(ExporterTypesTests.Fruit), + typeof(ExporterTypesTests.RefApple), + typeof(ExporterTypesTests.EdibleContainer_ContainsPolymorphicRefs), + }; + + XsdDataContractExporter exporter = new XsdDataContractExporter(); + ExportOptions options = new ExportOptions(); + options.KnownTypes.Add(typeof(ArrayList)); + options.KnownTypes.Add(typeof(Guid)); + + exporter.Export(types); + exporter.Export(types); // Run twice, to ensure that types are not re-exported + + string schemas = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine(schemas); + _output.WriteLine($"----------------- {exporter.Schemas.Count}, {exporter.Schemas.GlobalElements.Count}, {exporter.Schemas.GlobalTypes.Count}"); + + Assert.Equal(3, exporter.Schemas.Count); + Assert.Equal(39, exporter.Schemas.GlobalElements.Count); + Assert.Equal(21, exporter.Schemas.GlobalTypes.Count); + + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + SchemaUtils.OrderedContains(@"", ref schemas); + } + + [Theory] + [InlineData(typeof(ExporterTypesTests.Fruit2), typeof(InvalidDataContractException), @"The IsReference setting for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit2' is 'False', but the same setting for its parent class 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+RefEdibleItem' is 'True'. Derived types must have the same value for IsReference as the base type. Change the setting on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit2' to 'True', or on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+RefEdibleItem' to 'False', or do not set IsReference explicitly.")] + [InlineData(typeof(ExporterTypesTests.Orange), typeof(InvalidDataContractException), @"The IsReference setting for type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Orange' is 'False', but the same setting for its parent class 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit' is 'True'. Derived types must have the same value for IsReference as the base type. Change the setting on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Orange' to 'True', or on type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+Fruit' to 'False', or do not set IsReference explicitly.")] + [InlineData(typeof(ExporterTypesTests.RefEnum), typeof(InvalidDataContractException), @"Enum type 'System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ExporterTypesTests+RefEnum' cannot have the IsReference setting of 'True'. Either change the setting to 'False', or remove it completely.")] + public void ReferenceTypes_Negative(Type badRefType, Type exType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + var ex = Assert.Throws(exType, () => exporter.Export(badRefType)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + +#pragma warning disable CS0169, CS0414 + public class NoDataContract + { + } + + [DataContract] + public class DataContractWithValidMember + { + [DataMember] + NoDataContract member; + } + + [Serializable] + public class SerializableWithValidMember + { + NoDataContract member; + } + + public class NoDataContractWithoutParameterlessConstructor + { + public NoDataContractWithoutParameterlessConstructor(string init) + { + } + } + + [DataContract] + public class DataContractWithInvalidMember + { + [DataMember] + NoDataContractWithoutParameterlessConstructor member; + } + + [Serializable] + public class SerializableWithInvalidMember + { + NoDataContractWithoutParameterlessConstructor member; + } + + [DataContract(Name = "Order")] + public class OrderA + { + [DataMember] + public AddressA address; + } + + [DataContract(Name = "Order")] + public class OrderB + { + [DataMember] + public AddressB address; + } + + [DataContract(Name = "BaseOrder")] + public class BaseOrder + { + } + + [DataContract(Name = "Order")] + public class OrderD : BaseOrder + { + [DataMember] + public AddressA address; + } + + [DataContract(Name = "Address")] + public class AddressA + { + [DataMember] + public string zip; + } + + [DataContract(Name = "Address")] + public class AddressB + { + [DataMember] + public int zip; + } + + [DataContract(Name = "Address")] + public class AddressC + { + [DataMember] + public string street; + [DataMember(IsRequired = true)] + public string zip; + } + + [DataContract] + public class ArrayContainer + { + [DataMember] + public ArrayA a1; + [DataMember] + public ArrayB a2; + } + + [DataContract(Name = "Array")] + public class ArrayA + { + [DataMember] + public ArrayA[] items; + } + + [DataContract(Name = "Array")] + public class ArrayB + { + [DataMember] + public ArrayC[] items; + } + + [DataContract(Name = "Array")] + public class ArrayC + { + [DataMember] + public int items; + } + + [DataContract(Name = "EnumContainer")] + public class EnumContainerA + { + [DataMember] + public EnumA member; + } + + [DataContract(Name = "EnumContainer")] + public class EnumContainerB + { + [DataMember] + public EnumB member; + } + + [DataContract(Name = "Enum")] + public enum EnumA : long + { + } + + [DataContract(Name = "Enum")] + public enum EnumB : long + { + [EnumMember] Min, + [EnumMember] Zero, + [EnumMember] Max, + } + + [DataContract(Name = "Enum")] + public enum EnumC : long + { + [EnumMember] Min, + [EnumMember] Max, + } + + [Serializable] + public class Group + { + public IList People; + } + + [KnownType(typeof(Employee))] + [Serializable] + public class Person + { + string name = "John Smith"; + } + + + [KnownType(typeof(Admin))] + [KnownType(typeof(Architect))] + [KnownType(typeof(Engineer))] + [Serializable] + public class Employee : Person + { + int empId = 42; + } + + [Serializable] + public class Engineer : Employee + { + } + + [Serializable] + [KnownType(typeof(Admin))] + public class Admin : Employee + { + } + + [Serializable] + [KnownType(typeof(Person))] + public class Architect : Employee + { + } + + [CollectionDataContract(Name = "MyCollection", ItemName = "MyItemA")] + public class CollectionA : List + { + } + + [CollectionDataContract(Name = "MyCollection", ItemName = "MyItemB")] + public class CollectionB : List + { + } + + [CollectionDataContract(Name = "MyDictionary", KeyName = "MyKeyA")] + public class DictionaryA : Dictionary + { + } + + [CollectionDataContract(Name = "MyDictionary", KeyName = "MyKeyB")] + public class DictionaryB : Dictionary + { + } + + [CollectionDataContract(KeyName = "MyName", ValueName = "MyName")] + public class KeyValueNameSame : Dictionary + { + } + + [XmlSchemaProvider(null, IsAny = true)] + [XmlRoot(ElementName = "AnyRootElement", IsNullable = false)] + public class AnyWithRoot : XmlSerializableBase + { + } + + public class PersonInfo + { + CollectionWithoutParameterlessCtor localPersons; + ArrayList localPersonArrayList; + public InnerPersonCollection innerPersonInfo = new InnerPersonCollection(); + + public CollectionWithoutParameterlessCtor Persons + { + get + { + localPersons = localPersons ?? new CollectionWithoutParameterlessCtor(5); + return localPersons; + } + } + + public ArrayList PersonArrayList + { + get + { + localPersonArrayList = localPersonArrayList ?? new ArrayList(); + return localPersonArrayList; + } + } + + public PersonInfo() + { + Person p1 = new Person(); + + Person p2 = new Person(); + + Person p3 = new Person(); + + this.Persons.Add(p1); + this.Persons.Add(p2); + + this.PersonArrayList.Add(new Guid()); + this.PersonArrayList.Add("teststring"); + + this.innerPersonInfo.Friends.Add(p3); + + this.innerPersonInfo.PotentialSalaries[0] = 90.0; + this.innerPersonInfo.PotentialSalaries[1] = 100.0; + this.innerPersonInfo.PotentialSalaries[2] = 106.0; + + this.innerPersonInfo.PotentialExpenditures = new double[] { 50.0, 55.0, 69.0 }; + + } + } + + public class InnerPersonCollection + { + private double[] potentialSalaries; + private double[] potentialExpenditures; + CollectionWithoutParameterlessCtor friends; + + public double[] PotentialSalaries + { + get + { + potentialSalaries = potentialSalaries ?? new double[3]; + return potentialSalaries; + } + } + + public double[] PotentialExpenditures + { + get + { + return potentialExpenditures; + } + set + { + potentialExpenditures = value; + } + } + + + public CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new CollectionWithoutParameterlessCtor(2); + return friends; + } + } + } + + public class TypeWithReadWriteCollectionAndNoCtorOnCollection + { + private double[] potentialSalaries; + private double[] potentialExpenditures; + CollectionWithoutParameterlessCtor friends; + + public double[] PotentialSalaries + { + get + { + potentialSalaries = potentialSalaries ?? new double[3]; + return potentialSalaries; + } + } + + public double[] PotentialExpenditures + { + get + { + return potentialExpenditures; + } + set + { + potentialExpenditures = value; + } + } + + + public CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new CollectionWithoutParameterlessCtor(2); + return friends; + } + set + { + friends = value; + } + } + } + + public class CollectionWithoutParameterlessCtor : ICollection + { + ArrayList list; + + public CollectionWithoutParameterlessCtor(int size) + { + list = new ArrayList(size); + } + + #region ICollection Members + + public void Add(T item) + { + list.Add(item); + } + + public void Clear() + { + list.Clear(); + } + + public bool Contains(T item) + { + return list.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + list.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return list.Count; } + } + + public bool IsReadOnly + { + get { return list.IsReadOnly; } + } + + public bool Remove(T item) + { + list.Remove(item); + return true; + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < list.Count; i++) + { + yield return (T)list[i]; + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return list.GetEnumerator(); + } + + #endregion + } + + #region IsReferenceTypes + [DataContract] + class Order_ContainsRef + { + [DataMember] + public string Id = "29817691"; + [DataMember] + public string Url = "http://www.contoso.com/store/exec/OrderManagement?id=x9876270adh8q"; + [DataMember] + public RefCustomer RefCustomer = RefCustomer.CreateInstance(); + } + + [DataContract(IsReference = true)] + class RefEdibleItem + { + } + + [DataContract(IsReference = false)] + class Fruit2 : RefEdibleItem + { + } + + [DataContract] + class Fruit : RefEdibleItem + { + } + + [DataContract(IsReference = true)] + class RefApple : Fruit + { + } + + [DataContract(IsReference = false)] + class Orange : Fruit + { + } + + [DataContract] + [KnownType(typeof(Fruit))] + [KnownType(typeof(RefApple))] + class EdibleContainer_ContainsPolymorphicRefs + { + [DataMember] + RefEdibleItem w = new Fruit(); + [DataMember] + RefEdibleItem x = new RefApple(); + [DataMember] + Fruit z = new RefApple(); + } + + [DataContract] + class Customers_ContainsDuplicateRefs + { + static RefCustomer customer = RefCustomer.CreateInstance(); + [DataMember] + public RefCustomer RefCustomer1 = customer; + [DataMember] + public RefCustomer RefCustomer2 = customer; + } + + [DataContract] + public class Student_ContainsDuplicateCollectionRefs + { + static RefGrades grades; + + static Student_ContainsDuplicateCollectionRefs() + { + grades = new RefGrades(); + grades.Add("A"); + } + + [DataMember] + RefGrades grades1 = grades; + [DataMember] + RefGrades grades2 = grades; + } + + [CollectionDataContract(IsReference = true)] + public class RefGrades : List + { + } + + [DataContract(IsReference = true)] + class RefCustomer + { + [DataMember] + string Name; + [DataMember] + int ZipCode; + + internal static RefCustomer CreateInstance() + { + RefCustomer x = new RefCustomer(); + x.Name = "Bill Gates"; + x.ZipCode = 98052; + return x; + } + } + + + [DataContract] + public class CircularLinkedList_ContainsBackpointingRef + { + [DataMember] + RefNode start; + + [DataMember] + int numberOfNodes; + + public CircularLinkedList_ContainsBackpointingRef() + { + numberOfNodes = 4; + RefNode currentNode = null, prevNode = null; + start = null; + for (int i = 0; i < numberOfNodes; i++) + { + currentNode = new RefNode(i, "Hello World"); + if (i == 0) + start = currentNode; + if (prevNode != null) + prevNode.Next = currentNode; + prevNode = currentNode; + } + currentNode.Next = start; + } + } + + [DataContract(IsReference = true)] + public class RefNode + { + [DataMember] + public RefNode Next; + + [DataMember] + int id; + + [DataMember] + string name; + + public RefNode(int id, string name) + { + this.id = id; + this.name = name; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularLinks_ContainsBackpointer + { + [DataMember] + RefCircularLinks_ContainsBackpointer link; + + public RefCircularLinks_ContainsBackpointer() + { + link = this; + } + } + + + [DataContract(IsReference = true)] + public class RefCircularNodeA_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeB_ContainsRefWithBackpointer linkToB; + + public RefCircularNodeA_ContainsRefWithBackpointer() + { + linkToB = new RefCircularNodeB_ContainsRefWithBackpointer(this); + } + } + + [DataContract(IsReference = true)] + public class RefCircularNodeB_ContainsRefWithBackpointer + { + [DataMember] + RefCircularNodeA_ContainsRefWithBackpointer linkToA; + + public RefCircularNodeB_ContainsRefWithBackpointer(RefCircularNodeA_ContainsRefWithBackpointer nodeA) + { + linkToA = nodeA; + } + } + + + [DataContract(IsReference = true)] + public class RefNestedNode_ContainsBackpointer + { + [DataMember] + RefNestedNode_ContainsBackpointer node; + + [DataMember] + int level; + + public RefNestedNode_ContainsBackpointer(int level) + : this(level, null) + { + } + + public RefNestedNode_ContainsBackpointer(int level, RefNestedNode_ContainsBackpointer rootNode) + { + if (level > 0) + this.node = new RefNestedNode_ContainsBackpointer(level - 1, (rootNode == null ? this : rootNode)); + else + this.node = rootNode; + this.level = level; + } + } + + + [DataContract(IsReference = true)] + public class RefSimpleDataContractCycle_ContainsRefWithBackpointer + { + [DataMember] + object emptyFirstMember = new object(); + [DataMember] + public RefSimpleDataContractCycleNextLink next; + + public static RefSimpleDataContractCycle_ContainsRefWithBackpointer CreateInstance() + { + RefSimpleDataContractCycle_ContainsRefWithBackpointer simpleCycle = new RefSimpleDataContractCycle_ContainsRefWithBackpointer(); + RefSimpleDataContractCycleNextLink childLink = new RefSimpleDataContractCycleNextLink(); + simpleCycle.next = childLink; + childLink.backLink = simpleCycle; + return simpleCycle; + } + } + + + [DataContract] + public class RefSimpleDataContractCycleNextLink + { + [DataMember] + public RefSimpleDataContractCycle_ContainsRefWithBackpointer backLink; + } + + [DataContract(IsReference = true)] + enum RefEnum + { + + } + #endregion + +#pragma warning restore CS0169, CS0414 + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SchemaUtils.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SchemaUtils.cs new file mode 100644 index 0000000000000..9379df362e593 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SchemaUtils.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using Xunit; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + internal class SchemaUtils + { + internal static string SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/"; + static XmlWriterSettings writerSettings = new XmlWriterSettings() { Indent = true }; + + public static string OrderedContains(string expected, ref string actual) + { + Assert.Contains(expected, actual); + actual = actual.Substring(actual.IndexOf(expected)); + return actual; + } + + public static string DumpSchema(XmlSchemaSet schemas) + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + foreach (XmlSchema schema in schemas.Schemas()) + { + if (schema.TargetNamespace != SerializationNamespace) + { + schema.Write(sw); + } + sw.WriteLine(); + } + sw.Flush(); + return sb.ToString(); + } + + internal static XmlSchema GetSchema(XmlSchemaSet schemaSet, string targetNs) + { + XmlSchema schema = null; + foreach (XmlSchema ctSchema in schemaSet.Schemas()) + { + if (ctSchema.TargetNamespace == targetNs) + { + schema = ctSchema; + break; + } + } + return schema; + } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ArrayTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ArrayTypes.cs new file mode 100644 index 0000000000000..3ae741883b396 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ArrayTypes.cs @@ -0,0 +1,102 @@ +using System; +using System.Runtime.Serialization; +using System.Collections.Generic; + +#if UseSeparateAssemblyNamespace +using Address = SerializableTypes.XsdDataContractExporterTests.Address; +using Employee = SerializableTypes.XsdDataContractExporterTests.Employee; + +namespace SerializableTypes.XsdDataContractExporterTests.ArrayTypes +#else +using Address = System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Address; +using Employee = System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Employee; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests.ArrayTypes +#endif +{ + [Serializable] + public class Company + { + string name; + string[] products; + Address address; + [OptionalField] + Employee[] employees; + } + + // IsRequired default different for [Serializable] and [DataContract] + [DataContract(Name="Company")] + public class Company2 + { + [DataMember(IsRequired=true)] + public string name; + + [DataMember(Name="products", IsRequired=true)] + public string[] Products; + + [DataMember(IsRequired = true)] + Address address; + + [DataMember] + Employee[] employees; + + public Company2() + { + } + } + + [DataContract(Namespace="http://schemas.datacontract.org/2004/07/SerializableTypes.XsdDataContractExporterTests")] + public class Employee + { + [DataMember(IsRequired=true)] + Company company; + } + + [DataContract] + public class JaggedArrays + { + [DataMember] + Company[] companyArray_1rank; + + [DataMember] + Company[][] companyArray_2rank; + + [DataMember] + Company[][][] companyArray_3rank; + + [DataMember] + object[] objectArray_1rank; + + [DataMember] + object[][] objectArray_2rank; + + [DataMember] + ManagerEmployeeList peerList; + + [DataMember] + DateTimeOffset[] dateTimeOffsetArray_1rank; + + [DataMember] + DateTimeOffset[][] dateTimeOffsetArray_2rank; + } + + [DataContract] + public class SystemArray + { + + [DataMember] + public Array[] arrayArray; + + [DataMember] + public Array array; + + } + + [CollectionDataContract] + public class ManagerEmployeeList : List> + { + } +} + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConfigTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConfigTypes.cs new file mode 100644 index 0000000000000..93f3e69494afc --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConfigTypes.cs @@ -0,0 +1,52 @@ +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + using System; + + [Serializable] + public class ConfigBase1 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived1 : ConfigBase1 + { + } + + [Serializable] + public class ConfigBase2 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived2 : ConfigBase2 + { + } + + [Serializable] + public class ConfigBase3 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived3 : ConfigBase3 + { + } + + [Serializable] + public class ConfigBase4 + { + string foo = "bar"; + } + + [Serializable] + public class ConfigDerived4 : ConfigBase4 + { + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConflictingNameTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConflictingNameTypes.cs new file mode 100644 index 0000000000000..b44de0e7f640e --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ConflictingNameTypes.cs @@ -0,0 +1,72 @@ +using System; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class ConflictingNameTypes + { + [DataContract] + public class ConflictBase + { + [DataMember(IsRequired=true)] + int a; + } + + [DataContract] + public class ConflictDerived1 : ConflictBase + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract] + public class ConflictDerived2 : ConflictBase + { + [DataMember(IsRequired = true)] + string[] a; + } + + [DataContract] + public class ConflictDerived11 : ConflictDerived1 + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract] + public class ConflictDerived12 : ConflictDerived1 + { + [DataMember(IsRequired = true)] + string a; + } + + [DataContract] + public class NoConflictBase + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract] + public class NoConflictDerived1 : NoConflictBase + { + [DataMember(IsRequired = true)] + int a; + } + + [DataContract(Namespace="http://www.tempuri.org/")] + public class NoConflictDerived2 : NoConflictBase + { + [DataMember(IsRequired = true)] + string a; + } + + } +} + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractSurrogate.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractSurrogate.cs new file mode 100644 index 0000000000000..c7570de114bd6 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractSurrogate.cs @@ -0,0 +1,98 @@ +using System; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Serialization; +using System.Xml.Schema; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class DataContractSurrogate + { + [DataContract] + public class CircleContainer + { + [DataMember] + Circle circle; + [DataMember] + public Circle[] Circles{ get { return null;} set {}} + } + + [Serializable] + public class Circle + { + public int Radius; + } + + [Serializable] + public class Square + { + public int Side; + } + + public class Node + { + Node next; + } + + [Serializable] + public class SerializableNode + { + SerializableNode next; + } + + + [XmlRoot("XmlSerializerPersonElement")] + public class XmlSerializerPerson + { + public XmlSerializerPerson(){} + [XmlAttribute] + public string Name; + [XmlAttribute] + public int Age; + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XmlSerializerAdapter : IXmlSerializable + { + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemaSet) + { + XmlReflectionImporter importer = new XmlReflectionImporter(); + XmlTypeMapping xmlTypeMapping = importer.ImportTypeMapping(typeof(T)); + XmlSchemas schemas = new XmlSchemas(); + XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); + exporter.ExportTypeMapping(xmlTypeMapping); + schemas.Compile(new ValidationEventHandler (ValidationCallbackWithErrorCode), true); + for (int i = 0; i < schemas.Count; i++) + { + XmlSchema schema = schemas[i]; + schemaSet.Add(schema); + } + return new XmlQualifiedName(xmlTypeMapping.TypeName, xmlTypeMapping.Namespace); + } + + private static void ValidationCallbackWithErrorCode (object sender, ValidationEventArgs args) { + Console.WriteLine("Schema warning: " + args.Message); + } + } + } +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractTypes.cs new file mode 100644 index 0000000000000..37c256f96a34d --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/DataContractTypes.cs @@ -0,0 +1,607 @@ +using System; +using System.Runtime.Serialization; +using System.Collections.Generic; + +[assembly:ContractNamespace("http://special1.tempuri.org", ClrNamespace= "SerializableTypes.XsdDataContractExporterTests.More")] + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests.More +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests.More +#endif +{ + [DataContract] + public class Foo + { + [DataMember] + int id; + } + + [KnownType(typeof(GenericBasePOCO>))] + [KnownType(typeof(GenericBasePOCO))] + [KnownType(typeof(SimpleBaseContainerPOCO))] + public class GenericContainerPOCO + { + public GenericBasePOCO2 GenericData; + public object TestGenericBasePOCO; + public GenericContainerPOCO() + { + } + } + + + public class GenericBasePOCO where T : new() + { + public object genericData = new T(); + } + + + public class GenericBasePOCO2 + where T : new() + where K : new() + { + public T genericData1 = new T(); + + public K genericData2 = new K(); + } + + [KnownType(typeof(SimpleBaseDerivedPOCO2))] + public class SimpleBaseContainerPOCO + { + public SimpleBasePOCO Base1; + + public List Base2; + + public SimpleBaseContainerPOCO() + { + } + } + + [KnownType(typeof(SimpleBaseDerivedPOCO))] + public class SimpleBasePOCO + { + public string BaseData = String.Empty; + } + + + public class SimpleBaseDerivedPOCO : SimpleBasePOCO + { + public string DerivedData = String.Empty; + } + + public class SimpleBaseDerivedPOCO2 : SimpleBasePOCO + { + public string DerivedData = String.Empty; + } + + [DataContract] + [KnownType(typeof(GenericBaseDC>))] + [KnownType(typeof(SimpleBaseContainerDC))] + public class GenericContainerDC + { + [DataMember] + public GenericBaseDC2 GenericData; + public GenericContainerDC() + { + } + } + + + [DataContract] + public class GenericBaseDC where T : new() + { + [DataMember] + public object genericData = new T(); + } + + + [DataContract] + public class GenericBaseDC2 + where T : new() + where K : new() + { + [DataMember] + public T genericData1 = new T(); + + [DataMember] + public K genericData2 = new K(); + } + + [DataContract] + [KnownType(typeof(SimpleBaseDerivedDC2))] + public class SimpleBaseContainerDC + { + [DataMember] + public SimpleBaseDC Base1; + + [DataMember] + public List Base2; + + public SimpleBaseContainerDC() + { } + } + + [DataContract] + [KnownType(typeof(SimpleBaseDerivedDC))] + public class SimpleBaseDC + { + [DataMember] + public string BaseData = String.Empty; + } + + + [DataContract] + public class SimpleBaseDerivedDC : SimpleBaseDC + { + [DataMember] + public string DerivedData = String.Empty; + } + + [DataContract] + public class SimpleBaseDerivedDC2 : SimpleBaseDC + { + [DataMember] + public string DerivedData = String.Empty; + } +} + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class DataContractTypes + { + [DataContract] + public class Person1 + { + [DataMember] + public string name; + + [DataMember] + public int age; + + [DataMember] + float salary; + + Person1() + { + } + + public Person1(string init) + { + name = "John Anderson"; + age = 25; + salary = 100000; + } + } + + // Unknown data does not affect the data contract of the class + [DataContract(Name="DataContractTypes.Person1")] + public class Person2 : IExtensibleDataObject + { + public string firstName; + public string lastName; + + [DataMember(Name="name")] + internal string Name + { + get { return firstName + " " + lastName; } + private set + { + int splitIndex = value.IndexOf(' '); + firstName = value.Substring(0, splitIndex); + lastName = value.Substring(splitIndex+1); + } + } + + protected int personAge; + + [DataMember(Name="age")] + public virtual int Age + { + get { return personAge; } + set { personAge = value; } + } + + [DataMember] + internal float salary; + + protected Person2() + { + } + + public Person2(string init) + { + Name = "John Anderson"; + Age = 25; + salary = 100000; + } + + ExtensionDataObject extensionData; + public ExtensionDataObject ExtensionData + { + get { return extensionData; } + set { extensionData = value; } + } + } + + [DataContract(Name="PersonContract")] + internal class Person3 + { + [DataMember(Name="Name")] + public string name; + + [DataMember(Name="Nickname")] + public string name2; + + [DataMember(Name="Age")] + public int age; + + [DataMember(Name="Salary")] + float salary; + + [DataMember] + Address address; + + [DataMember] + public Address Address + { + get { return address; } + set { address = value; } + } + + public Person3() + { + name = "John Anderson"; + name2 = "Johny"; + age = 25; + salary = 100000; + Address = new Address(null); + } + } + + [DataContract(Name="Person")] + public struct Person4 + { + [DataMember] + public string name; + + [DataMember] + public int age; + + [DataMember] + float salary; + + [DataMember] + Address address; + + [DataMember] + DateTimeOffset hireDate; + + public Person4(StreamingContext context) + { + name = "John Anderson"; + age = 25; + salary = 100000; + address = new Address(null); + hireDate = new DateTimeOffset(new DateTime(1995, 01, 17, 5, 30, 0), new TimeSpan(1,15,120)); + } + } + + [DataContract(Name="Address")] + public class Address + { + [DataMember(IsRequired=true)] + public string street; + + string city; + + [DataMember(Name = "city", IsRequired = true)] + public string City + { + get { return city; } + set { city = value; } + } + + [DataMember(IsRequired=true)] + string state; + + int zip; + + [DataMember(Name = "zip", IsRequired = true)] + int Zip + { + get { return zip; } + set {zip = value; } + } + + public Address() + { + } + + public Address(string init) + { + street = "One FooBar Avenue"; + City = "BazTown"; + state = "WA"; + Zip = 66066; + } + } + + [DataContract(Name="Address", Namespace="http://schemas.datacontract.org/2004/07/schemaexport.suites")] + public struct Address2 + { + [DataMember] + public string street; + + [DataMember] + string city; + + string state; + + [DataMember(Name="state")] + public string State + { + get { return state; } + set { state = value; } + } + + [DataMember] + int zip; + + public Address2(string init) + { + street = "One FooBar Avenue"; + city = "BazTown"; + state = "WA"; + zip = 66066; + } + } + + [DataContract(Namespace="http://invalid.org?query")] + public class Child : Person2 + { + [DataMember(Name="age")] + public override int Age + { + get { return personAge; } + set + { + if (personAge > 18) + throw new Exception("Children must be aged 18 or younger"); + personAge = value; + } + } + + [DataMember] + Person2 mother; + [DataMember] + Person2 father; + + Child(StreamingContext context) + { + } + + public Child(string init) : base(init) + { + personAge = 13; + mother = new Person2(null); + father = null; + } + } + + [DataContract(Name="DerivedAddress")] + public class DerivedAddress : Address + { + [DataMember] + string email; + + [DataMember] + string phone; + + public DerivedAddress() : base(null) + { + email = "neo@zion.net"; + phone = "222-111-2222"; + } + } + + [DataContract(Name="Node")] + class Node + { + [DataMember] + Node nextNode; + + [DataMember] + Node previousNode; + + Node() + { + } + } + + [DataContract(Name="Node")] + class Node2 + { + [DataMember] + Node2 nextNode; + + [DataMember] + Node3 previousNode; + + Node2() + { + } + } + + [DataContract(Name="Node")] + class Node3 + { + [DataMember] + Node3 nextNode; + + [DataMember] + Node4 previousNode; + + Node3() + { + } + } + + [DataContract(Name="Node")] + class Node4 + { + [DataMember] + Node3 nextNode; + + [DataMember] + Node2 previousNode; + + Node4() + { + } + } + + [DataContract] + class A + { + [DataMember] + B member; + + A() + { + } + } + + [DataContract] + class B + { + [DataMember] + A member; + + B() + { + } + } + + [DataContract] + public class ClassWithInterfaceMember + { + [DataMember] + ISampleInterface interfaceMember; + } + + interface ISampleInterface + { + void InterfaceMethod(); + } + + [DataContract(Name="DerivedAddress2")] + public class DerivedAddress2 : DerivedAddress + { + [DataMember] + byte[] extraData; + + public DerivedAddress2() : base() + { + } + } + + [DataContract] + public class Foo + { + [DataMember(IsRequired=false)] + int i=1; + [DataMember(IsRequired=true)] + int j=3; + [DataMember(Order=3)] + string a = "a"; + [DataMember(Order=4, IsRequired=false)] + public int z = 32; + + } + + [DataContract] + public class Bar + { + [DataMember] + int i=1; + [DataMember(Order=4, IsRequired=true)] + int j=3; + [DataMember(Order=5)] + string a = "a"; + [DataMember(Order=8)] + public int z = 32; + + } + + [DataContract] + [Serializable] + public class MixedDCSerializable + { + int serializableInt = 0; + [DataMember] + int dataContractInt = 0; + } + + [Serializable] + public class SerializableOnly : MixedDCSerializable + { + string serializableString; + [NonSerialized] + int nonSerializedInt; + } + + [DataContract] + [Serializable] + public class DerivedMixedDCSerializable : SerializableOnly + { + public float serializableFloat = 0.0F; + [DataMember] + public float dataContractFloat = 0.0F; + } + + [Serializable] + public class AllPrimitives + { + public object objectMember; + public char charMember; + public bool boolMember; + public byte unsignedByteMember; + //[CLSCompliant(false)] + public sbyte byteMember; + public short shortMember; + //[CLSCompliant(false)] + public ushort unsignedShortMember; + public int intMember; + //[CLSCompliant(false)] + public uint unsignedIntMember; + public long longMember; + //[CLSCompliant(false)] + public ulong unsignedLongMember; + public float floatMember; + public double doubleMember; + public decimal decimalMember; + public DateTime dateTimeMember; + public string stringMember; + byte[] byteArrayMember; + public Guid guidMember; + public TimeSpan timeSpanMember; + public Uri uri; + } + + [DataContract] + public class AllSpecialTypes + { + [DataMember] System.Enum enumMember; + [DataMember] System.ValueType valueTypeMember; + [DataMember] System.Array arrayMember; + } + + } +} + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/Enums.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/Enums.cs new file mode 100644 index 0000000000000..4d22f65ba8325 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/Enums.cs @@ -0,0 +1,138 @@ +using System; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class Enums + { + public enum Mode + { + NotSpecified, + Single, + Multiple, + } + + [DataContract(Name="DifferentMode")] + public enum Mode2 + { + [EnumMember(Value="None")] + NotSpecified = -1, + Single, + [EnumMember] + Multiple, + } + + public enum Color : byte + { + Red, + Green, + Blue, + Reserved, + } + + //[CLSCompliant(false)] + public enum ULongRange : ulong + { + Min = UInt64.MinValue, + Small = 1, + Medium = 10, + Large = 100, + Max = UInt64.MaxValue, + } + + [Flags] + public enum FlagsEnum + { + Foo = 1, + Bar = 2, + Baz = 4, + Bazooka = 8 + } + + [Flags] + [DataContract(Namespace="http://special2.tempuri.org")] + public enum LongRange : long + { + [EnumMember] + Min = Int64.MinValue, + [EnumMember(Value="Small")] + Value1 = 1L, + Value10 = 10L, + [EnumMember(Value="Medium")] + Value100 = 100L, + [EnumMember(Value="Large")] + Value1000 = 1000L, + [EnumMember] + Max = Int64.MaxValue, + } + + [DataContract] + public class EnumContainer + { + [DataMember] + Mode mode; + + [DataMember] + Mode2 mode2; + + [DataMember] + Color color; + + [DataMember] + ULongRange enumValue; + + [DataMember] + FlagsEnum flagsEnum; + + [DataMember] + LongRange longFlagsEnum; + + public EnumContainer() + { + } + + [DataContract] + public class NestedEnumContainer + { + [DataMember] + Mode mode; + + NestedEnumContainer() + { + } + } + + internal enum NestedSimpleEnum + { + Negative = -1, + NoComment = 0, + Affirmative = 1, + } + } + } + + public enum Mode + { + NotSpecified, + Single, + Multiple, + } + + [DataContract(Name="Mode")] + public enum DifferentMode + { + [EnumMember(Value="NotSpecified")] + Member1, + [EnumMember] + Single=1, + [EnumMember(Value="Multiple")] + Mult, + } + +} + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/GenericTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/GenericTypes.cs new file mode 100644 index 0000000000000..01b0e7f76394d --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/GenericTypes.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Security; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + internal class DateTimeOffsetGeneric + { + T single; + T[] array; + List list; + Dictionary dictionary; + } + + [Serializable] + internal class Foo where Q : new() + { + Q single; + Q[] array; + List list; + Dictionary dictionary; + } + + [DataContract(Name = "PairOf{0}and{1}")] + internal struct Pair + { + [DataMember] + T1 t1; + [DataMember] + T2 t2; + } + + [KnownType(typeof(Pair>, Foo>))] + [Serializable] + public class Bar + { + Foo fooBar; + XsdType xsdFloat; + XsdType xsdDecimal; + WrapperISerializable wrappedInt; + WrapperISerializable wrappedIntArray; + DateTimeOffsetGeneric genericDateTimeOffset; + DateTimeOffsetGeneric genericDateTimeOffsetArray; + } + + [Serializable] + internal class WrapperISerializable : ISerializable + { + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) { } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XsdType : IXmlSerializable + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + const string ns = "http://GenericXmlSerializableNs"; + XmlSchema schema = new XmlSchema(); + schema.TargetNamespace = ns; + schema.Namespaces.Add("tns", ns); + schemas.Add(schema); + + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + schemaType.Name = typeof(T).Name + "Wrapper"; + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + if (typeof(T) == typeof(decimal)) + element.SchemaTypeName = new XmlQualifiedName("decimal", XmlSchema.Namespace); + else if (typeof(T) == typeof(float)) + element.SchemaTypeName = new XmlQualifiedName("float", XmlSchema.Namespace); + else + element.SchemaTypeName = new XmlQualifiedName("anyType", XmlSchema.Namespace); + sequence.Items.Add(element); + schema.Items.Add(schemaType); + schemas.Add(schema); + return schemaType; + } + + public XmlSchema GetSchema() { throw new NotImplementedException(); } + public void ReadXml(XmlReader reader) { throw new NotImplementedException(); } + public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); } + } + +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ISerializableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ISerializableTypes.cs new file mode 100644 index 0000000000000..e4e667cfb1954 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/ISerializableTypes.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections; +using System.Runtime.Serialization; +using System.Security; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + public class BaseISerializable : ISerializable + { + protected BaseISerializable() + { + } + + protected BaseISerializable(SerializationInfo info, StreamingContext context) + { + } + + public string street; + int zip; + [NonSerialized] + float privateData; + + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + } + } + + [Serializable] + public class DerivedISerializable : BaseISerializable + { + DerivedISerializable(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + } + + [Serializable] + public class MyUri : Uri + { + protected MyUri(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + [Serializable] + public class MyDerivedUri : MyUri + { + protected MyDerivedUri(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + [Serializable] + public struct StructISerializable : ISerializable + { + public StructISerializable(SerializationInfo serInfo, StreamingContext context) + { + } + + [SecurityCritical] + public void GetObjectData(SerializationInfo serInfo, StreamingContext context) + { + } + + } + + [DataContract] + public class UseISerializable + { + [DataMember] + Hashtable hashtable; + + [DataMember] + InvalidOperationException exception; + +#if !HideTypesWithoutSerializableAttribute + [DataMember] + System.Reflection.Assembly assembly; + + [DataMember] + System.IO.DirectoryInfo directoryInfo; +#endif + [DataMember] + StructISerializable structISerMember; + + [DataMember] + BaseISerializable classISerMember; + } + + [Serializable] + public class ISerializableDerivingDC : DataContractTypes.Address, ISerializable + { + public ISerializableDerivingDC(SerializationInfo serInfo, StreamingContext context) + { + } + + [SecurityCritical] + void ISerializable.GetObjectData(SerializationInfo serInfo, StreamingContext context) + { + } + } + +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/IXmlSerializableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/IXmlSerializableTypes.cs new file mode 100644 index 0000000000000..e42d9fb96407f --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/IXmlSerializableTypes.cs @@ -0,0 +1,399 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlTypes; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class XmlSerializableBase : IXmlSerializable + { + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + public static XmlSchema GetSchema(string ns, XmlSchemaSet schemas) + { + if (ns == null) + { + ns = String.Empty; + } + + ICollection currentSchemas = schemas.Schemas(); + foreach (XmlSchema schema in currentSchemas) + { + if ((schema.TargetNamespace == null && ns.Length == 0) || ns.Equals(schema.TargetNamespace)) + return schema; + } + if (ns.Length > 0) + { + XmlSchema newSchema = new XmlSchema(); + newSchema.TargetNamespace = ns; + newSchema.Namespaces.Add("tns", ns); + schemas.Add(newSchema); + return newSchema; + } + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="ComplexTypeElement", Namespace ="http://ElementNs/", IsNullable = false)] + public class ComplexType : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchema schema = GetSchema("http://TypeNs/", schemas); + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + schemaType.Name = "MyComplexType"; + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + sequence.Items.Add(element); + schema.Items.Add(schemaType); + schemas.Add(schema); + return schemaType; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class SimpleType : XmlSerializableBase + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchema schema = GetSchema("http://TypeNs/", schemas); + XmlSchemaSimpleType schemaType = new XmlSchemaSimpleType(); + schemaType.Name = "MySimpleType"; + XmlSchemaSimpleTypeRestriction content = new XmlSchemaSimpleTypeRestriction(); + schemaType.Content = content; + content.BaseType = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Boolean); + schema.Items.Add(schemaType); + schemas.Add(schema); + return new XmlQualifiedName(schemaType.Name, schema.TargetNamespace); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="IntElement", Namespace ="http://ElementNs/", IsNullable = false)] + public struct XsdInt : IXmlSerializable + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchemaType schemaType = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Int); + return schemaType.QualifiedName; + } + + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="StringElement", Namespace ="http://ElementNs/", IsNullable = true)] + public class XsdString : IXmlSerializable + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchemaType schemaType = XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String); + return schemaType.QualifiedName; + } + + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName="ComplexStructElement", Namespace="http://ElementNs/", IsNullable = false)] + public struct ComplexStruct : IXmlSerializable + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchema schema = XmlSerializableBase.GetSchema("http://TypeNs/", schemas); + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + schemaType.Name = "MyComplexStruct"; + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + sequence.Items.Add(element); + schema.Items.Add(schemaType); + schemas.Add(schema); + return schemaType; + } + + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("StaticGetSchema")] + [XmlRoot(ElementName ="AnonElement", Namespace ="http://ElementNs/", IsNullable = true)] + public class AnonymousType : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + XmlSchemaComplexType schemaType = new XmlSchemaComplexType(); + XmlSchemaSequence sequence = new XmlSchemaSequence(); + schemaType.Particle = sequence; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "MyElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + sequence.Items.Add(element); + return schemaType; + } + } + + public class NoSchema : XmlSerializableBase + { + } + + [XmlRoot] + public class EmptyXmlRoot : XmlSerializableBase + { + } + + [XmlRoot(IsNullable=true)] + public class NullableOnlyXmlRoot : XmlSerializableBase + { + } + + [XmlRoot(ElementName=null)] + public class NullElementXmlRoot : XmlSerializableBase + { + } + + [XmlRoot(ElementName="")] + public class EmptyElementXmlRoot : XmlSerializableBase + { + } + + [XmlSchemaProvider(null, IsAny=true)] + public class AnyBasic : XmlSerializableBase + { + } + + [XmlSchemaProvider("StaticGetSchema", IsAny = true)] + public class AnyWithSchemaTypeMethod : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema", IsAny = true)] + public class AnyWithQnameMethod : XmlSerializableBase + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class AnyImplicitWithSchemaTypeMethod : XmlSerializableBase + { + static XmlSchemaType StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + [XmlSchemaProvider("StaticGetSchema")] + public class AnyImplicitWithQnameMethod : XmlSerializableBase + { + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemas) + { + return null; + } + } + + public class NoSchemaProviderWithSchema : IXmlSerializable + { + public XmlSchema GetSchema() + { + XmlSchema schema = new XmlSchema(); + schema.Id = this.GetType().Name; + XmlSchemaElement element = new XmlSchemaElement(); + element.Name = "userElement"; + element.SchemaTypeName = new XmlQualifiedName("int", XmlSchema.Namespace); + schema.Items.Add(element); + return schema; + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } + + [XmlSchemaProvider("GetTypedDataSetSchema")] + [XmlRoot("TypedDataSet", Namespace = "http://datasetns/")] + public class TypedDataSet : DataSet + { + public static System.Xml.Schema.XmlSchemaComplexType GetTypedDataSetSchema(System.Xml.Schema.XmlSchemaSet xs) + { + TypedDataSet ds = new TypedDataSet(); + System.Xml.Schema.XmlSchemaComplexType type = new System.Xml.Schema.XmlSchemaComplexType(); + System.Xml.Schema.XmlSchemaSequence sequence = new System.Xml.Schema.XmlSchemaSequence(); + xs.Add(ds.GetSchemaSerializable()); + if (PublishLegacyWSDL()) + { + System.Xml.Schema.XmlSchemaAny any = new System.Xml.Schema.XmlSchemaAny(); + any.Namespace = ds.Namespace; + sequence.Items.Add(any); + } + else + { + System.Xml.Schema.XmlSchemaAny any1 = new System.Xml.Schema.XmlSchemaAny(); + any1.Namespace = "http://www.w3.org/2001/XMLSchema"; + any1.MinOccurs = new System.Decimal(0); + any1.ProcessContents = System.Xml.Schema.XmlSchemaContentProcessing.Lax; + sequence.Items.Add(any1); + System.Xml.Schema.XmlSchemaAny any2 = new System.Xml.Schema.XmlSchemaAny(); + any2.Namespace = "urn:schemas-microsoft-com:xml-diffgram-v1"; + any2.MinOccurs = new System.Decimal(0); + any2.ProcessContents = System.Xml.Schema.XmlSchemaContentProcessing.Lax; + sequence.Items.Add(any2); + sequence.MaxOccurs = System.Decimal.MaxValue; + System.Xml.Schema.XmlSchemaAttribute attribute = new System.Xml.Schema.XmlSchemaAttribute(); + attribute.Name = "namespace"; + attribute.FixedValue = ds.Namespace; + type.Attributes.Add(attribute); + } + type.Particle = sequence; + return type; + } + protected override System.Xml.Schema.XmlSchema GetSchemaSerializable() + { + System.IO.MemoryStream stream = new System.IO.MemoryStream(); + this.WriteXmlSchema(new System.Xml.XmlTextWriter(stream, null)); + stream.Position = 0; + return System.Xml.Schema.XmlSchema.Read(new System.Xml.XmlTextReader(stream), null); + } + protected static bool PublishLegacyWSDL() + { + //System.Collections.Specialized.NameValueCollection settings = ((System.Collections.Specialized.NameValueCollection)(System.Configuration.ConfigurationManager.GetSection("system.data.dataset"))); + //if ((settings != null)) + //{ + // string[] values = settings.GetValues("WSDL_VERSION"); + // if ((values != null)) + // { + // System.Single version = System.Single.Parse(((string)(values[0])), ((System.IFormatProvider)(null))); + // return(version < 2); + // } + //} + return true; + } + } + + [Serializable] + public class IXmlSerializablesContainer + { + ComplexType complexType; + ComplexStruct complexStruct; + SimpleType simpleType; + AnonymousType anonymousType; + NoSchema noSchema; + XsdInt xsdInt; + XsdString xsdString; + DataSet dataSet; + TypedDataSet typedDataSet; + AnyBasic anyBasic; + AnyBasic[] anyArray; + AnyWithSchemaTypeMethod anyWithSchemaType; + AnyWithQnameMethod anyWithQname; + AnyImplicitWithSchemaTypeMethod anyImplicitWithSchemaType; + AnyImplicitWithQnameMethod anyImplicitWithQname; + NoSchemaProviderWithSchema noSchemaProviderWithSchema; + XmlElement xmlElement; + XmlElement[] xmlElementArray; + XmlNode[] xmlNodes; + XmlNode[][] xmlNodesArray; + Dictionary xmlElementDictionary; + } + + [Serializable] + public class SqlTypeContainer + { + // The following were disabled in NetFx test... but should work now. + // SqlBinary, SqlChars, SqlInt32, SqlString, SqlDateTime, SqlGuid + + public SqlBinary sqlBinary = new SqlBinary(new byte[]{4,2}); + public SqlByte sqlByte = new SqlByte(4); + public SqlBytes sqlBytes = new SqlBytes(new byte[]{4,2}); + public SqlChars sqlChars = new SqlChars(new char[]{'4', '2'}); + public SqlDecimal sqlDecimal = new SqlDecimal(4.2); + public SqlDouble sqlDouble = new SqlDouble(4.2); + public SqlInt16 sqlInt16 = new SqlInt16(42); + public SqlInt32 sqlInt32 = new SqlInt32(42); + public SqlInt64 sqlInt64 = new SqlInt64(42L); + public SqlMoney sqlMoney = new SqlMoney(42); + public SqlSingle sqlSingle = new SqlSingle(4.2); + public SqlString sqlString = new SqlString("MySqlString"); + public SqlDateTime sqlDateTime = new SqlDateTime(); + public SqlGuid sqlGuid = new SqlGuid(); + } +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/LegacyTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/LegacyTypes.cs new file mode 100644 index 0000000000000..332cb876f6639 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/LegacyTypes.cs @@ -0,0 +1,42 @@ +using System; +using System.Xml; +using System.Collections; +using System.Collections.Generic; +using System.Security; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + [SecuritySafeCritical] +#if UseSeparateAssemblyNamespace + public unsafe class LegacyTypes +#else + public class LegacyTypes +#endif + { + Hashtable h; + List lInt; + public IList list; + public IList stringList; + public ICollection collection; + public ICollection stringCollection; + public IEnumerable enumerable; + public IEnumerable stringEnumerable; + public IDictionary dictionary; + public IDictionary dictionaryOfStringToInt; + Dictionary dExVer; +#if !HideTypesWithoutSerializableAttribute + float* f; +#endif + IntPtr iPtr; + DBNull dbNull; + } + +} + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/NullableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/NullableTypes.cs new file mode 100644 index 0000000000000..109ab772167b2 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/NullableTypes.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.Serialization; +using System.Security; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [DataContract] + public struct Point + { + + Nullable x; + Nullable y; + + [DataMember] + public Nullable X { get { return x; } set { x = value; } } + [DataMember] + public Nullable Y { get { return y; } set { y = value; } } + + } + + [DataContract] + public struct Rectangle + { + [DataMember] + public Nullable TopLeft; + [DataMember] + public Point? BottomRight; + } + + [Serializable] + [KnownType(typeof(Point))] + public struct Polygon : ISerializable + { + Nullable[] points; + + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + } + } + + [Serializable] + [KnownType(typeof(Polygon))] + [KnownType(typeof(Nullable))] + [KnownType(typeof(Container))] + public class Container + { + Polygon polygon; + Rectangle? excessivelyNullableRectangle; //not excessively anymore + Nullable[] nullableInts; + Nullable[][] nullableLongss; + } + + [DataContract] + public struct NullableDateTimeOffset + { + Nullable nullableDTO; + + [DataMember] + public Nullable NullableDTO { get { return nullableDTO; } set { nullableDTO = value; } } + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/PartialTrust.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/PartialTrust.cs new file mode 100644 index 0000000000000..596176da62008 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/PartialTrust.cs @@ -0,0 +1,110 @@ +using System; +using System.Runtime.Serialization; +using System.Security; +using System.Security.Permissions; +using System.Xml; +using System.Xml.Serialization; +using System.Xml.Schema; + +[assembly:AllowPartiallyTrustedCallers] +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + public class PartialTrust + { + [Serializable] + //[SerializationPermissionNotRequired] + public class SafePoint + { + int x = 42, y = 43; + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class SafePoint3D : SafePoint + { + int z = 44; + DateTimeOffset dateCreated = new DateTimeOffset(new DateTime(1997, 03, 11, 07, 15, 30), new TimeSpan(1,2,60)); + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class SafeCube + { + SafePoint3D topLeftBehind = new SafePoint3D(); + SafePoint3D bottomRightFront = new SafePoint3D(); + } + + [Serializable] + public class UnsafePoint + { + int x = 42, y = 43; + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class UnsafePoint3D : UnsafePoint + { + int z = 44; + } + + [Serializable] + //[SerializationPermissionNotRequired] + public class UnsafeCube + { + UnsafePoint3D topLeftBehind = new UnsafePoint3D(); + UnsafePoint3D bottomRightFront = new UnsafePoint3D(); + } + + //[SerializationPermissionNotRequired] + public class AttributeOnlyIXmlSerializable : IXmlSerializable + { + public AttributeOnlyIXmlSerializable() + { + // This was not commented in NetFx. It clutters output though and seems unneccesary for our needs. + //Console.WriteLine("Default Ctor"); + } + + public AttributeOnlyIXmlSerializable(string init) + { + + } + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + Console.WriteLine(reader.NodeType + " " + reader.Name); + Console.WriteLine("Value1 = " + reader.GetAttribute("myAttribute1")); + Console.WriteLine("Value2 = " + reader.GetAttribute("myAttribute2")); + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteAttributeString("myAttribute1", "", "myAttribute1Value"); + writer.WriteAttributeString("myAttribute2", "", "myAttribute2Value"); + } + } + + public class UnsafeAttributeOnlyIXmlSerializable : AttributeOnlyIXmlSerializable + { + public UnsafeAttributeOnlyIXmlSerializable() + { + //may be called to invoke GetSchema() method + } + + public UnsafeAttributeOnlyIXmlSerializable(string init) + { + + } + + } + } +} + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializableTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializableTypes.cs new file mode 100644 index 0000000000000..87fc463c59c8c --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializableTypes.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using System.Runtime.Serialization; + +#if UseSeparateAssemblyNamespace +namespace SerializableTypes.XsdDataContractExporterTests +#else +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +#endif +{ + [Serializable] + public class Address + { + public string street; + string city; + string state; + int zip; + + [NonSerialized] + float privateData; + + public string Apartment + { + get { return null; } + set { } + } + + public Address() + { + } + } + + [Serializable] + public struct Address2 + { + public string street; + string city; + string state; + int zip; + + [NonSerialized] + float privateData; + + } + + [Serializable] + public class Employee //: DataContractTypes.Person2 + { + ArrayTypes.Company company; + + Employee(StreamingContext context) + { + } + } + + [Serializable] + [KnownType(typeof(ArrayList))] + [KnownType(typeof(int))] + [KnownType(typeof(DateTime))] + [KnownType(typeof(Employee))] + [KnownType(typeof(ObjectContainer))] + public class ObjectContainer + { + object obj; + } + +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializationTypes.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializationTypes.csproj new file mode 100644 index 0000000000000..440b641b2ac93 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SerializationTypes/SerializationTypes.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.1 + true + UseSeparateAssemblyNamespace;HideTypesWithoutSerializableAttribute + $(NoWarn);169;414 + + + + + all + + + + + + + + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SurrogateTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SurrogateTests.cs new file mode 100644 index 0000000000000..7a2c32aa527d9 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/SurrogateTests.cs @@ -0,0 +1,525 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests +{ + public class SurrogateTests + { + private readonly ITestOutputHelper _output; + public SurrogateTests(ITestOutputHelper output) + { + _output = output; + } + + [Theory] + [MemberData(nameof(SurrogateProvider_MemberData))] + public void SurrogateProvider(Type type, ISerializationSurrogateProvider surrogate, Action schemaCheck = null) + { + ExportOptions options = new ExportOptions() { DataContractSurrogate = surrogate }; + XsdDataContractExporter exporter = new XsdDataContractExporter() { Options = options }; + + exporter.Export(type); + string schema = SchemaUtils.DumpSchema(exporter.Schemas); + _output.WriteLine(schema); + + if (schemaCheck != null) + schemaCheck(schema, exporter.Schemas); + } + public static IEnumerable SurrogateProvider_MemberData() + { + yield return new object[] { typeof(SurrogateTests.CircleContainer), new NodeToSerializableNode(new CircleToSquare(new XmlSerializerToXmlFormatter(null))), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Property", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Field", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"7", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Field", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.Node), new NodeToSerializableNode(new CircleToSquare(new XmlSerializerToXmlFormatter(null))), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"7", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"0", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"Field", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.XmlSerializerPerson), new NodeToSerializableNode(new CircleToSquare(new XmlSerializerToXmlFormatter(null))), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"XmlSerializable", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.ValidSurrogateTest), new PersonSurrogate(), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + yield return new object[] { typeof(SurrogateTests.ValidSurrogateTestDC), new PersonSurrogate(), (string s, XmlSchemaSet ss) => { + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + SchemaUtils.OrderedContains(@"", ref s); + } }; + } + + [Theory] + [MemberData(nameof(SurrogateProvider_Negative_MemberData))] + public void SurrogateProvider_Negative(Type badType, ISerializationSurrogateProvider surrogate, Type exceptionType, string exMsg = null) + { + XsdDataContractExporter exporter = new XsdDataContractExporter(); + exporter.Options = new ExportOptions(); + exporter.Options.DataContractSurrogate = surrogate; + + var ex = Assert.Throws(exceptionType, () => exporter.Export(badType)); + if (exMsg != null) + Assert.Equal(exMsg, ex.Message); + } + public static IEnumerable SurrogateProvider_Negative_MemberData() + { + yield return new object[] { typeof(SurrogateTests.InvalidSurrogateTest), new CollectionASurrogate(), typeof(InvalidDataContractException) }; + yield return new object[] { typeof(SurrogateTests.InvalidSurrogateTestDC), new CollectionASurrogate(), typeof(InvalidDataContractException) }; + } + + #region SurrogateProviders + public class CircleToSquare : ISerializationSurrogateProvider2 + { + ISerializationSurrogateProvider2? _nextSurrogate; + public CircleToSquare(ISerializationSurrogateProvider2? nextSurrogate) + { + this._nextSurrogate = nextSurrogate; + } + + public Type GetSurrogateType(Type type) + { + if (type == typeof(SurrogateTests.Circle)) + return typeof(SurrogateTests.Square); + return (_nextSurrogate != null) ? _nextSurrogate.GetSurrogateType(type) : type; + } + + public object GetCustomDataToExport(Type clrType, Type dcType) + { + if (clrType == typeof(SurrogateTests.Circle) && dcType == typeof(SurrogateTests.Square)) + return clrType.Assembly.GetName().Version; + return (_nextSurrogate != null) ? _nextSurrogate.GetCustomDataToExport(clrType, dcType) : null; + } + + public object GetCustomDataToExport(MemberInfo memberInfo, Type dcType) => memberInfo.MemberType.ToString(); + public void GetKnownCustomDataTypes(Collection knownTypes) { } + public object GetObjectToSerialize(object obj, Type memberType) => throw new NotImplementedException(); + public object GetDeserializedObject(object obj, Type memberType) => throw new NotImplementedException(); + public Type GetReferencedTypeOnImport(string name, string ns, object customData) => null; + } + + public class NodeToSerializableNode : ISerializationSurrogateProvider2 + { + ISerializationSurrogateProvider2? _nextSurrogate; + public NodeToSerializableNode(ISerializationSurrogateProvider2? nextSurrogate) + { + this._nextSurrogate = nextSurrogate; + } + + public Type GetSurrogateType(Type type) + { + if (type == typeof(SurrogateTests.Node)) + return typeof(SurrogateTests.SerializableNode); + return (_nextSurrogate != null) ? _nextSurrogate.GetSurrogateType(type) : type; + } + + public object GetCustomDataToExport(Type clrType, Type dcType) + { + if (clrType == typeof(SurrogateTests.Node) && dcType == typeof(SurrogateTests.SerializableNode)) + return clrType.Assembly.GetName().Version; + return (_nextSurrogate != null) ? _nextSurrogate.GetCustomDataToExport(clrType, dcType) : null; + } + + public object GetCustomDataToExport(MemberInfo memberInfo, Type dcType) => memberInfo.MemberType.ToString(); + public void GetKnownCustomDataTypes(Collection knownTypes) => knownTypes.Add(typeof(Version)); + public object GetObjectToSerialize(object obj, Type memberType) => throw new NotImplementedException(); + public object GetDeserializedObject(object obj, Type memberType) => throw new NotImplementedException(); + public Type GetReferencedTypeOnImport(string name, string ns, object customData) => null; + } + + + public class XmlSerializerToXmlFormatter : ISerializationSurrogateProvider2 + { + ISerializationSurrogateProvider2? _nextSurrogate; + public XmlSerializerToXmlFormatter(ISerializationSurrogateProvider2? nextSurrogate) + { + this._nextSurrogate = nextSurrogate; + } + + public Type GetSurrogateType(Type type) + { + if (type == typeof(SurrogateTests.XmlSerializerPerson)) + return typeof(SurrogateTests.XmlSerializerAdapter); + return (_nextSurrogate != null) ? _nextSurrogate.GetSurrogateType(type) : type; + } + + public object GetCustomDataToExport(Type clrType, Type dcType) + { + if (clrType == typeof(SurrogateTests.XmlSerializerPerson) && dcType == typeof(SurrogateTests.XmlSerializerAdapter)) + return "XmlSerializable"; + return (_nextSurrogate != null) ? _nextSurrogate.GetCustomDataToExport(clrType, dcType) : null; + } + + public object GetCustomDataToExport(MemberInfo memberInfo, Type dcType) => memberInfo.MemberType.ToString(); + public void GetKnownCustomDataTypes(Collection knownTypes) { } + public Type GetReferencedTypeOnImport(string name, string ns, object customData) => null; + public object GetObjectToSerialize(object obj, Type memberType) => throw new NotImplementedException(); + public object GetDeserializedObject(object obj, Type memberType) => throw new NotImplementedException(); + } + + class PersonSurrogate : ISerializationSurrogateProvider2 + { + public Type GetSurrogateType(Type type) + { + if (typeof(SurrogateTests.NonSerializablePerson).IsAssignableFrom(type)) + { + return typeof(SurrogateTests.Person); + } + + if (typeof(SurrogateTests.NonSerializablePersonDC).IsAssignableFrom(type)) + { + return typeof(SurrogateTests.PersonDC); + } + return type; + } + + public object GetObjectToSerialize(object obj, Type targetType) + { + SurrogateTests.NonSerializablePerson nonSerializablePerson = obj as SurrogateTests.NonSerializablePerson; + if (nonSerializablePerson != null) + { + return new Person(); + } + SurrogateTests.NonSerializablePersonDC nonSerializablePersonDC = obj as SurrogateTests.NonSerializablePersonDC; + if (nonSerializablePersonDC != null) + { + return new SurrogateTests.PersonDC(); + } + return obj; + } + + public object GetDeserializedObject(object obj, Type targetType) + { + SurrogateTests.Person ps = obj as SurrogateTests.Person; + if (ps != null) + { + return new SurrogateTests.NonSerializablePerson("John Smith"); + } + SurrogateTests.PersonDC psDC = obj as SurrogateTests.PersonDC; + if (psDC != null) + { + return new SurrogateTests.NonSerializablePersonDC("John Smith"); + } + return obj; + } + + public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) + { + if (typeNamespace.Equals("http://schemas.datacontract.org/2004/07/Suites.SchemaExport")) + { + if (typeName.Equals("DataContractSurrogateTest.Person")) + { + return typeof(SurrogateTests.NonSerializablePerson); + } + if (typeName.Equals("DataContractSurrogateTest.PersonDC")) + { + return typeof(SurrogateTests.NonSerializablePersonDC); + } + } + return null; + } + + public object GetCustomDataToExport(Type clrType, Type dataContractType) => null; + public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType) => null; + public void GetKnownCustomDataTypes(Collection customDataTypes) { } + } + + //This is the surrogate that substitutes CollectionWithoutParameterlessCtor for CollectionA. + class CollectionASurrogate : ISerializationSurrogateProvider2 + { + public Type GetSurrogateType(Type type) + { + if (typeof(ExporterTypesTests.CollectionA).IsAssignableFrom(type)) + { + return typeof(ExporterTypesTests.CollectionWithoutParameterlessCtor); + } + return type; + } + + public object GetObjectToSerialize(object obj, Type targetType) + { + ExporterTypesTests.CollectionA collectionA = obj as ExporterTypesTests.CollectionA; + if (collectionA != null) + { + ExporterTypesTests.CollectionWithoutParameterlessCtor validCollection = new ExporterTypesTests.CollectionWithoutParameterlessCtor(1); + validCollection.Add(1); + return validCollection; + } + return obj; + } + + public object GetDeserializedObject(object obj, Type targetType) + { + ExporterTypesTests.CollectionWithoutParameterlessCtor validCollection = obj as ExporterTypesTests.CollectionWithoutParameterlessCtor; + if (validCollection != null) + { + return new ExporterTypesTests.CollectionA(); + } + return obj; + } + + public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) + { + if (typeNamespace.Equals("http://schemas.datacontract.org/2004/07/Suites.SchemaExport")) + { + if (typeName.Equals("ExporterTypesTests.CollectionWithoutParameterlessCtor`1")) + { + return typeof(ExporterTypesTests.CollectionA); + } + } + return null; + } + + public object GetCustomDataToExport(Type clrType, Type dataContractType) => null; + public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType) => null; + public void GetKnownCustomDataTypes(Collection customDataTypes) { } + } + #endregion + + #region Surrogate Test Types +#pragma warning disable CS0169, CS0414 + public class ValidSurrogateTest + { + ExporterTypesTests.CollectionWithoutParameterlessCtor friends; + + public ExporterTypesTests.CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new ExporterTypesTests.CollectionWithoutParameterlessCtor(2); + return friends; + } + } + } + + public class InvalidSurrogateTest + { + ExporterTypesTests.CollectionA localList = new ExporterTypesTests.CollectionA(); + + public ExporterTypesTests.CollectionA Surrogated + { + get + { + return localList; + } + } + } + + [DataContract] + public class ValidSurrogateTestDC + { + ExporterTypesTests.CollectionWithoutParameterlessCtor friends; + + [DataMember] + public ExporterTypesTests.CollectionWithoutParameterlessCtor Friends + { + get + { + friends = friends ?? new ExporterTypesTests.CollectionWithoutParameterlessCtor(2); + return friends; + } + } + } + + [DataContract] + public class InvalidSurrogateTestDC + { + ExporterTypesTests.CollectionA localList = new ExporterTypesTests.CollectionA(); + + [DataMember] + public ExporterTypesTests.CollectionA Surrogated + { + get + { + return localList; + } + } + } + + [DataContract] + public class CircleContainer + { + [DataMember] + Circle circle; + [DataMember] + public Circle[] Circles { get { return null; } set { } } + } + + [Serializable] + public class Circle + { + public int Radius; + } + + [Serializable] + public class Square + { + public int Side; + } + + public class Node + { + Node next; + } + + [Serializable] + public class SerializableNode + { + SerializableNode next; + } + + [XmlRoot("XmlSerializerPersonElement")] + public class XmlSerializerPerson + { + public XmlSerializerPerson() { } + [XmlAttribute] + public string Name; + [XmlAttribute] + public int Age; + } + + [XmlSchemaProvider("StaticGetSchema")] + public class XmlSerializerAdapter : IXmlSerializable + { + public XmlSchema GetSchema() + { + throw new NotImplementedException(); + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + + static XmlQualifiedName StaticGetSchema(XmlSchemaSet schemaSet) + { + XmlReflectionImporter importer = new XmlReflectionImporter(); + XmlTypeMapping xmlTypeMapping = importer.ImportTypeMapping(typeof(T)); + XmlSchemas schemas = new XmlSchemas(); + XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); + exporter.ExportTypeMapping(xmlTypeMapping); + schemas.Compile(new ValidationEventHandler(ValidationCallbackWithErrorCode), true); + for (int i = 0; i < schemas.Count; i++) + { + XmlSchema schema = schemas[i]; + schemaSet.Add(schema); + } + return new XmlQualifiedName(xmlTypeMapping.TypeName, xmlTypeMapping.Namespace); + } + + private static void ValidationCallbackWithErrorCode(object sender, ValidationEventArgs args) + { + Console.WriteLine("Schema warning: " + args.Message); + } + } + + public class Person + { + public string name = "John Smith"; + } + + public class NonSerializablePerson + { + public string name; + + public NonSerializablePerson(string name) + { + this.name = name; + } + } + + [DataContract] + public class PersonDC + { + [DataMember] + public string name = "John Smith"; + } + + public class NonSerializablePersonDC + { + public string name; + + public NonSerializablePersonDC(string name) + { + this.name = name; + } + } +#pragma warning restore CS0169, CS0414 + #endregion + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/Types.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/Types.cs new file mode 100644 index 0000000000000..f50cdb081d524 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XsdDataContractExporterTests/Types.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; + +namespace System.Runtime.Serialization.Xml.XsdDataContractExporterTests.Types +{ + [DataContract(Namespace = "http://basic")] + public class Point + { + [DataMember] + public int X = 42; + [DataMember] + public int Y = 43; + } + + [DataContract(Namespace = "http://shapes")] + public class Circle + { + [DataMember] + public Point Center = new Point(); + [DataMember] + public int Radius = 5; + } + + [DataContract(Namespace = "http://shapes")] + public class Square + { + [DataMember] + public Point BottomLeft = new Point(); + [DataMember] + public int Side = 5; + } + + public class NonSerializableSquare + { + public int Length = 5; + + public NonSerializableSquare(int length) + { + Length = length; + } + } + + public struct NonAttributedPersonStruct + { + public string firstName; + public string lastName; + } + + public class NonAttributedPersonClass + { + public string firstName = "John"; + public string lastName = "Smith"; + + internal NonAttributedPersonClass() + { + } + } + + public class ExtendedSquare : Square + { + public string lineColor = "black"; + } + + public class RecursiveCollection1 : IEnumerable + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class RecursiveCollection2 : IEnumerable> + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class Box + { + } + + public class RecursiveCollection3 : IEnumerable>> + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator>> GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class RecursiveCollection4 : IEnumerable> + { + public void Add(RecursiveCollection1 item) + { + } + + public IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj index 243d4766ec96a..cc75876fe686c 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj @@ -2,12 +2,14 @@ $(NetCoreAppCurrent) + + - - - + + + diff --git a/src/libraries/oob.proj b/src/libraries/oob.proj index cc910f2d6c8c4..f8232a88803fe 100644 --- a/src/libraries/oob.proj +++ b/src/libraries/oob.proj @@ -44,7 +44,7 @@ - + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 18b04c046cf07..5b8767f56a447 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -546,6 +546,7 @@ +