From fca4d1f4d9a5c72904f725142d0813eaa3f7d884 Mon Sep 17 00:00:00 2001 From: James Cracknell Date: Sat, 7 May 2022 21:01:44 -0400 Subject: [PATCH] Generate STJ JsonIgnoreAttributes for model properties (#1513) * Generate STJ JsonIgnoreAttributes for model properties * Update PropertyModel.cs Co-authored-by: Rico Suter --- .../GeneralGeneratorTests.cs | 126 ++++++++++++++++++ .../Models/PropertyModel.cs | 12 +- .../Templates/Class.liquid | 3 + 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs index e2941c5d6..306dd2ed5 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs @@ -738,6 +738,132 @@ public async Task When_property_is_required_then_CSharp_code_is_correct() AssertCompile(code); } + + [Fact] + public async Task When_using_SystemTextJson_JsonIgnoreAttributes_are_generated_based_on_optionality() { + //// Arrange + var schema = await JsonSchema.FromJsonAsync(@"{ + ""type"": ""object"", + ""required"": [""requiredValue"",""requiredRef""], + ""properties"": { + ""requiredValue"": { ""type"": ""integer"", ""format"": ""int32"" }, + ""requiredRef"": { ""type"": ""string"" }, + ""optionalValue"": { ""type"": ""integer"", ""format"": ""int32"" }, + ""optionalRef"": { ""type"": ""string"" } + } + }"); + + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { + JsonLibrary = CSharpJsonLibrary.SystemTextJson + }); + + static string Normalized(string str) => + Regex.Replace(str, @"\s+", " "); + + //// Act + var code = generator.GenerateFile("MyClass"); + + /// Assert + Assert.Contains( + Normalized(@"public int OptionalValue {"), + Normalized(code) + ); + + Assert.Contains( + Normalized(@" + [System.Text.Json.Serialization.JsonPropertyName(""requiredValue"")] + [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Never)] + "), + Normalized(code) + ); + + Assert.Contains( + Normalized(@" + [System.Text.Json.Serialization.JsonPropertyName(""requiredRef"")] + [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Never)] + "), + Normalized(code) + ); + + Assert.Contains( + Normalized(@" + [System.Text.Json.Serialization.JsonPropertyName(""optionalValue"")] + [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + "), + Normalized(code) + ); + + Assert.Contains( + Normalized(@" + [System.Text.Json.Serialization.JsonPropertyName(""optionalRef"")] + [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + "), + Normalized(code) + ); + } + + [Fact] + public async Task When_using_SystemTextJson_and_RequiredPropertiesMustBeDefined_is_false_JsonIgnoreAttributes_are_not_generated_for_required_properties() { + //// Arrange + var schema = await JsonSchema.FromJsonAsync(@"{ + ""type"": ""object"", + ""required"": [""required""], + ""properties"": { + ""required"": { ""type"": ""string"" } + } + }"); + + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { + JsonLibrary = CSharpJsonLibrary.SystemTextJson, + RequiredPropertiesMustBeDefined = false + }); + + static string Normalized(string str) => + Regex.Replace(str, @"\s+", " "); + + //// Act + var code = generator.GenerateFile("MyClass"); + + /// Assert + Assert.DoesNotContain( + Normalized(@" + [System.Text.Json.Serialization.JsonIgnore + "), + Normalized(code) + ); + } + + [Fact] + public async Task When_using_SystemTextJson_and_RequiredPropertiesMustBeDefined_is_false_JsonIgnoreAttributes_are_still_generated_for_optional_properties() { + //// Arrange + var schema = await JsonSchema.FromJsonAsync(@"{ + ""type"": ""object"", + ""required"": [], + ""properties"": { + ""optional"": { ""type"": ""string"" } + } + }"); + + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { + JsonLibrary = CSharpJsonLibrary.SystemTextJson, + RequiredPropertiesMustBeDefined = false + }); + + static string Normalized(string str) => + Regex.Replace(str, @"\s+", " "); + + //// Act + var code = generator.GenerateFile("MyClass"); + + /// Assert + Assert.Contains( + Normalized(@" + [System.Text.Json.Serialization.JsonPropertyName(""optional"")] + [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + "), + Normalized(code) + ); + } [Fact] public void When_array_property_is_required_or_not_then_the_code_has_correct_initializer() diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs index a62ba3c84..185392862 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs @@ -64,7 +64,17 @@ public class PropertyModel : PropertyModelBase (_property.ActualTypeSchema.IsArray && _settings.GenerateImmutableArrayProperties) || (_property.ActualTypeSchema.IsDictionary && _settings.GenerateImmutableDictionaryProperties) )) == false; - + + /// Indicates whether or not this property has a . + public bool HasJsonIgnoreCondition => JsonIgnoreCondition != null; + + /// Returns the System.Text.Json.Serialization.JsonIgnoreCondition value to be applied to the property. + public string JsonIgnoreCondition => _property switch { + { IsRequired: false } => "System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull", + { IsRequired: true } when _settings.RequiredPropertiesMustBeDefined => "System.Text.Json.Serialization.JsonIgnoreCondition.Never", + _ => null + }; + /// Gets the json property required. public string JsonPropertyRequiredCode { diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid index e70dfdb1d..64648c55d 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid @@ -53,6 +53,9 @@ {%- endif -%} {%- if UseSystemTextJson %} [System.Text.Json.Serialization.JsonPropertyName("{{ property.Name }}")] +{%- if property.HasJsonIgnoreCondition %} + [System.Text.Json.Serialization.JsonIgnore(Condition = {{ property.JsonIgnoreCondition }})] +{%- endif -%} {%- if property.IsStringEnumArray %} // TODO(system.text.json): Add string enum item converter {%- endif -%}