diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/AdditionalPropertiesTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/AdditionalPropertiesTests.cs index 6f5329547..0df010cfa 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/AdditionalPropertiesTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/AdditionalPropertiesTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading.Tasks; using NJsonSchema.CodeGeneration.CSharp; @@ -141,7 +141,84 @@ public async Task When_using_SystemTextJson_additionalProperties_schema_is_set_f // There are two matches, the Person class and the Pet class Assert.Equal(2, matches.Count); } + + [Fact] + public async Task When_using_SystemTextJson_additionalProperties_schema_is_set_for_object_then_special_property_is_rendered_only_for_lowest_base_class() + { + var json = + @"{ + ""properties"": { + ""Name"": { + ""type"": ""string"" + } + }, + ""definitions"": { + ""Cat"": { + ""allOf"": [ + { + ""$ref"": ""#/definitions/Pet"" + }, + { + ""type"": ""object"", + ""additionalProperties"": { + ""nullable"": true + }, + ""properties"": { + ""whiskers"": { + ""type"": ""string"" + } + } + } + ] + }, + ""Pet"": { + ""allOf"": [ + { + ""$ref"": ""#/definitions/Animal"" + }, + { + ""type"": ""object"", + ""additionalProperties"": { + ""nullable"": true + }, + ""properties"": { + ""id"": { + ""type"": ""integer"", + ""format"": ""int64"" + } + } + } + ] + }, + ""Animal"": { + ""type"": ""object"", + ""properties"": { + ""category"": { + ""type"": ""string"", + ""nullable"": true + } + } + } + } +}"; + var schema = await JsonSchema.FromJsonAsync(json); + + //// Act + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings() + { + JsonLibrary = CSharpJsonLibrary.SystemTextJson + }); + + var code = generator.GenerateFile("SommeDummyClass"); + + //// Assert + var matches = Regex.Matches(code, @"(\[System\.Text\.Json\.Serialization\.JsonExtensionData\])"); + + // There are two matches, the SommeDummyClass class and the Animal class + Assert.Equal(2, matches.Count); + } + public class Page { } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs index 4847682ae..8ff26fca9 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------- +//----------------------------------------------------------------------- // <copyright file="ClassTemplateModel.cs" company="NJsonSchema"> // Copyright (c) Rico Suter. All rights reserved. // </copyright> @@ -62,15 +62,21 @@ public ClassTemplateModel(string typeName, CSharpGeneratorSettings settings, /// <summary>Gets a value indicating whether the C#8 nullable reference types are enabled for this file.</summary> public bool GenerateNullableReferenceTypes => _settings.GenerateNullableReferenceTypes; - /// <summary>Gets a value indicating whether an additional properties type is available and needed.</summary> + /// <summary>Gets a value indicating whether an additional properties type is available.</summary> public bool HasAdditionalPropertiesType => + HasAdditionalPropertiesTypeInBaseClass || // if the base class has them, inheritance dictates that this class will have them to !_schema.IsDictionary && !_schema.ActualTypeSchema.IsDictionary && !_schema.IsArray && !_schema.ActualTypeSchema.IsArray && (_schema.ActualTypeSchema.AllowAdditionalProperties || - _schema.ActualTypeSchema.AdditionalPropertiesSchema != null) - && BaseClass?.HasAdditionalPropertiesType != true; // if base class already has extension data array, we need to avoid it in the subclass + _schema.ActualTypeSchema.AdditionalPropertiesSchema != null); + + /// <summary>Gets a value indicating whether an additional properties type is available in the base class.</summary> + public bool HasAdditionalPropertiesTypeInBaseClass => BaseClass?.HasAdditionalPropertiesType ?? false; + + /// <summary> Gets a value indicating if the "Additional properties" property should be generated. </summary> + public bool GenerateAdditionalPropertiesProperty => HasAdditionalPropertiesType && !HasAdditionalPropertiesTypeInBaseClass; /// <summary>Gets the type of the additional properties.</summary> public string AdditionalPropertiesType => HasAdditionalPropertiesType ? "object" : null; // TODO: Find a way to use typed dictionaries diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid index 64648c55d..ffd3baed5 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid @@ -121,7 +121,7 @@ {%- endif %} {%- endfor -%} -{%- if HasAdditionalPropertiesType -%} +{%- if GenerateAdditionalPropertiesProperty -%} private System.Collections.Generic.IDictionary<string, {{ AdditionalPropertiesType }}> _additionalProperties = new System.Collections.Generic.Dictionary<string, {{ AdditionalPropertiesType }}>();