From 5f129d99e87111a6b4430db7617002da0ee75608 Mon Sep 17 00:00:00 2001 From: jacob povar Date: Mon, 30 Nov 2020 16:10:16 +0300 Subject: [PATCH] Use nullable reference for AnyType if required --- .../NullableReferenceTypesTests.cs | 152 ++++++++++++++++++ .../CSharpTypeResolver.cs | 7 +- 2 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs new file mode 100644 index 000000000..5291206cf --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs @@ -0,0 +1,152 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using NJsonSchema.CodeGeneration.CSharp; +using NJsonSchema.Generation; +using Xunit; + +namespace NJsonSchema.CodeGeneration.Tests.CSharp +{ + public class NullableReferenceTypesTests + { + private class ClassWithRequiredObject + { + public object Property { get; set; } + + [Required] + [Newtonsoft.Json.JsonProperty("property2", Required = Newtonsoft.Json.Required.Always)] + public object Property2 { get; set; } + } + + [Fact] + public async Task When_property_is_optional_and_GenerateNullableReferenceTypes_is_not_set_then_CSharp_property_is_not_nullable() + { + //// Arrange + var schema = JsonSchema.FromType(new JsonSchemaGeneratorSettings + { + SchemaType = SchemaType.OpenApi3 + }); + var schemaData = schema.ToJson(); + + //// Act + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco, + SchemaType = SchemaType.OpenApi3, + GenerateNullableReferenceTypes = false + }); + var code = generator.GenerateFile("MyClass"); + + //// Assert + Assert.Contains("public object Property { get; set; }", code); + Assert.Contains("public object Property2 { get; set; }", code); + } + + [Fact] + public async Task When_property_is_optional_and_GenerateNullableOptionalProperties_is_set_then_CSharp_property_is_nullable() + { + //// Arrange + var schema = JsonSchema.FromType(new JsonSchemaGeneratorSettings + { + SchemaType = SchemaType.OpenApi3 + }); + var schemaData = schema.ToJson(); + + //// Act + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco, + SchemaType = SchemaType.OpenApi3, + GenerateNullableReferenceTypes = true + }); + var code = generator.GenerateFile("MyClass"); + + //// Assert + Assert.Contains("public object? Property { get; set; }= default!;", code); + Assert.Contains("public object Property2 { get; set; }= default!;", code); + } + + [Fact] + public async Task When_generating_from_json_schema_property_is_optional_and_GenerateNullableOptionalProperties_is_not_set_then_CSharp_property() + { + //// Arrange + + // CSharpGenerator `new object()` adds = new object() initializer to property only if it's explicitly marked + // as having `type: object` in json schema + var schemaJson = @" + { + ""type"": ""object"", + ""required"": [ + ""property2"" + ], + ""properties"": { + ""Property"": { + ""x-nullable"": true, + ""type"": ""object"" + }, + ""property2"": { + ""type"": ""object"" + } + } + } + "; + + var schema = await JsonSchema.FromJsonAsync(schemaJson); + var schemaData = schema.ToJson(); + + //// Act + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco, + SchemaType = SchemaType.OpenApi3, + GenerateNullableReferenceTypes = false + }); + var code = generator.GenerateFile("MyClass"); + + //// Assert + Assert.Contains("public object Property { get; set; }", code); + Assert.Contains("public object Property2 { get; set; } = new object();", code); + } + + [Fact] + public async Task When_generating_from_json_schema_property_is_optional_and_GenerateNullableOptionalProperties_is_set_then_CSharp_property() + { + //// Arrange + + // CSharpGenerator `new object()` adds = new object() initializer to property only if it's explicitly marked + // as having `type: object` in json schema + var schemaJson = @" + { + ""type"": ""object"", + ""required"": [ + ""property2"" + ], + ""properties"": { + ""Property"": { + ""x-nullable"": true, + ""type"": ""object"" + }, + ""property2"": { + ""type"": ""object"" + } + } + } + "; + + var schema = await JsonSchema.FromJsonAsync(schemaJson); + var schemaData = schema.ToJson(); + + //// Act + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco, + SchemaType = SchemaType.OpenApi3, + GenerateNullableReferenceTypes = true + }); + var code = generator.GenerateFile("MyClass"); + + //// Assert + Assert.Contains("public object? Property { get; set; }= default!;", code); + Assert.Contains("public object Property2 { get; set; } = new object();", code); + } + } +} \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs index cf0a5eef3..fb26820d8 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs @@ -75,13 +75,15 @@ schema is JsonSchemaProperty property && isNullable = true; } + var markAsNullableType = Settings.GenerateNullableReferenceTypes && isNullable; + if (schema.ActualTypeSchema.IsAnyType && schema.InheritedSchema == null && // not in inheritance hierarchy schema.AllOf.Count == 0 && !Types.Keys.Contains(schema) && !schema.HasReference) { - return Settings.AnyType; + return markAsNullableType ? Settings.AnyType + "?" : Settings.AnyType; } var type = schema.ActualTypeSchema.Type; @@ -107,7 +109,8 @@ schema is JsonSchemaProperty property && return ResolveBoolean(isNullable); } - var nullableReferenceType = Settings.GenerateNullableReferenceTypes && isNullable ? "?" : string.Empty; + + var nullableReferenceType = markAsNullableType ? "?" : string.Empty; if (schema.IsBinary) {