Skip to content

Commit b584a23

Browse files
authored
Merge pull request #583 from igotinfected/feature/generate-list-of-enums-instead-of-strings
Add `EnumCollection` flag which allows generating list of enums
2 parents 31e2722 + 7c24275 commit b584a23

8 files changed

Lines changed: 261 additions & 61 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,10 @@ Options:
200200
--oxi, --omitXmlIncludeAttribute
201201
omit generation of XmlIncludeAttribute for derived
202202
types (default is false)
203-
--ns, --namingScheme use the specified naming scheme for class and
203+
--ecl, --enumCollection
204+
generate typed enum collections for xs:list types
205+
instead of string collections (default is false)
206+
--ns, --namingScheme use the specified naming scheme for class and
204207
property names (default is Pascal; can be:
205208
Direct, Pascal, Legacy)
206209
--fu, --forceUriScheme=VALUE

XmlSchemaClassGenerator.Console/Program.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using XmlSchemaClassGenerator;
1+
using XmlSchemaClassGenerator;
22
using XmlSchemaClassGenerator.NamingProviders;
33
using System;
44
using System.CodeDom;
@@ -69,8 +69,9 @@ static int Main(string[] args)
6969
var separateNamespaceHierarchy = false;
7070
var serializeEmptyCollections = false;
7171
var allowDtdParse = false;
72-
var omitXmlIncludeAttribute = false;
73-
NamingScheme? namingScheme = null;
72+
var omitXmlIncludeAttribute = false;
73+
var enumCollection = false;
74+
NamingScheme? namingScheme = null;
7475
var forceUriScheme = "none";
7576

7677

@@ -170,7 +171,8 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
170171
{ "uc|unionCommonType", "generate a common type for unions if possible (default is false)", v => unionCommonType = v != null },
171172
{ "ec|serializeEmptyCollections", "serialize empty collections (default is false)", v => serializeEmptyCollections = v != null },
172173
{ "dtd|allowDtdParse", "allows dtd parse (default is false)", v => allowDtdParse = v != null },
173-
{ "oxi|omitXmlIncludeAttribute", "omit generation of XmlIncludeAttribute for derived types (default is false)", v => omitXmlIncludeAttribute = v != null },
174+
{ "oxi|omitXmlIncludeAttribute", "omit generation of XmlIncludeAttribute for derived types (default is false)", v => omitXmlIncludeAttribute = v != null },
175+
{ "ecl|enumCollection", "generate typed enum collections for xs:list types instead of string collections (default is false)", v => enumCollection = v != null },
174176
{ "ns|namingScheme=", @"use the specified naming scheme for class and property names (default is Pascal; can be: Direct, Pascal, Legacy)",
175177
v =>
176178
{
@@ -272,8 +274,9 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
272274
SeparateNamespaceHierarchy = separateNamespaceHierarchy,
273275
SerializeEmptyCollections = serializeEmptyCollections,
274276
AllowDtdParse = allowDtdParse,
275-
OmitXmlIncludeAttribute = omitXmlIncludeAttribute,
276-
ForceUriScheme = forceUriScheme
277+
OmitXmlIncludeAttribute = omitXmlIncludeAttribute,
278+
EnumCollection = enumCollection,
279+
ForceUriScheme = forceUriScheme
277280
};
278281

279282
if (namingScheme != null)

XmlSchemaClassGenerator.Tests/Compiler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public static Assembly GenerateFiles(string name, IEnumerable<string> files, Gen
120120
MapUnionToWidestCommonType = generatorPrototype.MapUnionToWidestCommonType,
121121
SeparateNamespaceHierarchy = generatorPrototype.SeparateNamespaceHierarchy,
122122
OmitXmlIncludeAttribute = generatorPrototype.OmitXmlIncludeAttribute,
123+
EnumCollection = generatorPrototype.EnumCollection,
123124
};
124125

125126
gen.CommentLanguages.Clear();

XmlSchemaClassGenerator.Tests/XmlTests.cs

Lines changed: 144 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ private static IEnumerable<string> ConvertXml(string name, IEnumerable<string> x
5959
UseArrayItemAttribute = generatorPrototype.UseArrayItemAttribute,
6060
EnumAsString = generatorPrototype.EnumAsString,
6161
AllowDtdParse = generatorPrototype.AllowDtdParse,
62-
OmitXmlIncludeAttribute = generatorPrototype.OmitXmlIncludeAttribute
62+
OmitXmlIncludeAttribute = generatorPrototype.OmitXmlIncludeAttribute,
63+
EnumCollection = generatorPrototype.EnumCollection,
6364
};
6465

6566
gen.CommentLanguages.Clear();
@@ -186,35 +187,35 @@ public void TestGuid()
186187

187188
[Fact, TestPriority(1)]
188189
[UseCulture("en-US")]
189-
public void TestUnion()
190-
{
191-
var assembly = Compiler.Generate("Union", UnionPattern, new Generator
192-
{
193-
NamespacePrefix = "Union",
194-
IntegerDataType = typeof(int),
195-
MapUnionToWidestCommonType = true
196-
});
197-
198-
Assert.NotNull(assembly);
199-
200-
SharedTestFunctions.TestSamples(Output, "Union", UnionPattern);
201-
202-
var snapshotType = assembly.GetType("Union.Snapshot");
203-
Assert.NotNull(snapshotType);
204-
205-
var date = snapshotType.GetProperty("Date");
206-
Assert.NotNull(date);
207-
Assert.Equal(typeof(DateTime), date.PropertyType);
208-
209-
var count = snapshotType.GetProperty("Count");
210-
Assert.NotNull(count);
211-
Assert.Equal(typeof(int), count.PropertyType);
212-
213-
var num = snapshotType.GetProperty("Num");
214-
Assert.NotNull(num);
215-
Assert.Equal(typeof(decimal), num.PropertyType);
216-
}
217-
190+
public void TestUnion()
191+
{
192+
var assembly = Compiler.Generate("Union", UnionPattern, new Generator
193+
{
194+
NamespacePrefix = "Union",
195+
IntegerDataType = typeof(int),
196+
MapUnionToWidestCommonType = true
197+
});
198+
199+
Assert.NotNull(assembly);
200+
201+
SharedTestFunctions.TestSamples(Output, "Union", UnionPattern);
202+
203+
var snapshotType = assembly.GetType("Union.Snapshot");
204+
Assert.NotNull(snapshotType);
205+
206+
var date = snapshotType.GetProperty("Date");
207+
Assert.NotNull(date);
208+
Assert.Equal(typeof(DateTime), date.PropertyType);
209+
210+
var count = snapshotType.GetProperty("Count");
211+
Assert.NotNull(count);
212+
Assert.Equal(typeof(int), count.PropertyType);
213+
214+
var num = snapshotType.GetProperty("Num");
215+
Assert.NotNull(num);
216+
Assert.Equal(typeof(decimal), num.PropertyType);
217+
}
218+
218219
[Fact, TestPriority(1)]
219220
[UseCulture("en-US")]
220221
public void TestSimpleContentEnum()
@@ -275,13 +276,13 @@ public void TestSimpleContentEnum()
275276
enumValueProperty.SetValue(instance, null);
276277
stringValue = valueProperty.GetValue(instance);
277278
Assert.Null(stringValue);
278-
}
279-
280-
[Fact, TestPriority(1)]
281-
[UseCulture("en-US")]
282-
public void TestList()
283-
{
284-
Compiler.Generate("List", ListPattern);
279+
}
280+
281+
[Fact, TestPriority(1)]
282+
[UseCulture("en-US")]
283+
public void TestList()
284+
{
285+
Compiler.Generate("List", ListPattern);
285286
SharedTestFunctions.TestSamples(Output, "List", ListPattern);
286287
}
287288

@@ -517,6 +518,112 @@ public void TestListWithPublicPropertySettersWithoutConstructorsSpecified()
517518
}
518519
}
519520

521+
[Fact]
522+
public void TestEnumCollection()
523+
{
524+
var assembly = Compiler.Generate("ListEnumCollection", ListPattern, new Generator
525+
{
526+
GenerateNullables = true,
527+
IntegerDataType = typeof(int),
528+
DataAnnotationMode = DataAnnotationMode.All,
529+
GenerateDesignerCategoryAttribute = false,
530+
GenerateComplexTypesForCollections = true,
531+
EntityFramework = false,
532+
GenerateInterfaces = true,
533+
NamespacePrefix = "List",
534+
GenerateDescriptionAttribute = true,
535+
TextValuePropertyName = "Value",
536+
EnumCollection = true
537+
});
538+
539+
Assert.NotNull(assembly);
540+
541+
var myClassType = assembly.GetType("List.MyClass");
542+
Assert.NotNull(myClassType);
543+
544+
var enumType = assembly.GetType("List.EnumType");
545+
Assert.NotNull(enumType);
546+
Assert.True(enumType.IsEnum);
547+
548+
// Element property: should be Collection<EnumType>, not Collection<string>
549+
var enumElemProp = myClassType.GetProperty("EnumElem");
550+
Assert.NotNull(enumElemProp);
551+
Assert.True(enumElemProp.PropertyType.IsGenericType);
552+
Assert.Equal(enumType, enumElemProp.PropertyType.GetGenericArguments()[0]);
553+
554+
// Attribute property: should use enum type, not string
555+
var enumAttrProp = myClassType.GetProperty("EnumAttr");
556+
Assert.NotNull(enumAttrProp);
557+
if (enumAttrProp.PropertyType.IsArray)
558+
{
559+
Assert.Equal(enumType, enumAttrProp.PropertyType.GetElementType());
560+
}
561+
else if (enumAttrProp.PropertyType.IsGenericType)
562+
{
563+
Assert.Equal(enumType, enumAttrProp.PropertyType.GetGenericArguments()[0]);
564+
}
565+
else
566+
{
567+
Assert.Fail($"Expected array or generic collection, got {enumAttrProp.PropertyType}");
568+
}
569+
570+
// Non-enum list properties should remain string-based
571+
var timeListElemProp = myClassType.GetProperty("TimeListElem");
572+
Assert.NotNull(timeListElemProp);
573+
Assert.True(timeListElemProp.PropertyType.IsGenericType);
574+
Assert.Equal(typeof(string), timeListElemProp.PropertyType.GetGenericArguments()[0]);
575+
}
576+
577+
[Theory]
578+
[InlineData(typeof(Collection<>), null)]
579+
[InlineData(typeof(List<>), null)]
580+
[InlineData(typeof(HashSet<>), null)]
581+
[InlineData(typeof(Array), null)]
582+
public void TestEnumCollectionRespectsCollectionType(Type collectionType, Type collectionImplementationType)
583+
{
584+
var assembly = Compiler.Generate($"ListEnumCollection_{collectionType.Name}_{collectionImplementationType?.Name}", ListPattern, new Generator
585+
{
586+
GenerateNullables = true,
587+
IntegerDataType = typeof(int),
588+
DataAnnotationMode = DataAnnotationMode.All,
589+
GenerateDesignerCategoryAttribute = false,
590+
GenerateComplexTypesForCollections = true,
591+
EntityFramework = false,
592+
GenerateInterfaces = true,
593+
NamespacePrefix = "List",
594+
GenerateDescriptionAttribute = true,
595+
TextValuePropertyName = "Value",
596+
EnumCollection = true,
597+
CollectionType = collectionType,
598+
CollectionImplementationType = collectionImplementationType
599+
});
600+
601+
Assert.NotNull(assembly);
602+
603+
var myClassType = assembly.GetType("List.MyClass");
604+
Assert.NotNull(myClassType);
605+
606+
var enumType = assembly.GetType("List.EnumType");
607+
Assert.NotNull(enumType);
608+
609+
var enumElemProp = myClassType.GetProperty("EnumElem");
610+
Assert.NotNull(enumElemProp);
611+
612+
if (collectionType == typeof(System.Array))
613+
{
614+
Assert.True(enumElemProp.PropertyType.IsArray);
615+
Assert.Equal(enumType, enumElemProp.PropertyType.GetElementType());
616+
}
617+
else
618+
{
619+
Assert.True(enumElemProp.PropertyType.IsGenericType);
620+
Assert.Equal(enumType, enumElemProp.PropertyType.GetGenericArguments()[0]);
621+
622+
var expectedCollectionType = collectionType.MakeGenericType(enumType);
623+
Assert.Equal(expectedCollectionType, enumElemProp.PropertyType);
624+
}
625+
}
626+
520627
public static TheoryData<CodeTypeReferenceOptions, NamingScheme, Type> TestSimpleData() {
521628
var theoryData = new TheoryData<CodeTypeReferenceOptions, NamingScheme, Type>();
522629
foreach (var referenceMode in new[]

XmlSchemaClassGenerator/Generator.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.CodeDom;
33
using System.Collections.Generic;
44
using System.IO;
@@ -366,13 +366,19 @@ public bool AllowDtdParse
366366
set { _configuration.AllowDtdParse = value; }
367367
}
368368

369-
public bool OmitXmlIncludeAttribute
370-
{
371-
get { return _configuration.OmitXmlIncludeAttribute; }
372-
set { _configuration.OmitXmlIncludeAttribute = value; }
373-
}
374-
375-
public bool ValidationError { get; private set; }
369+
public bool OmitXmlIncludeAttribute
370+
{
371+
get { return _configuration.OmitXmlIncludeAttribute; }
372+
set { _configuration.OmitXmlIncludeAttribute = value; }
373+
}
374+
375+
public bool EnumCollection
376+
{
377+
get { return _configuration.EnumCollection; }
378+
set { _configuration.EnumCollection = value; }
379+
}
380+
381+
public bool ValidationError { get; private set; }
376382

377383
static Generator()
378384
{

XmlSchemaClassGenerator/GeneratorConfiguration.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.CodeDom;
33
using System.Collections.Generic;
44
using System.Collections.ObjectModel;
@@ -366,8 +366,14 @@ public void WriteLog(string message)
366366
/// </summary>
367367
public bool AllowDtdParse { get; set; } = false;
368368

369-
/// <summary>
370-
/// Omit generation of XmlIncludeAttribute for derived types. Default is false.
371-
/// </summary>
372-
public bool OmitXmlIncludeAttribute { get; set; } = false;
373-
}
369+
/// <summary>
370+
/// Omit generation of XmlIncludeAttribute for derived types. Default is false.
371+
/// </summary>
372+
public bool OmitXmlIncludeAttribute { get; set; } = false;
373+
374+
/// <summary>
375+
/// Generate typed enum collections for xs:list types whose item type is an enumeration,
376+
/// instead of falling back to string collections. Default is false.
377+
/// </summary>
378+
public bool EnumCollection { get; set; }
379+
}

0 commit comments

Comments
 (0)