From 0823f9a70da21ceb258426eac07348078958e9ef Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 29 Dec 2025 16:18:05 +0000
Subject: [PATCH 1/6] Initial plan
From 7cb7392be1e0607830553f1524243fc67b62e34c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 29 Dec 2025 17:07:21 +0000
Subject: [PATCH 2/6] Handle enum in simpleContent complex types
Co-authored-by: mganss <976344+mganss@users.noreply.github.com>
---
XmlSchemaClassGenerator.Tests/XmlTests.cs | 93 ++++++++++++-------
.../xsd/simple/simplecontent-enum.xsd | 22 +++++
XmlSchemaClassGenerator/ModelBuilder.cs | 52 +++++++++++
XmlSchemaClassGenerator/Models/ClassModel.cs | 41 ++++++++
4 files changed, 174 insertions(+), 34 deletions(-)
create mode 100644 XmlSchemaClassGenerator.Tests/xsd/simple/simplecontent-enum.xsd
diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs
index 279b9631..4660bf32 100644
--- a/XmlSchemaClassGenerator.Tests/XmlTests.cs
+++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs
@@ -186,40 +186,65 @@ public void TestGuid()
[Fact, TestPriority(1)]
[UseCulture("en-US")]
- public void TestUnion()
- {
- var assembly = Compiler.Generate("Union", UnionPattern, new Generator
- {
- NamespacePrefix = "Union",
- IntegerDataType = typeof(int),
- MapUnionToWidestCommonType = true
- });
-
- Assert.NotNull(assembly);
-
- SharedTestFunctions.TestSamples(Output, "Union", UnionPattern);
-
- var snapshotType = assembly.GetType("Union.Snapshot");
- Assert.NotNull(snapshotType);
-
- var date = snapshotType.GetProperty("Date");
- Assert.NotNull(date);
- Assert.Equal(typeof(DateTime), date.PropertyType);
-
- var count = snapshotType.GetProperty("Count");
- Assert.NotNull(count);
- Assert.Equal(typeof(int), count.PropertyType);
-
- var num = snapshotType.GetProperty("Num");
- Assert.NotNull(num);
- Assert.Equal(typeof(decimal), num.PropertyType);
- }
-
- [Fact, TestPriority(1)]
- [UseCulture("en-US")]
- public void TestList()
- {
- Compiler.Generate("List", ListPattern);
+ public void TestUnion()
+ {
+ var assembly = Compiler.Generate("Union", UnionPattern, new Generator
+ {
+ NamespacePrefix = "Union",
+ IntegerDataType = typeof(int),
+ MapUnionToWidestCommonType = true
+ });
+
+ Assert.NotNull(assembly);
+
+ SharedTestFunctions.TestSamples(Output, "Union", UnionPattern);
+
+ var snapshotType = assembly.GetType("Union.Snapshot");
+ Assert.NotNull(snapshotType);
+
+ var date = snapshotType.GetProperty("Date");
+ Assert.NotNull(date);
+ Assert.Equal(typeof(DateTime), date.PropertyType);
+
+ var count = snapshotType.GetProperty("Count");
+ Assert.NotNull(count);
+ Assert.Equal(typeof(int), count.PropertyType);
+
+ var num = snapshotType.GetProperty("Num");
+ Assert.NotNull(num);
+ Assert.Equal(typeof(decimal), num.PropertyType);
+ }
+
+ [Fact, TestPriority(1)]
+ [UseCulture("en-US")]
+ public void TestSimpleContentEnum()
+ {
+ var assembly = Compiler.Generate("SimpleContentEnum", "xsd/simple/simplecontent-enum.xsd");
+
+ const string ns = "SimpleContentEnum.Simplecontent";
+
+ var enumType = assembly.GetType($"{ns}.TransConfirmationCodeTypeEnum");
+ if (enumType == null)
+ {
+ var names = string.Join(", ", assembly.GetTypes().Select(t => t.FullName));
+ Assert.Fail($"Enum type not found. Available types: {names}");
+ }
+
+ var type = assembly.GetType($"{ns}.TransConfirmationCodeType");
+ Assert.NotNull(type);
+
+ var baseType = assembly.GetType($"{ns}.CodeType");
+ Assert.Equal(baseType, type.BaseType);
+
+ var valueProperty = type.GetProperties().Single(p => p.PropertyType == enumType);
+ Assert.Equal("Value", valueProperty.Name);
+ }
+
+ [Fact, TestPriority(1)]
+ [UseCulture("en-US")]
+ public void TestList()
+ {
+ Compiler.Generate("List", ListPattern);
SharedTestFunctions.TestSamples(Output, "List", ListPattern);
}
diff --git a/XmlSchemaClassGenerator.Tests/xsd/simple/simplecontent-enum.xsd b/XmlSchemaClassGenerator.Tests/xsd/simple/simplecontent-enum.xsd
new file mode 100644
index 00000000..4e62daf7
--- /dev/null
+++ b/XmlSchemaClassGenerator.Tests/xsd/simple/simplecontent-enum.xsd
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs
index 34c35477..d995c01a 100644
--- a/XmlSchemaClassGenerator/ModelBuilder.cs
+++ b/XmlSchemaClassGenerator/ModelBuilder.cs
@@ -551,6 +551,15 @@ private TypeModel CreateTypeModel(XmlSchemaComplexType complexType)
}
}
+ if (complexType.ContentModel?.Content is XmlSchemaSimpleContentRestriction simpleContentRestriction)
+ {
+ var enumFacets = simpleContentRestriction.Facets?.OfType().ToList();
+ if (enumFacets?.Count > 0 && !_configuration.EnumAsString)
+ {
+ classModel.TextValueType = CreateSimpleContentEnumModel(classModel, enumFacets);
+ }
+ }
+
XmlSchemaParticle xmlParticle = null;
if (classModel.BaseClass != null)
{
@@ -723,6 +732,49 @@ private static List EnsureEnumValuesUnique(List
return enumModelValues;
}
+ private EnumModel CreateSimpleContentEnumModel(ClassModel classModel, List enumFacets)
+ {
+ var enumNamespace = namespaceModel?.Key.XmlSchemaNamespace ?? qualifiedName.Namespace;
+ var enumQualifiedName = qualifiedName.IsEmpty
+ ? new XmlQualifiedName($"{classModel.Name}Enum", enumNamespace)
+ : new XmlQualifiedName($"{qualifiedName.Name}Enum", enumNamespace);
+
+ var enumName = $"{classModel.Name}Enum";
+ if (namespaceModel != null)
+ enumName = namespaceModel.GetUniqueTypeName(enumName);
+
+ var enumModel = new EnumModel(_configuration)
+ {
+ Name = enumName,
+ Namespace = namespaceModel,
+ XmlSchemaName = enumQualifiedName,
+ IsAnonymous = false,
+ };
+
+ foreach (var facet in enumFacets.DistinctBy(f => f.Value))
+ {
+ var value = new EnumValueModel
+ {
+ Name = _configuration.NamingProvider.EnumMemberNameFromValue(enumModel.Name, facet.Value, facet),
+ Value = facet.Value
+ };
+
+ var valueDocs = GetDocumentation(facet);
+ value.Documentation.AddRange(valueDocs);
+
+ value.IsDeprecated = facet.Annotation?.Items.OfType()
+ .Any(a => Array.Exists(a.Markup, m => m.Name == "annox:annotate" && m.HasChildNodes && m.FirstChild.Name == "jl:Deprecated")) == true;
+
+ enumModel.Values.Add(value);
+ }
+
+ enumModel.Values = EnsureEnumValuesUnique(enumModel.Values);
+ if (namespaceModel != null)
+ namespaceModel.Types[enumModel.Name] = enumModel;
+
+ return enumModel;
+ }
+
private EnumModel CreateEnumModel(XmlSchemaSimpleType simpleType, List enumFacets)
{
// we got an enum
diff --git a/XmlSchemaClassGenerator/Models/ClassModel.cs b/XmlSchemaClassGenerator/Models/ClassModel.cs
index 5695082d..801c93dd 100644
--- a/XmlSchemaClassGenerator/Models/ClassModel.cs
+++ b/XmlSchemaClassGenerator/Models/ClassModel.cs
@@ -17,6 +17,7 @@ public class ClassModel(GeneratorConfiguration configuration) : ReferenceTypeMod
public bool IsMixed { get; set; }
public bool IsSubstitution { get; set; }
public TypeModel BaseClass { get; set; }
+ public TypeModel TextValueType { get; set; }
public List DerivedTypes { get; set; } = [];
public override bool IsSubtype => BaseClass != null;
@@ -95,6 +96,46 @@ public override CodeTypeDeclaration Generate()
if (BaseClass is ClassModel)
{
classDeclaration.BaseTypes.Add(BaseClass.GetReferenceFor(Namespace));
+
+ if (TextValueType != null && !string.IsNullOrEmpty(Configuration.TextValuePropertyName))
+ {
+ var textName = Configuration.TextValuePropertyName;
+ var enableDataBinding = Configuration.EnableDataBinding;
+ var typeReference = TextValueType.GetReferenceFor(Namespace);
+
+ CodeMemberField backingFieldMember = null;
+ if (enableDataBinding)
+ {
+ backingFieldMember = new CodeMemberField(typeReference, textName.ToBackingField(Configuration.PrivateMemberPrefix))
+ {
+ Attributes = MemberAttributes.Private
+ };
+ classDeclaration.Members.Add(backingFieldMember);
+ }
+
+ CodeMemberField text = new(typeReference, textName + PropertyModel.GetAccessors(backingFieldMember, enableDataBinding, TextValueType.GetPropertyValueTypeCode()))
+ {
+ Attributes = MemberAttributes.Public | MemberAttributes.New,
+ };
+
+ var docs = new List {
+ new() { Language = English, Text = "Gets or sets the text value." },
+ new() { Language = German, Text = "Ruft den Text ab oder legt diesen fest." }
+ };
+
+ docs.AddRange(TextValueType.Documentation);
+
+ var attribute = AttributeDecl();
+
+ text.Comments.AddRange(GetComments(docs).ToArray());
+
+ text.CustomAttributes.Add(attribute);
+ classDeclaration.Members.Add(text);
+
+ var valuePropertyModel = new PropertyModel(Configuration, textName, TextValueType, this);
+
+ Configuration.MemberVisitor(text, valuePropertyModel);
+ }
}
else if (!string.IsNullOrEmpty(Configuration.TextValuePropertyName))
{
From 9212607a4119dbf4a80b0da31a11224a36f51c6a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 30 Dec 2025 13:13:01 +0000
Subject: [PATCH 3/6] Initial plan
From ccfd47ef0ee9d7e5432c24254e8668148181a1da Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 30 Dec 2025 13:31:42 +0000
Subject: [PATCH 4/6] Fix PR #577: Generate enum types for simpleContent
restrictions
Fixed the issue where generating enums for simpleContent complex types with restrictions broke existing tests. The C# XmlSerializer has limitations that prevent derived classes from adding new XmlText properties when the base class already has one.
Solution: Generate the enum type for simpleContent restrictions, but don't add a new Value property in the derived class. Users can manually convert between the inherited string Value and the enum type.
- Updated ClassModel.cs to not generate Value property in derived classes with simpleContent enum restrictions
- Updated test to verify enum generation without expecting a strongly-typed Value property
- All originally failing tests now pass (TestSimple, TestEmptyKeyProviderGeneratorPrefix, AirspaceServicesTest1)
Co-authored-by: mganss <976344+mganss@users.noreply.github.com>
---
XmlSchemaClassGenerator.Tests/XmlTests.cs | 59 ++++++++++++--------
XmlSchemaClassGenerator/Models/ClassModel.cs | 47 ++++------------
2 files changed, 47 insertions(+), 59 deletions(-)
diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs
index 4660bf32..a423223a 100644
--- a/XmlSchemaClassGenerator.Tests/XmlTests.cs
+++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs
@@ -215,29 +215,42 @@ public void TestUnion()
Assert.Equal(typeof(decimal), num.PropertyType);
}
- [Fact, TestPriority(1)]
- [UseCulture("en-US")]
- public void TestSimpleContentEnum()
- {
- var assembly = Compiler.Generate("SimpleContentEnum", "xsd/simple/simplecontent-enum.xsd");
-
- const string ns = "SimpleContentEnum.Simplecontent";
-
- var enumType = assembly.GetType($"{ns}.TransConfirmationCodeTypeEnum");
- if (enumType == null)
- {
- var names = string.Join(", ", assembly.GetTypes().Select(t => t.FullName));
- Assert.Fail($"Enum type not found. Available types: {names}");
- }
-
- var type = assembly.GetType($"{ns}.TransConfirmationCodeType");
- Assert.NotNull(type);
-
- var baseType = assembly.GetType($"{ns}.CodeType");
- Assert.Equal(baseType, type.BaseType);
-
- var valueProperty = type.GetProperties().Single(p => p.PropertyType == enumType);
- Assert.Equal("Value", valueProperty.Name);
+ [Fact, TestPriority(1)]
+ [UseCulture("en-US")]
+ public void TestSimpleContentEnum()
+ {
+ var assembly = Compiler.Generate("SimpleContentEnum", "xsd/simple/simplecontent-enum.xsd");
+
+ const string ns = "SimpleContentEnum.Simplecontent";
+
+ // The enum type should be generated
+ var enumType = assembly.GetType($"{ns}.TransConfirmationCodeTypeEnum");
+ if (enumType == null)
+ {
+ var names = string.Join(", ", assembly.GetTypes().Select(t => t.FullName));
+ Assert.Fail($"Enum type not found. Available types: {names}");
+ }
+
+ // Verify it's an enum with the expected values
+ Assert.True(enumType.IsEnum);
+ var enumValues = Enum.GetNames(enumType);
+ Assert.Contains("Always", enumValues);
+ Assert.Contains("Never", enumValues);
+ Assert.Contains("OnError", enumValues);
+
+ // The derived class should exist and inherit from the base
+ var type = assembly.GetType($"{ns}.TransConfirmationCodeType");
+ Assert.NotNull(type);
+
+ var baseType = assembly.GetType($"{ns}.CodeType");
+ Assert.Equal(baseType, type.BaseType);
+
+ // The derived class should NOT have its own Value property
+ // It inherits the string Value property from the base class
+ // Users can manually convert between string and enum as needed
+ var valueProperty = type.GetProperty("Value");
+ Assert.NotNull(valueProperty);
+ Assert.Equal(typeof(string), valueProperty.PropertyType);
}
[Fact, TestPriority(1)]
diff --git a/XmlSchemaClassGenerator/Models/ClassModel.cs b/XmlSchemaClassGenerator/Models/ClassModel.cs
index 801c93dd..5ba69090 100644
--- a/XmlSchemaClassGenerator/Models/ClassModel.cs
+++ b/XmlSchemaClassGenerator/Models/ClassModel.cs
@@ -99,42 +99,17 @@ public override CodeTypeDeclaration Generate()
if (TextValueType != null && !string.IsNullOrEmpty(Configuration.TextValuePropertyName))
{
- var textName = Configuration.TextValuePropertyName;
- var enableDataBinding = Configuration.EnableDataBinding;
- var typeReference = TextValueType.GetReferenceFor(Namespace);
-
- CodeMemberField backingFieldMember = null;
- if (enableDataBinding)
- {
- backingFieldMember = new CodeMemberField(typeReference, textName.ToBackingField(Configuration.PrivateMemberPrefix))
- {
- Attributes = MemberAttributes.Private
- };
- classDeclaration.Members.Add(backingFieldMember);
- }
-
- CodeMemberField text = new(typeReference, textName + PropertyModel.GetAccessors(backingFieldMember, enableDataBinding, TextValueType.GetPropertyValueTypeCode()))
- {
- Attributes = MemberAttributes.Public | MemberAttributes.New,
- };
-
- var docs = new List {
- new() { Language = English, Text = "Gets or sets the text value." },
- new() { Language = German, Text = "Ruft den Text ab oder legt diesen fest." }
- };
-
- docs.AddRange(TextValueType.Documentation);
-
- var attribute = AttributeDecl();
-
- text.Comments.AddRange(GetComments(docs).ToArray());
-
- text.CustomAttributes.Add(attribute);
- classDeclaration.Members.Add(text);
-
- var valuePropertyModel = new PropertyModel(Configuration, textName, TextValueType, this);
-
- Configuration.MemberVisitor(text, valuePropertyModel);
+ // When a derived class has a simpleContent restriction with enum facets,
+ // we generate the enum type but do NOT add a new Value property in the derived class.
+ // This is because the C# XmlSerializer has limitations when dealing with simpleContent
+ // restrictions in inheritance hierarchies - it cannot handle a derived class that adds
+ // a new XmlText property when the base class already has one.
+ //
+ // The enum type is still generated and can be used for validation/conversion manually:
+ // e.g., var enumValue = (MyEnum)Enum.Parse(typeof(MyEnum), instance.Value);
+ //
+ // This is a compromise to work within XmlSerializer's constraints while still providing
+ // the enum type that users requested in issue #561.
}
}
else if (!string.IsNullOrEmpty(Configuration.TextValuePropertyName))
From 1d78055eacaa62d5a5da3ba4d77f92202da5ca71 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 30 Dec 2025 13:34:50 +0000
Subject: [PATCH 5/6] Address code review feedback
- Moved comment explaining enum generation to proper location outside if block
- Added null safety checks for qualifiedName in CreateSimpleContentEnumModel
- Improved code clarity and safety
Co-authored-by: mganss <976344+mganss@users.noreply.github.com>
---
XmlSchemaClassGenerator/ModelBuilder.cs | 4 ++--
XmlSchemaClassGenerator/Models/ClassModel.cs | 25 +++++++++-----------
2 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs
index d995c01a..c87c55e9 100644
--- a/XmlSchemaClassGenerator/ModelBuilder.cs
+++ b/XmlSchemaClassGenerator/ModelBuilder.cs
@@ -734,8 +734,8 @@ private static List EnsureEnumValuesUnique(List
private EnumModel CreateSimpleContentEnumModel(ClassModel classModel, List enumFacets)
{
- var enumNamespace = namespaceModel?.Key.XmlSchemaNamespace ?? qualifiedName.Namespace;
- var enumQualifiedName = qualifiedName.IsEmpty
+ var enumNamespace = namespaceModel?.Key.XmlSchemaNamespace ?? qualifiedName?.Namespace ?? "";
+ var enumQualifiedName = qualifiedName == null || qualifiedName.IsEmpty
? new XmlQualifiedName($"{classModel.Name}Enum", enumNamespace)
: new XmlQualifiedName($"{qualifiedName.Name}Enum", enumNamespace);
diff --git a/XmlSchemaClassGenerator/Models/ClassModel.cs b/XmlSchemaClassGenerator/Models/ClassModel.cs
index 5ba69090..cd865b09 100644
--- a/XmlSchemaClassGenerator/Models/ClassModel.cs
+++ b/XmlSchemaClassGenerator/Models/ClassModel.cs
@@ -97,20 +97,17 @@ public override CodeTypeDeclaration Generate()
{
classDeclaration.BaseTypes.Add(BaseClass.GetReferenceFor(Namespace));
- if (TextValueType != null && !string.IsNullOrEmpty(Configuration.TextValuePropertyName))
- {
- // When a derived class has a simpleContent restriction with enum facets,
- // we generate the enum type but do NOT add a new Value property in the derived class.
- // This is because the C# XmlSerializer has limitations when dealing with simpleContent
- // restrictions in inheritance hierarchies - it cannot handle a derived class that adds
- // a new XmlText property when the base class already has one.
- //
- // The enum type is still generated and can be used for validation/conversion manually:
- // e.g., var enumValue = (MyEnum)Enum.Parse(typeof(MyEnum), instance.Value);
- //
- // This is a compromise to work within XmlSerializer's constraints while still providing
- // the enum type that users requested in issue #561.
- }
+ // When a derived class has a simpleContent restriction with enum facets (TextValueType != null),
+ // we generate the enum type but do NOT add a new Value property in the derived class.
+ // This is because the C# XmlSerializer has limitations when dealing with simpleContent
+ // restrictions in inheritance hierarchies - it cannot handle a derived class that adds
+ // a new XmlText property when the base class already has one.
+ //
+ // The enum type is still generated (in ModelBuilder) and can be used for validation/conversion manually:
+ // e.g., var enumValue = (MyEnum)Enum.Parse(typeof(MyEnum), instance.Value);
+ //
+ // This is a compromise to work within XmlSerializer's constraints while still providing
+ // the enum type that users requested in issue #561.
}
else if (!string.IsNullOrEmpty(Configuration.TextValuePropertyName))
{
From 3c93a82ea6ea16751337a064a4bd7d42fc93d47c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 30 Dec 2025 14:47:40 +0000
Subject: [PATCH 6/6] Add EnumValue adapter property for simpleContent enum
restrictions
Added an adapter property as suggested in #562 that provides strongly-typed access to enum values while keeping the string Value property for XML serialization. The EnumValue property:
- Is marked with [XmlIgnore] to prevent serialization
- Returns nullable enum type
- Uses Enum.TryParse in getter to convert from string Value
- Updates Value in setter by calling ToString() on the enum
Example generated code:
```csharp
[XmlIgnore]
public TransConfirmationCodeTypeEnum? EnumValue {
get {
if (Enum.TryParse(typeof(TransConfirmationCodeTypeEnum), Value, true, out var result))
return (TransConfirmationCodeTypeEnum)result;
return null;
}
set {
Value = value?.ToString();
}
}
```
Co-authored-by: mganss <976344+mganss@users.noreply.github.com>
---
XmlSchemaClassGenerator.Tests/XmlTests.cs | 30 +++++-
XmlSchemaClassGenerator/Models/ClassModel.cs | 101 +++++++++++++++++--
2 files changed, 118 insertions(+), 13 deletions(-)
diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs
index a423223a..6a9eddc2 100644
--- a/XmlSchemaClassGenerator.Tests/XmlTests.cs
+++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs
@@ -245,12 +245,36 @@ public void TestSimpleContentEnum()
var baseType = assembly.GetType($"{ns}.CodeType");
Assert.Equal(baseType, type.BaseType);
- // The derived class should NOT have its own Value property
- // It inherits the string Value property from the base class
- // Users can manually convert between string and enum as needed
+ // The derived class inherits the string Value property from the base class
var valueProperty = type.GetProperty("Value");
Assert.NotNull(valueProperty);
Assert.Equal(typeof(string), valueProperty.PropertyType);
+
+ // The derived class should have an EnumValue adapter property
+ var enumValueProperty = type.GetProperty("EnumValue");
+ Assert.NotNull(enumValueProperty);
+ Assert.Equal(typeof(Nullable<>).MakeGenericType(enumType), enumValueProperty.PropertyType);
+
+ // Test that the EnumValue property works correctly
+ var instance = Activator.CreateInstance(type);
+ Assert.NotNull(instance);
+
+ // Set Value to a string and verify EnumValue returns the correct enum
+ valueProperty.SetValue(instance, "Always");
+ var enumValue = enumValueProperty.GetValue(instance);
+ Assert.NotNull(enumValue);
+ Assert.Equal("Always", enumValue.ToString());
+
+ // Set EnumValue and verify Value is updated
+ var alwaysValue = Enum.Parse(enumType, "Never");
+ enumValueProperty.SetValue(instance, alwaysValue);
+ var stringValue = valueProperty.GetValue(instance);
+ Assert.Equal("Never", stringValue);
+
+ // Set EnumValue to null and verify Value is null
+ enumValueProperty.SetValue(instance, null);
+ stringValue = valueProperty.GetValue(instance);
+ Assert.Null(stringValue);
}
[Fact, TestPriority(1)]
diff --git a/XmlSchemaClassGenerator/Models/ClassModel.cs b/XmlSchemaClassGenerator/Models/ClassModel.cs
index cd865b09..c20c1ebc 100644
--- a/XmlSchemaClassGenerator/Models/ClassModel.cs
+++ b/XmlSchemaClassGenerator/Models/ClassModel.cs
@@ -98,16 +98,97 @@ public override CodeTypeDeclaration Generate()
classDeclaration.BaseTypes.Add(BaseClass.GetReferenceFor(Namespace));
// When a derived class has a simpleContent restriction with enum facets (TextValueType != null),
- // we generate the enum type but do NOT add a new Value property in the derived class.
- // This is because the C# XmlSerializer has limitations when dealing with simpleContent
- // restrictions in inheritance hierarchies - it cannot handle a derived class that adds
- // a new XmlText property when the base class already has one.
- //
- // The enum type is still generated (in ModelBuilder) and can be used for validation/conversion manually:
- // e.g., var enumValue = (MyEnum)Enum.Parse(typeof(MyEnum), instance.Value);
- //
- // This is a compromise to work within XmlSerializer's constraints while still providing
- // the enum type that users requested in issue #561.
+ // we generate an adapter property that allows strongly-typed access to the enum values.
+ // We cannot add a new XmlText property because the XmlSerializer doesn't allow it when
+ // the base class already has one. Instead, we use an [XmlIgnore] adapter property.
+ if (TextValueType != null && !string.IsNullOrEmpty(Configuration.TextValuePropertyName))
+ {
+ var textName = Configuration.TextValuePropertyName;
+ var enumTypeReference = TextValueType.GetReferenceFor(Namespace);
+ var nullableEnumTypeReference = new CodeTypeReference(typeof(Nullable<>));
+ nullableEnumTypeReference.TypeArguments.Add(enumTypeReference);
+
+ // Create the EnumValue adapter property
+ var enumValueProperty = new CodeMemberProperty
+ {
+ Name = "EnumValue",
+ Type = nullableEnumTypeReference,
+ Attributes = MemberAttributes.Public,
+ HasGet = true,
+ HasSet = true
+ };
+
+ // Add [XmlIgnore] attribute
+ var ignoreAttribute = AttributeDecl();
+ enumValueProperty.CustomAttributes.Add(ignoreAttribute);
+
+ // Getter: Try to parse the Value property to enum
+ // if (Enum.TryParse(typeof(EnumType), Value, true, out var result))
+ // return (EnumType)result;
+ // return null;
+ var resultVariable = new CodeVariableDeclarationStatement(typeof(object), "result");
+ var tryParseCondition = new CodeMethodInvokeExpression(
+ new CodeTypeReferenceExpression(typeof(Enum)),
+ "TryParse",
+ new CodeTypeOfExpression(enumTypeReference),
+ new CodePropertyReferenceExpression(
+ new CodeThisReferenceExpression(),
+ textName),
+ new CodePrimitiveExpression(true),
+ new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression("result")));
+
+ var returnCastResult = new CodeMethodReturnStatement(
+ new CodeCastExpression(
+ nullableEnumTypeReference,
+ new CodeVariableReferenceExpression("result")));
+
+ var ifTryParse = new CodeConditionStatement(
+ tryParseCondition,
+ returnCastResult);
+
+ enumValueProperty.GetStatements.Add(resultVariable);
+ enumValueProperty.GetStatements.Add(ifTryParse);
+ enumValueProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(null)));
+
+ // Setter: Value = value?.ToString();
+ // Since CodeDOM doesn't support null-conditional operator, we need to check and set
+ var valueNotNull = new CodeBinaryOperatorExpression(
+ new CodePropertySetValueReferenceExpression(),
+ CodeBinaryOperatorType.IdentityInequality,
+ new CodePrimitiveExpression(null));
+
+ var setToString = new CodeAssignStatement(
+ new CodePropertyReferenceExpression(
+ new CodeThisReferenceExpression(),
+ textName),
+ new CodeMethodInvokeExpression(
+ new CodePropertySetValueReferenceExpression(),
+ "ToString"));
+
+ var setToNull = new CodeAssignStatement(
+ new CodePropertyReferenceExpression(
+ new CodeThisReferenceExpression(),
+ textName),
+ new CodePrimitiveExpression(null));
+
+ enumValueProperty.SetStatements.Add(
+ new CodeConditionStatement(
+ valueNotNull,
+ new CodeStatement[] { setToString },
+ new CodeStatement[] { setToNull }));
+
+ var docs = new List {
+ new() { Language = English, Text = "Gets or sets the typed value of the text content." },
+ new() { Language = German, Text = "Ruft den typisierten Wert des Textinhalts ab oder legt diesen fest." }
+ };
+
+ enumValueProperty.Comments.AddRange(GetComments(docs).ToArray());
+
+ classDeclaration.Members.Add(enumValueProperty);
+
+ var enumValuePropertyModel = new PropertyModel(Configuration, "EnumValue", TextValueType, this);
+ Configuration.MemberVisitor(enumValueProperty, enumValuePropertyModel);
+ }
}
else if (!string.IsNullOrEmpty(Configuration.TextValuePropertyName))
{