diff --git a/XmlSchemaClassGenerator.Console/Program.cs b/XmlSchemaClassGenerator.Console/Program.cs index 1cdb594d..300c5880 100644 --- a/XmlSchemaClassGenerator.Console/Program.cs +++ b/XmlSchemaClassGenerator.Console/Program.cs @@ -56,6 +56,7 @@ static void Main(string[] args) var netCoreSpecificCode = false; var nullableReferenceAttributes = false; var generateCommandLineArgs = true; + var useArrayItemAttribute = true; var options = new OptionSet { { "h|help", "show this message and exit", v => showHelp = v != null }, @@ -129,6 +130,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l { "gc|generatedCodeAttribute", "add version information to GeneratedCodeAttribute (default is true)", v => createGeneratedCodeAttributeVersion = v != null }, { "nc|netCore", "generate .NET Core specific code that might not work with .NET Framework (default is false)", v => netCoreSpecificCode = v != null }, { "nr|nullableReferenceAttributes", "generate attributes for nullable reference types (default is false)", v => nullableReferenceAttributes = v != null }, + { "ar|useArrayItemAttribute", "use ArrayItemAttribute for sequences with single elements (default is true)", v => useArrayItemAttribute = v != null }, { "ca|commandArgs", "generate a comment with the exact command line arguments that were used to generate the source code (default is true)", v => generateCommandLineArgs = v != null }, }; @@ -207,6 +209,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l NetCoreSpecificCode = netCoreSpecificCode, EnableNullableReferenceAttributes = nullableReferenceAttributes, GenerateCommandLineArgumentsComment = generateCommandLineArgs, + UseArrayItemAttribute = useArrayItemAttribute, }; generator.CommentLanguages.AddRange(commentLanguages); diff --git a/XmlSchemaClassGenerator.Tests/Compiler.cs b/XmlSchemaClassGenerator.Tests/Compiler.cs index 95453d4a..fc0dd3f7 100644 --- a/XmlSchemaClassGenerator.Tests/Compiler.cs +++ b/XmlSchemaClassGenerator.Tests/Compiler.cs @@ -114,7 +114,8 @@ public static Assembly GenerateFiles(string name, IEnumerable files, Gen CreateGeneratedCodeAttributeVersion = generatorPrototype.CreateGeneratedCodeAttributeVersion, NetCoreSpecificCode = generatorPrototype.NetCoreSpecificCode, EnableNullableReferenceAttributes = generatorPrototype.EnableNullableReferenceAttributes, - NamingScheme = generatorPrototype.NamingScheme + NamingScheme = generatorPrototype.NamingScheme, + UseArrayItemAttribute = generatorPrototype.UseArrayItemAttribute, }; gen.CommentLanguages.Clear(); diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index f32c41d8..43162982 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -64,6 +64,7 @@ private static IEnumerable ConvertXml(string name, IEnumerable x CollectionType = generatorPrototype.CollectionType, CollectionImplementationType = generatorPrototype.CollectionImplementationType, CollectionSettersMode = generatorPrototype.CollectionSettersMode, + UseArrayItemAttribute = generatorPrototype.UseArrayItemAttribute, }; gen.CommentLanguages.Clear(); @@ -2371,20 +2372,20 @@ void UnknownAttributeHandler(object sender, XmlAttributeEventArgs e) [Fact, TestPriority(1)] [UseCulture("en-US")] - public void TestNullableReferenceAttributes() + public void TestNullableReferenceAttributes() { var files = Glob.ExpandNames(NullableReferenceAttributesPattern).OrderByDescending(f => f); - var generator = new Generator + var generator = new Generator { EnableNullableReferenceAttributes = true, UseShouldSerializePattern = true, - NamespaceProvider = new NamespaceProvider + NamespaceProvider = new NamespaceProvider { GenerateNamespace = key => "Test" } }; var assembly = Compiler.Generate(nameof(TestNullableReferenceAttributes), NullableReferenceAttributesPattern, generator); - void assertNullable(string typename, bool nullable) + void assertNullable(string typename, bool nullable) { Type c = assembly.GetType(typename); var property = c.GetProperty("Text"); @@ -2528,5 +2529,54 @@ public partial class Elem }} ", csharp); } + + [Fact] + public void TestArrayItemAttribute() + { + // see https://github.com/mganss/XmlSchemaClassGenerator/issues/313 + + var xsd = +@" + + + + + + + + + + + + + + + + + + + +"; + var generator = new Generator + { + IntegerDataType = typeof(int), + GenerateNullables = true, + CollectionType = typeof(System.Array), + CollectionSettersMode = CollectionSettersMode.Public, + UseArrayItemAttribute = false + }; + var contents = ConvertXml(nameof(TestArrayItemAttribute), new[] { xsd }, generator).ToArray(); + var assembly = Compiler.Compile(nameof(TestArrayItemAttribute), contents); + var applicationType = assembly.GetType("Test_Generation_Namespace.T_Application"); + Assert.NotNull(applicationType); + var optionList = applicationType.GetProperty("OptionList"); + Assert.Equal("Test_Generation_Namespace.T_OptionList", optionList.PropertyType.FullName); + } } } diff --git a/XmlSchemaClassGenerator/CodeUtilities.cs b/XmlSchemaClassGenerator/CodeUtilities.cs index e76e4d78..6f87d805 100644 --- a/XmlSchemaClassGenerator/CodeUtilities.cs +++ b/XmlSchemaClassGenerator/CodeUtilities.cs @@ -245,7 +245,7 @@ public static string GetUniqueTypeName(this NamespaceModel model, string name) var n = name; var i = 2; - while (model.Types.ContainsKey(n) && !(model.Types[n] is SimpleModel)) + while (model.Types.ContainsKey(n) && model.Types[n] is not SimpleModel) { n = name + i; i++; diff --git a/XmlSchemaClassGenerator/Generator.cs b/XmlSchemaClassGenerator/Generator.cs index e0e8c215..bd621e23 100644 --- a/XmlSchemaClassGenerator/Generator.cs +++ b/XmlSchemaClassGenerator/Generator.cs @@ -115,7 +115,7 @@ public bool GenerateNullables set { _configuration.GenerateNullables = value; } } - public bool EnableNullableReferenceAttributes + public bool EnableNullableReferenceAttributes { get { return _configuration.EnableNullableReferenceAttributes; } set { _configuration.EnableNullableReferenceAttributes = value; } @@ -294,6 +294,12 @@ public bool NetCoreSpecificCode set { _configuration.NetCoreSpecificCode = value; } } + public bool UseArrayItemAttribute + { + get { return _configuration.UseArrayItemAttribute; } + set { _configuration.UseArrayItemAttribute = value;} + } + public bool GenerateCommandLineArgumentsComment { get { return _configuration.GenerateCommandLineArgumentsComment; } diff --git a/XmlSchemaClassGenerator/GeneratorConfiguration.cs b/XmlSchemaClassGenerator/GeneratorConfiguration.cs index 0d4433ed..e147a7c4 100644 --- a/XmlSchemaClassGenerator/GeneratorConfiguration.cs +++ b/XmlSchemaClassGenerator/GeneratorConfiguration.cs @@ -320,5 +320,11 @@ public void WriteLog(string message) /// A provider to obtain the command line arguments of the tool. /// public CommandLineArgumentsProvider CommandLineArgumentsProvider { get; set; } + + /// + /// Enables use of + /// for sequences with single elements. Default is true. + /// + public bool UseArrayItemAttribute { get; set; } = true; } } diff --git a/XmlSchemaClassGenerator/TypeModel.cs b/XmlSchemaClassGenerator/TypeModel.cs index 80b31f77..117e3fae 100644 --- a/XmlSchemaClassGenerator/TypeModel.cs +++ b/XmlSchemaClassGenerator/TypeModel.cs @@ -328,7 +328,7 @@ public override CodeTypeDeclaration Generate() if (IsAbstract) classDeclaration.TypeAttributes |= System.Reflection.TypeAttributes.Abstract; - if (Configuration.EnableDataBinding && !(BaseClass is ClassModel)) + if (Configuration.EnableDataBinding && BaseClass is not ClassModel) { var propertyChangedEvent = new CodeMemberEvent() { @@ -432,7 +432,7 @@ public override CodeTypeDeclaration Generate() classDeclaration.BaseTypes.Add(CodeUtilities.CreateTypeReference(typeof(INotifyPropertyChanged), Configuration)); } - if (Configuration.EntityFramework && !(BaseClass is ClassModel)) + if (Configuration.EntityFramework && BaseClass is not ClassModel) { // generate key var keyProperty = Properties.FirstOrDefault(p => p.Name.ToLowerInvariant() == "id") @@ -729,7 +729,8 @@ public bool IsArray { get { - return !IsCollection && !IsAttribute && !IsList && TypeClassModel != null + return Configuration.UseArrayItemAttribute + && !IsCollection && !IsAttribute && !IsList && TypeClassModel != null && TypeClassModel.BaseClass == null && TypeClassModel.Properties.Count == 1 && !TypeClassModel.Properties[0].IsAttribute && !TypeClassModel.Properties[0].IsAny @@ -752,9 +753,9 @@ private bool IsNullableValueType } } - private bool IsNullableReferenceType + private bool IsNullableReferenceType { - get + get { return DefaultValue == null && IsNullable && (IsCollection || IsArray || IsList || PropertyType is ClassModel || PropertyType is SimpleModel model && !model.ValueType.IsValueType); @@ -1124,7 +1125,7 @@ public void AddMembersTo(CodeTypeDeclaration typeDeclaration, bool withDataBindi typeDeclaration.Members.Add(specifiedProperty); } - if (isNullableReferenceType && Configuration.EnableNullableReferenceAttributes) + if (isNullableReferenceType && Configuration.EnableNullableReferenceAttributes) { member.CustomAttributes.Add(new CodeAttributeDeclaration("System.Diagnostics.CodeAnalysis.AllowNullAttribute")); member.CustomAttributes.Add(new CodeAttributeDeclaration("System.Diagnostics.CodeAnalysis.MaybeNullAttribute"));