diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs
index bdd3b5bd..bc0c0888 100644
--- a/XmlSchemaClassGenerator.Tests/XmlTests.cs
+++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs
@@ -1382,5 +1382,205 @@ public void RestrictedIntegerDerivedTypesAreMappedToExpectedCSharpTypes(string r
Assert.Contains($"public {expectedTypeName} RestrictedInteger{i}", generatedType);
}
}
+
+ [Fact]
+ public void EnumWithNonUniqueEntriesTest()
+ {
+ const string xsd = @"
+
+
+
+
+
+
+
+
+
+ ";
+
+ var generator = new Generator
+ {
+ NamespaceProvider = new NamespaceProvider
+ {
+ GenerateNamespace = key => "Test"
+ },
+ GenerateInterfaces = true,
+ AssemblyVisible = true
+ };
+ var contents = ConvertXml(nameof(EnumWithNonUniqueEntriesTest), xsd, generator);
+ var content = Assert.Single(contents);
+
+ var assembly = Compiler.Compile(nameof(EnumWithNonUniqueEntriesTest), content);
+ var durationEnumType = assembly.GetType("Test.TestEnum");
+ Assert.NotNull(durationEnumType);
+
+ var expectedEnumValues = new[] {"Test_Case", "Test_Case1", "Test_Case2", "Test_Case3"};
+ var enumValues = durationEnumType.GetEnumNames().OrderBy(n => n).ToList();
+ Assert.Equal(expectedEnumValues, enumValues);
+
+ var mEnumValue = durationEnumType.GetMembers().First(mi => mi.Name == "Test_Case1");
+ var xmlEnumAttribute = mEnumValue.GetCustomAttributes().FirstOrDefault();
+ Assert.NotNull(xmlEnumAttribute);
+ Assert.Equal("test_Case", xmlEnumAttribute.Name);
+ }
+
+ [Fact]
+ public void RenameInterfacePropertyInDerivedClassTest()
+ {
+ const string xsd = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ";
+
+ var generator = new Generator
+ {
+ NamespaceProvider = new NamespaceProvider
+ {
+ GenerateNamespace = key => "Test"
+ },
+ GenerateInterfaces = true,
+ AssemblyVisible = true
+ };
+ var contents = ConvertXml(nameof(RenameInterfacePropertyInDerivedClassTest), xsd, generator);
+ var content = Assert.Single(contents);
+
+ var assembly = Compiler.Compile(nameof(RenameInterfacePropertyInDerivedClassTest), content);
+ var classType = assembly.GetType("Test.ClassItem");
+ Assert.NotNull(classType);
+ Assert.Single(classType.GetProperties());
+ Assert.Equal("ClassItemBaseProperty", classType.GetProperties().First().Name);
+
+ var level1Interface = assembly.GetType("Test.ILevel1");
+ Assert.NotNull(level1Interface);
+ Assert.Empty(level1Interface.GetProperties());
+
+ var level2Interface = assembly.GetType("Test.ILevel1");
+ Assert.NotNull(level2Interface);
+ Assert.Empty(level2Interface.GetProperties());
+
+ var level3Interface = assembly.GetType("Test.ILevel3");
+ Assert.NotNull(level3Interface);
+ Assert.Single(level3Interface.GetProperties());
+ Assert.Equal("ClassItemBaseProperty", level3Interface.GetProperties().First().Name);
+ }
+
+ [Fact]
+ public void DoNotGenerateSamePropertiesInDerivedInterfacesClassTest()
+ {
+ const string xsd = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ";
+
+ var generator = new Generator
+ {
+ NamespaceProvider = new NamespaceProvider
+ {
+ GenerateNamespace = key => "Test"
+ },
+ GenerateInterfaces = true,
+ AssemblyVisible = true
+ };
+ var contents = ConvertXml(nameof(DoNotGenerateSamePropertiesInDerivedInterfacesClassTest), xsd, generator);
+ var content = Assert.Single(contents);
+
+ var assembly = Compiler.Compile(nameof(DoNotGenerateSamePropertiesInDerivedInterfacesClassTest), content);
+
+ var listType = assembly.GetType("Test.ParentClass");
+ Assert.NotNull(listType);
+
+ var listTypePropertyInfo = listType.GetProperties().FirstOrDefault(p => p.Name == "InterfaceProperty");
+ Assert.NotNull(listTypePropertyInfo);
+
+ var level1Interface = assembly.GetType("Test.ILevel1");
+ Assert.NotNull(level1Interface);
+ Assert.Empty(level1Interface.GetProperties());
+
+ var level2Interface = assembly.GetType("Test.ILevel2");
+ Assert.NotNull(level2Interface);
+ Assert.Empty(level2Interface.GetProperties());
+
+ var level3Interface = assembly.GetType("Test.ILevel3");
+ Assert.NotNull(level3Interface);
+ var level3InterfacePropertyInfo = level3Interface.GetProperties().FirstOrDefault(p => p.Name == "InterfaceProperty");
+ Assert.NotNull(level3InterfacePropertyInfo);
+
+ }
}
}
diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs
index e461cea5..c281f361 100644
--- a/XmlSchemaClassGenerator/ModelBuilder.cs
+++ b/XmlSchemaClassGenerator/ModelBuilder.cs
@@ -62,6 +62,66 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set)
var elements = set.GlobalElements.Values.Cast().Where(s => s.GetSchema() == schema);
CreateElements(elements);
}
+
+ if (configuration.GenerateInterfaces)
+ {
+ RenameInterfacePropertiesIfRenamedInDerivedClasses();
+ RemoveDuplicateInterfaceProperties();
+ }
+ }
+
+ private void RemoveDuplicateInterfaceProperties()
+ {
+ foreach (var interfaceModel in Types.Values.OfType())
+ {
+ var parentProperties = interfaceModel.Properties.ToList();
+ foreach (var baseInterfaceType in interfaceModel.AllDerivedReferenceTypes().OfType())
+ {
+ foreach (var parentProperty in parentProperties)
+ {
+ var baseProperties = baseInterfaceType.Properties.ToList();
+ foreach (var baseProperty in baseProperties.Where(baseProperty => parentProperty.Name == baseProperty.Name && parentProperty.Type.Name == baseProperty.Type.Name))
+ {
+ baseInterfaceType.Properties.Remove(baseProperty);
+ }
+ }
+ }
+ }
+ }
+
+ private void RenameInterfacePropertiesIfRenamedInDerivedClasses()
+ {
+ foreach (var interfaceModel in Types.Values.OfType())
+ {
+ foreach (var interfaceProperty in interfaceModel.Properties)
+ {
+ foreach (var implementationClass in interfaceModel.AllDerivedReferenceTypes())
+ foreach (var implementationClassProperty in implementationClass.Properties)
+ {
+ if (implementationClassProperty.Name != implementationClassProperty.OriginalPropertyName
+ && implementationClassProperty.OriginalPropertyName == interfaceProperty.Name
+ )
+ {
+ RenameInterfacePropertyInBaseClasses(interfaceModel, implementationClass, interfaceProperty, implementationClassProperty.Name);
+ interfaceProperty.Name = implementationClassProperty.Name;
+ }
+ }
+ }
+ }
+ }
+
+ private static void RenameInterfacePropertyInBaseClasses(InterfaceModel interfaceModel, ReferenceTypeModel implementationClass,
+ PropertyModel interfaceProperty, string newName)
+ {
+ foreach (var interfaceModelImplementationClass in interfaceModel.AllDerivedReferenceTypes().Where(c =>
+ c != implementationClass))
+ {
+ foreach (var propertyModel in interfaceModelImplementationClass.Properties.Where(p =>
+ p.Name == interfaceProperty.Name))
+ {
+ propertyModel.Name = newName;
+ }
+ }
}
private void ResolveDependencies(XmlSchema schema, List dependencyOrder, HashSet seenSchemas)
@@ -217,7 +277,7 @@ private TypeModel CreateTypeModel(Uri source, XmlSchemaGroup group, NamespaceMod
interfaceModel.Properties.AddRange(properties);
var interfaces = items.Select(i => i.XmlParticle).OfType()
.Select(i => (InterfaceModel)CreateTypeModel(CodeUtilities.CreateUri(i.SourceUri), Groups[i.RefName], i.RefName));
- interfaceModel.Interfaces.AddRange(interfaces);
+ interfaceModel.AddInterfaces(interfaces);
return interfaceModel;
}
@@ -249,7 +309,7 @@ private TypeModel CreateTypeModel(Uri source, XmlSchemaAttributeGroup attributeG
interfaceModel.Properties.AddRange(properties);
var interfaces = items.OfType()
.Select(a => (InterfaceModel)CreateTypeModel(CodeUtilities.CreateUri(a.SourceUri), AttributeGroups[a.RefName], a.RefName));
- interfaceModel.Interfaces.AddRange(interfaces);
+ interfaceModel.AddInterfaces(interfaces);
return interfaceModel;
}
@@ -307,17 +367,19 @@ private TypeModel CreateTypeModel(Uri source, XmlSchemaComplexType complexType,
}
else particle = complexType.Particle ?? complexType.ContentTypeParticle;
- var items = GetElements(particle, complexType);
- var properties = CreatePropertiesForElements(source, classModel, particle, items);
- classModel.Properties.AddRange(properties);
+ var items = GetElements(particle, complexType).ToList();
if (_configuration.GenerateInterfaces)
{
var interfaces = items.Select(i => i.XmlParticle).OfType()
- .Select(i => (InterfaceModel)CreateTypeModel(CodeUtilities.CreateUri(i.SourceUri), Groups[i.RefName], i.RefName));
- classModel.Interfaces.AddRange(interfaces);
+ .Select(i => (InterfaceModel)CreateTypeModel(CodeUtilities.CreateUri(i.SourceUri), Groups[i.RefName], i.RefName)).ToList();
+
+ classModel.AddInterfaces(interfaces);
}
+ var properties = CreatePropertiesForElements(source, classModel, particle, items);
+ classModel.Properties.AddRange(properties);
+
XmlSchemaObjectCollection attributes = null;
if (classModel.BaseClass != null)
{
@@ -344,7 +406,7 @@ private TypeModel CreateTypeModel(Uri source, XmlSchemaComplexType complexType,
{
var attributeInterfaces = attributes.OfType()
.Select(i => (InterfaceModel)CreateTypeModel(CodeUtilities.CreateUri(i.SourceUri), AttributeGroups[i.RefName], i.RefName));
- classModel.Interfaces.AddRange(attributeInterfaces);
+ classModel.AddInterfaces(attributeInterfaces);
}
}
@@ -413,6 +475,7 @@ private TypeModel CreateTypeModel(XmlSchemaSimpleType simpleType, NamespaceModel
enumModel.Values.Add(value);
}
+ enumModel.Values = EnsureEnumValuesUnique(enumModel.Values);
if (namespaceModel != null)
{
namespaceModel.Types[enumModel.Name] = enumModel;
@@ -459,6 +522,23 @@ private TypeModel CreateTypeModel(XmlSchemaSimpleType simpleType, NamespaceModel
return simpleModel;
}
+ private static List EnsureEnumValuesUnique(List enumModelValues)
+ {
+ var enumValueGroups = from enumValue in enumModelValues
+ group enumValue by enumValue.Name;
+
+ foreach (var g in enumValueGroups)
+ {
+ var i = 1;
+ foreach (var t in g.Skip(1))
+ {
+ t.Name = $"{t.Name}{i++}";
+ }
+ }
+
+ return enumModelValues;
+ }
+
private IEnumerable CreatePropertiesForAttributes(Uri source, TypeModel typeModel, IEnumerable items)
{
var properties = new List();
@@ -579,6 +659,7 @@ private IEnumerable CreatePropertiesForElements(Uri source, TypeM
}
var propertyName = _configuration.NamingProvider.ElementNameFromQualifiedName(element.QualifiedName);
+ var originalPropertyName = propertyName;
if (propertyName == typeModel.Name)
{
propertyName += "Property"; // member names cannot be the same as their enclosing type
@@ -589,6 +670,7 @@ private IEnumerable CreatePropertiesForElements(Uri source, TypeM
OwningType = typeModel,
XmlSchemaName = element.QualifiedName,
Name = propertyName,
+ OriginalPropertyName = originalPropertyName,
Type = CreateTypeModel(source, element.ElementSchemaType, elementQualifiedName),
IsNillable = element.IsNillable,
IsNullable = item.MinOccurs < 1.0m || (item.XmlParent is XmlSchemaChoice),
diff --git a/XmlSchemaClassGenerator/TypeModel.cs b/XmlSchemaClassGenerator/TypeModel.cs
index 9d13a07e..8740962e 100644
--- a/XmlSchemaClassGenerator/TypeModel.cs
+++ b/XmlSchemaClassGenerator/TypeModel.cs
@@ -1,6 +1,7 @@
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
+using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
@@ -193,17 +194,16 @@ public virtual CodeExpression GetDefaultValueFor(string defaultString, bool attr
}
}
- public class InterfaceModel : TypeModel
+ public class InterfaceModel : ReferenceTypeModel
{
public InterfaceModel(GeneratorConfiguration configuration)
: base(configuration)
{
Properties = new List();
- Interfaces = new List();
+ DerivedTypes = new List();
}
- public List Properties { get; set; }
- public List Interfaces { get; set; }
+ public List DerivedTypes { get; set; }
public override CodeTypeDeclaration Generate()
{
@@ -225,23 +225,48 @@ public override CodeTypeDeclaration Generate()
Configuration.TypeVisitor(interfaceDeclaration, this);
return interfaceDeclaration;
}
+
+ public IEnumerable AllDerivedReferenceTypes()
+ {
+ foreach (var interfaceModelDerivedType in DerivedTypes)
+ {
+ yield return interfaceModelDerivedType;
+ switch (interfaceModelDerivedType)
+ {
+ case InterfaceModel derivedInterfaceModel:
+ {
+ foreach (var referenceTypeModel in derivedInterfaceModel.AllDerivedReferenceTypes())
+ {
+ yield return referenceTypeModel;
+ }
+
+ break;
+ }
+ case ClassModel derivedClassModel:
+ {
+ foreach (var baseClass in derivedClassModel.GetAllDerivedTypes())
+ {
+ yield return baseClass;
+ }
+
+ break;
+ }
+ }
+ }
+ }
}
- public class ClassModel : TypeModel
+ public class ClassModel : ReferenceTypeModel
{
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; }
public List DerivedTypes { get; set; }
public ClassModel(GeneratorConfiguration configuration)
: base(configuration)
{
- Properties = new List();
- Interfaces = new List();
DerivedTypes = new List();
}
@@ -516,11 +541,35 @@ public override CodeExpression GetDefaultValueFor(string defaultString, bool att
}
}
+ public class ReferenceTypeModel : TypeModel
+ {
+ public ReferenceTypeModel(GeneratorConfiguration configuration)
+ : base(configuration)
+ {
+ Properties = new List();
+ Interfaces = new List();
+ }
+
+ public List Properties { get; set; }
+ public List Interfaces { get; }
+
+ public void AddInterfaces(IEnumerable interfaces)
+ {
+ foreach (var interfaceModel in interfaces)
+ {
+ Interfaces.Add(interfaceModel);
+ interfaceModel.DerivedTypes.Add(this);
+ }
+
+ }
+ }
+
[DebuggerDisplay("{Name}")]
public class PropertyModel
{
public TypeModel OwningType { get; set; }
public string Name { get; set; }
+ public string OriginalPropertyName { get; set; }
public bool IsAttribute { get; set; }
public TypeModel Type { get; set; }
public bool IsNullable { get; set; }