diff --git a/README.md b/README.md index a3a137d2..f71d2356 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Features from schema restrictions * Use [`Collection`](http://msdn.microsoft.com/en-us/library/ms132397.aspx) properties (initialized in constructor and with private setter) -* Use either int, long, decimal, or string for xs:integer and derived types +* Map xs:integer and derived types to the closest possible .NET type, if not possible - fall back to string. Can be overriden by explicitly defined type (int, long, or decimal) * Automatic properties * Pascal case for classes and properties * Generate nullable adapter properties for optional elements and attributes without default values (see [below](#nullables)) @@ -255,6 +255,26 @@ Collection types Values for the `--collectionType` and `--collectionImplementationType` options have to be given in the format accepted by the [`Type.GetType()`](https://docs.microsoft.com/en-us/dotnet/api/system.type.gettype) method. For the `System.Collections.Generic.List` class this means ``System.Collections.Generic.List`1``. +Integer and derived types +--------------------- +Not all numeric types defined by XML Schema can be safely and accurately mapped to .NET numeric data types, however, it's possible to approximate the mapping based on the integer bounds and restrictions such as `totalDigits`. +If an explicit integer type mapping is specified via `--integer=TYPE`, that type will be used, otherwise an approximation will be made based on the following table: + +| XML Schema type | totalDigits | C# type| +|-----------------|-------------|---------| +| xs:positiveInteger, xs:nonNegativeInteger| <3 | byte | +| xs:positiveInteger, xs:nonNegativeInteger| <5 | ushort | +| xs:positiveInteger, xs:nonNegativeInteger| <10 | uint | +| xs:positiveInteger, xs:nonNegativeInteger| <20 | ulong | +| xs:positiveInteger, xs:nonNegativeInteger| <30 | decimal | +| xs:positiveInteger, xs:nonNegativeInteger| >=30 | string | +| xs:integer, xs:nonPositiveInteger, xs:negativeInteger| <3 | sbyte | +| xs:integer, xs:nonPositiveInteger, xs:negativeInteger| <5 | short | +| xs:integer, xs:nonPositiveInteger, xs:negativeInteger| <10 | int | +| xs:integer, xs:nonPositiveInteger, xs:negativeInteger| <19 | long | +| xs:integer, xs:nonPositiveInteger, xs:negativeInteger| <29 | decimal | +| xs:integer, xs:nonPositiveInteger, xs:negativeInteger| >=29 | string | + Contributing ------------ diff --git a/XmlSchemaClassGenerator.Tests/XmlSchemaClassGenerator.Tests.csproj b/XmlSchemaClassGenerator.Tests/XmlSchemaClassGenerator.Tests.csproj index 4226be6e..125472a8 100644 --- a/XmlSchemaClassGenerator.Tests/XmlSchemaClassGenerator.Tests.csproj +++ b/XmlSchemaClassGenerator.Tests/XmlSchemaClassGenerator.Tests.csproj @@ -137,6 +137,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index 2538efda..88f48636 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -1,7 +1,5 @@ -using Ganss.IO; -using Microsoft.CodeAnalysis; -using Microsoft.Xml.XMLGen; -using System; +using System; +using System.CodeDom; using System.Collections.Generic; using System.IO; using System.Linq; @@ -11,6 +9,9 @@ using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; +using Ganss.IO; +using Microsoft.CodeAnalysis; +using Microsoft.Xml.XMLGen; using Xunit; using Xunit.Abstractions; @@ -42,6 +43,7 @@ private IEnumerable ConvertXml(string name, string xsd, Generator genera EntityFramework = generatorPrototype.EntityFramework, GenerateInterfaces = generatorPrototype.GenerateInterfaces, MemberVisitor = generatorPrototype.MemberVisitor, + CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions }; var set = new XmlSchemaSet(); @@ -386,7 +388,103 @@ public void DontGenerateElementForEmptyCollectionInChoice() Assert.DoesNotContain("tags", xml, StringComparison.OrdinalIgnoreCase); } - [Fact] + + [Theory] + [InlineData(CodeTypeReferenceOptions.GlobalReference, "[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]")] + [InlineData((CodeTypeReferenceOptions)0, "[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]")] + public void EditorBrowsableAttributeRespectsCodeTypeReferenceOptions(CodeTypeReferenceOptions codeTypeReferenceOptions, string expectedLine) + { + const string xsd = @" + + + + + + + + + + + + +"; + + var generatedType = ConvertXml(nameof(EditorBrowsableAttributeRespectsCodeTypeReferenceOptions), xsd, new Generator + { + CodeTypeReferenceOptions = codeTypeReferenceOptions, + GenerateNullables = true, + GenerateInterfaces = false, + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + } + }); + + Assert.Contains( + expectedLine, + generatedType.First()); + } + + [Fact] + public void MixedTypeMustNotCollideWithExistingMembers() + { + const string xsd = @" + + + + + + +"; + + var generatedType = ConvertXml(nameof(MixedTypeMustNotCollideWithExistingMembers), xsd, new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + } + }); + + Assert.Contains( + @"public string[] Text_1 { get; set; }", + generatedType.First()); + } + + [Fact] + public void MixedTypeMustNotCollideWithContainingTypeName() + { + const string xsd = @" + + + + + +"; + + var generatedType = ConvertXml(nameof(MixedTypeMustNotCollideWithExistingMembers), xsd, new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test" + } + }); + + Assert.Contains( + @"public string[] Text_1 { get; set; }", + generatedType.First()); + } + + [Theory] + [InlineData(@"xml/sameattributenames.xsd", @"xml/sameattributenames_import.xsd")] + public void CollidingAttributeAndPropertyNamesCanBeResolved(params string[] files) + { + // Compilation would previously throw due to duplicate type name within type + var assembly = Compiler.GenerateFiles("AttributesWithSameName", files); + + Assert.NotNull(assembly); + } + + [Fact] public void ComplexTypeWithAttributeGroupExtension() { const string xsd = @" @@ -537,5 +635,154 @@ private static void CompareOutput(string expected, string actual) string Normalize(string input) => Regex.Replace(input, @"[ \t]*\r\n", "\n"); Assert.Equal(Normalize(expected), Normalize(actual)); } + + + [Theory] + [InlineData(typeof(decimal), "decimal")] + [InlineData(typeof(long), "long")] + [InlineData(null, "string")] + public void UnmappedIntegerDerivedTypesAreMappedToExpectedCSharpType(Type integerDataType, string expectedTypeName) + { + const string xsd = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + + var generatedType = ConvertXml(nameof(UnmappedIntegerDerivedTypesAreMappedToExpectedCSharpType), xsd, new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test", + }, + GenerateNullables = true, + NamingScheme = NamingScheme.PascalCase, + IntegerDataType = integerDataType, + }).First(); + + Assert.Contains($"public {expectedTypeName} UnboundedInteger01", generatedType); + Assert.Contains($"public {expectedTypeName} UnboundedInteger02", generatedType); + Assert.Contains($"public {expectedTypeName} UnboundedInteger03", generatedType); + Assert.Contains($"public {expectedTypeName} UnboundedInteger04", generatedType); + Assert.Contains($"public {expectedTypeName} UnboundedInteger05", generatedType); + Assert.Contains($"public {expectedTypeName} OutOfBoundsInteger01", generatedType); + Assert.Contains($"public {expectedTypeName} OutOfBoundsInteger02", generatedType); + Assert.Contains($"public {expectedTypeName} OutOfBoundsInteger03", generatedType); + Assert.Contains($"public {expectedTypeName} OutOfBoundsInteger04", generatedType); + Assert.Contains($"public {expectedTypeName} OutOfBoundsInteger05", generatedType); + } + + [Theory] + [InlineData("xs:positiveInteger", 1, 2, "byte")] + [InlineData("xs:nonNegativeInteger", 1, 2, "byte")] + [InlineData("xs:integer", 1, 2, "sbyte")] + [InlineData("xs:negativeInteger", 1, 2, "sbyte")] + [InlineData("xs:nonPositiveInteger", 1, 2, "sbyte")] + [InlineData("xs:positiveInteger", 3, 4, "ushort")] + [InlineData("xs:nonNegativeInteger", 3, 4, "ushort")] + [InlineData("xs:integer", 3, 4, "short")] + [InlineData("xs:negativeInteger", 3, 4, "short")] + [InlineData("xs:nonPositiveInteger", 3, 4, "short")] + [InlineData("xs:positiveInteger", 5, 9, "uint")] + [InlineData("xs:nonNegativeInteger", 5, 9, "uint")] + [InlineData("xs:integer", 5, 9, "int")] + [InlineData("xs:negativeInteger", 5, 9, "int")] + [InlineData("xs:nonPositiveInteger", 5, 9, "int")] + [InlineData("xs:positiveInteger", 10, 19, "ulong")] + [InlineData("xs:nonNegativeInteger", 10, 19, "ulong")] + [InlineData("xs:integer", 10, 18, "long")] + [InlineData("xs:negativeInteger", 10, 18, "long")] + [InlineData("xs:nonPositiveInteger", 10, 18, "long")] + [InlineData("xs:positiveInteger", 20, 29, "decimal")] + [InlineData("xs:nonNegativeInteger", 20, 29, "decimal")] + [InlineData("xs:integer", 20, 28, "decimal")] + [InlineData("xs:negativeInteger", 20, 28, "decimal")] + [InlineData("xs:nonPositiveInteger", 20, 28, "decimal")] + public void RestrictedIntegerDerivedTypesAreMappedToExpectedCSharpTypes(string restrictionBase, int totalDigitsRangeFrom, int totalDigitsRangeTo, string expectedTypeName) + { + const string xsdTemplate = @" + + + + {0} + + + + {1} +"; + + const string elementTemplate = @""; + + const string simpleTypeTemplate = @" + + + + + +"; + + string elementDefinitions = "", simpleTypeDefinitions = ""; + for (var i = totalDigitsRangeFrom; i <= totalDigitsRangeTo; i++) + { + elementDefinitions += string.Format(elementTemplate, i); + simpleTypeDefinitions += string.Format(simpleTypeTemplate, restrictionBase, i); + } + + var xsd = string.Format(xsdTemplate, elementDefinitions, simpleTypeDefinitions); + var generatedType = ConvertXml(nameof(RestrictedIntegerDerivedTypesAreMappedToExpectedCSharpTypes), xsd, + new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => "Test", + }, + GenerateNullables = true, + NamingScheme = NamingScheme.PascalCase, + }).First(); + + for (var i = totalDigitsRangeFrom; i <= totalDigitsRangeTo; i++) + { + Assert.Contains($"public {expectedTypeName} RestrictedInteger{i}", generatedType); + } + } } } diff --git a/XmlSchemaClassGenerator.Tests/xml/sameattributenames.xsd b/XmlSchemaClassGenerator.Tests/xml/sameattributenames.xsd new file mode 100644 index 00000000..dda80a3c --- /dev/null +++ b/XmlSchemaClassGenerator.Tests/xml/sameattributenames.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/XmlSchemaClassGenerator.Tests/xml/sameattributenames_import.xsd b/XmlSchemaClassGenerator.Tests/xml/sameattributenames_import.xsd new file mode 100644 index 00000000..0ff0aa76 --- /dev/null +++ b/XmlSchemaClassGenerator.Tests/xml/sameattributenames_import.xsd @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/XmlSchemaClassGenerator.sln b/XmlSchemaClassGenerator.sln index 8744d63f..4eeb8618 100644 --- a/XmlSchemaClassGenerator.sln +++ b/XmlSchemaClassGenerator.sln @@ -11,7 +11,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XmlSampleGenerator", "XmlSa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XmlSchemaClassGenerator.Console", "XmlSchemaClassGenerator.Console\XmlSchemaClassGenerator.Console.csproj", "{F0000FE1-DE27-4BF9-A179-FC9643A57EEE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xscgen", "xscgen\xscgen.csproj", "{C5C1FF7F-31AD-4D4F-81F3-C9F54516D9D0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xscgen", "xscgen\xscgen.csproj", "{C5C1FF7F-31AD-4D4F-81F3-C9F54516D9D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6ED7BE60-B3EC-4CBA-8732-09DA45AC14BF}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/XmlSchemaClassGenerator/CodeUtilities.cs b/XmlSchemaClassGenerator/CodeUtilities.cs index 1352e0cd..ba7db3a8 100644 --- a/XmlSchemaClassGenerator/CodeUtilities.cs +++ b/XmlSchemaClassGenerator/CodeUtilities.cs @@ -12,6 +12,12 @@ public static class CodeUtilities // Match non-letter followed by letter static Regex PascalCaseRegex = new Regex(@"[^\p{L}]\p{L}", RegexOptions.Compiled); + private static readonly XmlTypeCode[] IntegerDerivedTypeCodes = + { + XmlTypeCode.Integer, XmlTypeCode.NegativeInteger, XmlTypeCode.NonNegativeInteger, + XmlTypeCode.NonPositiveInteger, XmlTypeCode.PositiveInteger + }; + // Uppercases first letter and all letters following non-letters. // Examples: testcase -> Testcase, html5element -> Html5Element, test_case -> Test_Case public static string ToPascalCase(this string s) @@ -55,7 +61,77 @@ public static string ToBackingField(this string propertyName, bool doNotUseUnder return result; } - public static Type GetEffectiveType(this XmlSchemaDatatype type, GeneratorConfiguration configuration, bool attribute = false) + private static Type GetIntegerDerivedType(XmlSchemaDatatype type, GeneratorConfiguration configuration, IEnumerable restrictions) + { + if (configuration.IntegerDataType != null) return configuration.IntegerDataType; + + var xmlTypeCode = type.TypeCode; + + Type result = null; + + if (!(restrictions.SingleOrDefault(r => r is TotalDigitsRestrictionModel) is TotalDigitsRestrictionModel totalDigits) + || ((xmlTypeCode == XmlTypeCode.PositiveInteger + || xmlTypeCode == XmlTypeCode.NonNegativeInteger) && totalDigits.Value >= 30) + || ((xmlTypeCode == XmlTypeCode.Integer + || xmlTypeCode == XmlTypeCode.NegativeInteger + || xmlTypeCode == XmlTypeCode.NonPositiveInteger) && totalDigits.Value >= 29)) + { + return typeof(string); + } + + switch (xmlTypeCode) + { + case XmlTypeCode.PositiveInteger: + case XmlTypeCode.NonNegativeInteger: + switch (totalDigits.Value) + { + case int n when (n < 3): + result = typeof(byte); + break; + case int n when (n < 5): + result = typeof(ushort); + break; + case int n when (n < 10): + result = typeof(uint); + break; + case int n when (n < 20): + result = typeof(ulong); + break; + case int n when (n < 30): + result = typeof(decimal); + break; + } + + break; + + case XmlTypeCode.Integer: + case XmlTypeCode.NegativeInteger: + case XmlTypeCode.NonPositiveInteger: + switch (totalDigits.Value) + { + case int n when (n < 3): + result = typeof(sbyte); + break; + case int n when (n < 5): + result = typeof(short); + break; + case int n when (n < 10): + result = typeof(int); + break; + case int n when (n < 19): + result = typeof(long); + break; + case int n when (n < 29): + result = typeof(decimal); + break; + } + break; + } + + return result; + } + + public static Type GetEffectiveType(this XmlSchemaDatatype type, GeneratorConfiguration configuration, IEnumerable restrictions, bool attribute = false) { Type resultType; @@ -85,7 +161,7 @@ public static Type GetEffectiveType(this XmlSchemaDatatype type, GeneratorConfig case XmlTypeCode.NonNegativeInteger: case XmlTypeCode.NonPositiveInteger: case XmlTypeCode.PositiveInteger: - resultType = configuration.IntegerDataType ?? typeof(string); + resultType = GetIntegerDerivedType(type, configuration, restrictions); break; default: resultType = type.ValueType; @@ -99,7 +175,7 @@ public static Type GetEffectiveType(this XmlSchemaDatatype type, GeneratorConfig // XmlSerializer doesn't support xsd:list for elements, only for attributes: // https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/t84dzyst(v%3dvs.100) - + // Also, de/serialization fails when the XML schema type is ambiguous (DateTime -> date, datetime, or time) if (!attribute || resultType == typeof(DateTime)) diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs index 93d21794..e8c69ba2 100644 --- a/XmlSchemaClassGenerator/ModelBuilder.cs +++ b/XmlSchemaClassGenerator/ModelBuilder.cs @@ -53,7 +53,8 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set) foreach (var rootElement in set.GlobalElements.Values.Cast()) { - var source = new Uri(rootElement.GetSchema().SourceUri); + var rootSchema = rootElement.GetSchema(); + var source = !string.IsNullOrEmpty(rootSchema.SourceUri) ? new Uri(rootElement.GetSchema().SourceUri) : default(Uri); var qualifiedName = rootElement.ElementSchemaType.QualifiedName; if (qualifiedName.IsEmpty) { qualifiedName = rootElement.QualifiedName; } var type = CreateTypeModel(source, rootElement.ElementSchemaType, qualifiedName); @@ -96,6 +97,11 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set) if (type is ClassModel classModel) { classModel.Documentation.AddRange(GetDocumentation(rootElement)); + if (!rootElement.SubstitutionGroup.IsEmpty) + { + classModel.IsSubstitution = true; + classModel.SubstitutionName = rootElement.QualifiedName; + } } type.RootElementName = rootElement.QualifiedName; @@ -371,7 +377,7 @@ private TypeModel CreateTypeModel(XmlSchemaSimpleType simpleType, NamespaceModel Namespace = namespaceModel, XmlSchemaName = qualifiedName, XmlSchemaType = simpleType, - ValueType = simpleType.Datatype.GetEffectiveType(_configuration), + ValueType = simpleType.Datatype.GetEffectiveType(_configuration, restrictions), }; simpleModel.Documentation.AddRange(docs); diff --git a/XmlSchemaClassGenerator/TypeModel.cs b/XmlSchemaClassGenerator/TypeModel.cs index fc88c817..5beefa70 100644 --- a/XmlSchemaClassGenerator/TypeModel.cs +++ b/XmlSchemaClassGenerator/TypeModel.cs @@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics; using System.Linq; -using System.Reflection; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Schema; @@ -225,6 +224,7 @@ public class ClassModel : TypeModel public bool IsAbstract { get; set; } public bool IsMixed { get; set; } public bool IsSubstitution { get; set; } + public XmlQualifiedName SubstitutionName { get; set; } public TypeModel BaseClass { get; set; } public List Properties { get; set; } public List Interfaces { get; set; } @@ -382,12 +382,30 @@ public override CodeTypeDeclaration Generate() keyProperty.IsKey = true; } - foreach (var property in Properties) - property.AddMembersTo(classDeclaration, Configuration.EnableDataBinding); - - if (IsMixed && (BaseClass == null || (BaseClass is ClassModel && !AllBaseClasses.Any(b => b.IsMixed)))) - { - var text = new CodeMemberField(typeof(string), "Text"); + foreach (var property in Properties.GroupBy(x => x.Name)) + { + var propertyIndex = 0; + foreach (var p in property) + { + if (propertyIndex > 0) + { + p.Name += $"_{propertyIndex}"; + } + p.AddMembersTo(classDeclaration, Configuration.EnableDataBinding); + propertyIndex++; + } + } + + if (IsMixed && (BaseClass == null || (BaseClass is ClassModel && !AllBaseClasses.Any(b => b.IsMixed)))) + { + var propName = "Text"; + + // To not collide with any existing members + for (var propertyIndex = 1; Properties.Any(x => x.Name.Equals(propName, StringComparison.Ordinal)) || propName.Equals(classDeclaration.Name, StringComparison.Ordinal); propertyIndex++) + { + propName = $"Text_{propertyIndex}"; + } + var text = new CodeMemberField(typeof(string[]), propName); // hack to generate automatic property text.Name += " { get; set; }"; text.Attributes = MemberAttributes.Public; @@ -862,8 +880,8 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi typeDeclaration.Members.Add(nullableMember); var editorBrowsableAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(EditorBrowsableAttribute), Configuration.CodeTypeReferenceOptions)); - editorBrowsableAttribute.Arguments.Add(new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EditorBrowsableState)), "Never"))); - specifiedMember.CustomAttributes.Add(editorBrowsableAttribute); + editorBrowsableAttribute.Arguments.Add(new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(new CodeTypeReference(typeof(EditorBrowsableState), Configuration.CodeTypeReferenceOptions)), "Never"))); + specifiedMember.CustomAttributes.Add(editorBrowsableAttribute); member.CustomAttributes.Add(editorBrowsableAttribute); if (Configuration.EntityFramework) { member.CustomAttributes.Add(notMappedAttribute); } } @@ -987,7 +1005,7 @@ private IEnumerable GetAttributes(bool isArray) foreach (var derivedType in derivedTypes) { var derivedAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(XmlElementAttribute), Configuration.CodeTypeReferenceOptions), - new CodeAttributeArgument(new CodePrimitiveExpression(derivedType.XmlSchemaName.Name)), + new CodeAttributeArgument(new CodePrimitiveExpression((derivedType.SubstitutionName ?? derivedType.XmlSchemaName).Name)), new CodeAttributeArgument("Type", new CodeTypeOfExpression(derivedType.GetReferenceFor(OwningType.Namespace)))); if (Order != null) { @@ -1184,7 +1202,7 @@ public override CodeTypeReference GetReferenceFor(NamespaceModel referencingName // http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.datatype(v=vs.110).aspx // XmlSerializer is inconsistent: maps xs:decimal to decimal but xs:integer to string, // even though xs:integer is a restriction of xs:decimal - type = XmlSchemaType.Datatype.GetEffectiveType(Configuration, attribute); + type = XmlSchemaType.Datatype.GetEffectiveType(Configuration, Restrictions, attribute); UseDataTypeAttribute = XmlSchemaType.Datatype.IsDataTypeAttributeAllowed(Configuration) ?? UseDataTypeAttribute; }