diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs
index 33549254..21dd017f 100644
--- a/XmlSchemaClassGenerator.Tests/XmlTests.cs
+++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs
@@ -1100,6 +1100,89 @@ public void ChoiceMembersAreNullable()
Assert.Contains("Opt4Specified", content);
}
+ [Fact]
+ public void NestedElementInChoiceIsNullable()
+ {
+ // Because nullability isn't directly exposed in the generated C#, we use "XXXSpecified" on a value type
+ // as a proxy.
+ const string xsd = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+";
+
+ var generator = new Generator
+ {
+ NamespaceProvider = new NamespaceProvider
+ {
+ GenerateNamespace = key => "Test"
+ }
+ };
+ var contents = ConvertXml(nameof(NestedElementInChoiceIsNullable), xsd, generator);
+ var content = Assert.Single(contents);
+
+ Assert.Contains("ElementASpecified", content);
+ Assert.Contains("ElementBSpecified", content);
+ }
+
+ [Fact]
+ public void OnlyFirstElementOfNestedElementsIsForcedToNullableInChoice()
+ {
+ // Because nullability isn't directly exposed in the generated C#, we use the "RequiredAttribute"
+ // as a proxy.
+ const string xsd = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+";
+
+ var generator = new Generator
+ {
+ NamespaceProvider = new NamespaceProvider
+ {
+ GenerateNamespace = key => "Test"
+ }
+ };
+ var contents = ConvertXml(nameof(OnlyFirstElementOfNestedElementsIsForcedToNullableInChoice), xsd, generator).ToArray();
+ var assembly = Compiler.Compile(nameof(OnlyFirstElementOfNestedElementsIsForcedToNullableInChoice), contents);
+
+ var elementWithChildProperty = assembly.GetType("Test.Root")?.GetProperty("ElementWithChild");
+ var nestedChildProperty = assembly.GetType("Test.RootElementWithChild")?.GetProperty("NestedChild");
+ Assert.NotNull(elementWithChildProperty);
+ Assert.NotNull(nestedChildProperty);
+
+ Type requiredType = typeof(System.ComponentModel.DataAnnotations.RequiredAttribute);
+ bool elementWithChildIsRequired = Attribute.GetCustomAttribute(elementWithChildProperty, requiredType) != null;
+ bool nestedChildIsRequired = Attribute.GetCustomAttribute(nestedChildProperty, requiredType) != null;
+ Assert.False(elementWithChildIsRequired);
+ Assert.True(nestedChildIsRequired);
+ }
+
[Fact]
public void AssemblyVisibleIsInternalClass()
{
diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs
index b6b81073..7202c7c8 100644
--- a/XmlSchemaClassGenerator/ModelBuilder.cs
+++ b/XmlSchemaClassGenerator/ModelBuilder.cs
@@ -917,6 +917,7 @@ private IEnumerable CreatePropertiesForElements(Uri source, TypeM
}
var effectiveElement = substitute?.Element ?? element;
+ var isNullableByChoice = IsNullableByChoice(item.XmlParent);
var propertyName = _configuration.NamingProvider.ElementNameFromQualifiedName(effectiveElement.QualifiedName, effectiveElement);
var originalPropertyName = propertyName;
if (propertyName == typeModel.Name)
@@ -934,9 +935,9 @@ private IEnumerable CreatePropertiesForElements(Uri source, TypeM
OriginalPropertyName = originalPropertyName,
Type = substitute?.Type ?? CreateTypeModel(element.ElementSchemaType, elementQualifiedName),
IsNillable = element.IsNillable,
- IsNullable = item.MinOccurs < 1.0m || (item.XmlParent is XmlSchemaChoice),
+ IsNullable = item.MinOccurs < 1.0m || isNullableByChoice,
IsCollection = item.MaxOccurs > 1.0m || particle.MaxOccurs > 1.0m, // http://msdn.microsoft.com/en-us/library/vstudio/d3hx2s7e(v=vs.100).aspx
- DefaultValue = element.DefaultValue ?? ((item.MinOccurs >= 1.0m && item.XmlParent is not XmlSchemaChoice) ? element.FixedValue : null),
+ DefaultValue = element.DefaultValue ?? ((item.MinOccurs >= 1.0m && !isNullableByChoice) ? element.FixedValue : null),
FixedValue = element.FixedValue,
XmlNamespace = !string.IsNullOrEmpty(effectiveElement.QualifiedName.Namespace) && effectiveElement.QualifiedName.Namespace != typeModel.XmlSchemaName.Namespace
? effectiveElement.QualifiedName.Namespace : null,
@@ -969,7 +970,7 @@ private IEnumerable CreatePropertiesForElements(Uri source, TypeM
OwningType = typeModel,
Name = "Any",
Type = new SimpleModel(_configuration) { ValueType = (_configuration.UseXElementForAny ? typeof(XElement) : typeof(XmlElement)), UseDataTypeAttribute = false },
- IsNullable = item.MinOccurs < 1.0m || (item.XmlParent is XmlSchemaChoice),
+ IsNullable = item.MinOccurs < 1.0m || IsNullableByChoice(item.XmlParent),
IsCollection = item.MaxOccurs > 1.0m || particle.MaxOccurs > 1.0m, // http://msdn.microsoft.com/en-us/library/vstudio/d3hx2s7e(v=vs.100).aspx
IsAny = true,
XmlParticle = item.XmlParticle,
@@ -1020,6 +1021,27 @@ private IEnumerable CreatePropertiesForElements(Uri source, TypeM
return properties;
}
+ private static bool IsNullableByChoice(XmlSchemaObject parent)
+ {
+ while (parent != null)
+ {
+ switch (parent)
+ {
+ case XmlSchemaChoice:
+ return true;
+ // Any ancestor element between the current item and the
+ // choice would already have been forced to nullable.
+ case XmlSchemaElement:
+ case XmlSchemaParticle p when p.MinOccurs < 1.0m:
+ return false;
+ default:
+ break;
+ }
+ parent = parent.Parent;
+ }
+ return false;
+ }
+
private NamespaceModel CreateNamespaceModel(Uri source, XmlQualifiedName qualifiedName)
{
NamespaceModel namespaceModel = null;