diff --git a/README.md b/README.md index 807deb7f..7cbca397 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,11 @@ Options: pattern (default is false) --sf, --separateFiles generate a separate file for each class (default is false) + --dnfin, --doNotForceIsNullable + do not force generator to emit IsNullable = true + in XmlElement annotation for nillable elements + when element is nullable (minOccurs < 1 or + parent element is choice) (default is false) ``` For use from code use the [library NuGet package](https://www.nuget.org/packages/XmlSchemaClassGenerator-beta/): diff --git a/XmlSchemaClassGenerator.Console/Program.cs b/XmlSchemaClassGenerator.Console/Program.cs index 4babcbf3..65db3582 100644 --- a/XmlSchemaClassGenerator.Console/Program.cs +++ b/XmlSchemaClassGenerator.Console/Program.cs @@ -46,6 +46,7 @@ static void Main(string[] args) var useShouldSerialize = false; var separateClasses = false; var collectionSettersMode = CollectionSettersMode.Private; + var doNotForceIsNullable = false; var options = new OptionSet { { "h|help", "show this message and exit", v => showHelp = v != null }, @@ -120,6 +121,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l { "cc|complexTypesForCollections", "generate complex types for collections (default is true)", v => generateComplexTypesForCollections = v != null }, { "s|useShouldSerialize", "use ShouldSerialize pattern instead of Specified pattern (default is false)", v => useShouldSerialize = v != null }, { "sf|separateFiles", "generate a separate file for each class (default is false)", v => separateClasses = v != null }, + { "dnfin|doNotForceIsNullable", "do not force generator to emit IsNullable = true in XmlElement annotation for nillable elements when element is nullable (minOccurs < 1 or parent element is choice) (default is false)", v => doNotForceIsNullable = v != null } }; var globsAndUris = options.Parse(args); @@ -188,7 +190,8 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l GenerateComplexTypesForCollections = generateComplexTypesForCollections, UseShouldSerializePattern = useShouldSerialize, SeparateClasses = separateClasses, - CollectionSettersMode = collectionSettersMode + CollectionSettersMode = collectionSettersMode, + DoNotForceIsNullable = doNotForceIsNullable }; if (pclCompatible) diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index 6382e47f..8fa6da13 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -53,7 +53,8 @@ private IEnumerable ConvertXml(string name, IEnumerable xsds, Ge AssemblyVisible = generatorPrototype.AssemblyVisible, GenerateInterfaces = generatorPrototype.GenerateInterfaces, MemberVisitor = generatorPrototype.MemberVisitor, - CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions + CodeTypeReferenceOptions = generatorPrototype.CodeTypeReferenceOptions, + DoNotForceIsNullable = generatorPrototype.DoNotForceIsNullable }; var set = new XmlSchemaSet(); @@ -1941,11 +1942,110 @@ public void TestShouldPatternForCollections() }; var contents = ConvertXml(nameof(TestShouldPatternForCollections), xsd, generator).ToArray(); - Assert.Equal(1, contents.Length); - var assembly = Compiler.Compile(nameof(GenerateXmlRootAttributeForEnumTest), contents); + Assert.Single(contents); + var assembly = Compiler.Compile(nameof(TestShouldPatternForCollections), contents); + var testType = assembly.GetType("Test_NS1.TestType"); + var serializer = new XmlSerializer(testType); + Assert.NotNull(serializer); + } + + + [Fact] + public void TestDoNotForceIsNullableGeneration() + { + const string xsd = @" + + + + + + + + + + + "; + + var generator = new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => key.XmlSchemaNamespace + }, + DoNotForceIsNullable = true + }; + + var contents = ConvertXml(nameof(TestDoNotForceIsNullableGeneration), xsd, generator).ToArray(); + Assert.Single(contents); + var assembly = Compiler.Compile(nameof(TestDoNotForceIsNullableGeneration), contents); + var testType = assembly.GetType("Test_NS1.TestType"); + var serializer = new XmlSerializer(testType); + Assert.NotNull(serializer); + + var prop = testType.GetProperty("StringProperty"); + Assert.NotNull(prop); + var xmlElementAttribute = prop.GetCustomAttribute(); + Assert.False(xmlElementAttribute.IsNullable); + + prop = testType.GetProperty("StringNullableProperty"); + Assert.NotNull(prop); + var xmlElementNullableAttribute = prop.GetCustomAttribute(); + Assert.True(xmlElementNullableAttribute.IsNullable); + + prop = testType.GetProperty("StringNullableProperty2"); + Assert.NotNull(prop); + var xmlElementNullableAttribute2 = prop.GetCustomAttribute(); + Assert.True(xmlElementNullableAttribute2.IsNullable); + } + + [Fact] + public void TestForceIsNullableGeneration() + { + const string xsd = @" + + + + + + + + + + + "; + + var generator = new Generator + { + NamespaceProvider = new NamespaceProvider + { + GenerateNamespace = key => key.XmlSchemaNamespace + }, + DoNotForceIsNullable = false + }; + + var contents = ConvertXml(nameof(TestForceIsNullableGeneration), xsd, generator).ToArray(); + Assert.Single(contents); + var assembly = Compiler.Compile(nameof(TestForceIsNullableGeneration), contents); var testType = assembly.GetType("Test_NS1.TestType"); var serializer = new XmlSerializer(testType); Assert.NotNull(serializer); + + var prop = testType.GetProperty("StringProperty"); + Assert.NotNull(prop); + var xmlElementAttribute = prop.GetCustomAttribute(); + Assert.True(xmlElementAttribute.IsNullable); + + prop = testType.GetProperty("StringNullableProperty"); + Assert.NotNull(prop); + var xmlElementNullableAttribute = prop.GetCustomAttribute(); + Assert.True(xmlElementNullableAttribute.IsNullable); + + prop = testType.GetProperty("StringNullableProperty2"); + Assert.NotNull(prop); + var xmlElementNullableAttribute2 = prop.GetCustomAttribute(); + Assert.True(xmlElementNullableAttribute2.IsNullable); } } } diff --git a/XmlSchemaClassGenerator/Generator.cs b/XmlSchemaClassGenerator/Generator.cs index f62006bd..7a4978bb 100644 --- a/XmlSchemaClassGenerator/Generator.cs +++ b/XmlSchemaClassGenerator/Generator.cs @@ -228,6 +228,12 @@ public bool DisableComments set { _configuration.DisableComments = value; } } + public bool DoNotForceIsNullable + { + get { return _configuration.DoNotForceIsNullable; } + set { _configuration.DoNotForceIsNullable = value; } + } + public string PrivateMemberPrefix { get { return _configuration.PrivateMemberPrefix; } diff --git a/XmlSchemaClassGenerator/GeneratorConfiguration.cs b/XmlSchemaClassGenerator/GeneratorConfiguration.cs index 5647da5a..e6d2e207 100644 --- a/XmlSchemaClassGenerator/GeneratorConfiguration.cs +++ b/XmlSchemaClassGenerator/GeneratorConfiguration.cs @@ -206,6 +206,12 @@ public void WriteLog(string message) public bool DisableComments { get; set; } + /// + /// If True then do not force generator to emit IsNullable=true in XmlElement annotation + /// for nillable elements when element is nullable (minOccurs < 1 or parent element is choice) + /// + public bool DoNotForceIsNullable { get; set; } + public string PrivateMemberPrefix { get; set; } = "_"; /// diff --git a/XmlSchemaClassGenerator/TypeModel.cs b/XmlSchemaClassGenerator/TypeModel.cs index 4dcba29c..b8f3aa74 100644 --- a/XmlSchemaClassGenerator/TypeModel.cs +++ b/XmlSchemaClassGenerator/TypeModel.cs @@ -1226,7 +1226,7 @@ private IEnumerable GetAttributes(bool isArray) } } - if (IsNillable && !(IsCollection && Type is SimpleModel m && m.ValueType.IsValueType)) + if (IsNillable && !(IsCollection && Type is SimpleModel m && m.ValueType.IsValueType) && !(IsNullable && Configuration.DoNotForceIsNullable)) { attribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression(true))); }