From b8086444799a601b293c1ff45c5bd68747b4eb6e Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Tue, 21 Nov 2023 15:47:56 +0200 Subject: [PATCH] Tolerate schema properties named AdditionalProperties --- .../GeneralGeneratorTests.cs | 30 +++++++++++++++- .../CSharpGenerator.cs | 12 +++---- .../Models/ClassTemplateModel.cs | 34 ++++++++++++++----- .../Templates/Class.liquid | 2 +- 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs index 5ac78e2ff..23bed12ca 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs @@ -13,7 +13,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; -using NJsonSchema.Generation; using NJsonSchema.NewtonsoftJson.Generation; using Xunit; @@ -1890,6 +1889,35 @@ public async Task When_schema_has_AdditionProperties_schema_then_JsonExtensionDa //// Assert Assert.Contains("JsonExtensionData", output); + Assert.Contains("IDictionary AdditionalProperties\n", output); + } + + + public class ClassWithAdditionalProperties + { + public string AdditionalProperties { get; set; } + + [JsonExtensionData] + public IDictionary ExtensionData { get; set; } + } + + [Fact] + public async Task When_schema_has_AdditionProperties_schema_and_type_has_member_with_same_name() + { + //// Arrange + var schema = NewtonsoftJsonSchemaGenerator.FromType(new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.OpenApi3 }); + var json = schema.ToJson(); + + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco + }); + + //// Act + var output = generator.GenerateFile("PersonAddress"); + + //// Assert + Assert.Contains("IDictionary AdditionalProperties2\n", output); } [Fact] diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs index 285e6a221..efc548001 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Linq; using NJsonSchema.CodeGeneration.CSharp.Models; -using NJsonSchema.CodeGeneration.Models; namespace NJsonSchema.CodeGeneration.CSharp { @@ -116,17 +115,16 @@ private CodeArtifact GenerateClass(JsonSchema schema, string typeName) { var model = new ClassTemplateModel(typeName, Settings, _resolver, schema, RootObject); - RenamePropertyWithSameNameAsClass(typeName, model.Properties); + RenamePropertyWithSameNameAsClass(typeName, model._properties); var template = Settings.TemplateFactory.CreateTemplate("CSharp", "Class", model); return new CodeArtifact(typeName, model.BaseClassName, CodeArtifactType.Class, CodeArtifactLanguage.CSharp, CodeArtifactCategory.Contract, template); } - private static void RenamePropertyWithSameNameAsClass(string typeName, IEnumerable properties) + private static void RenamePropertyWithSameNameAsClass(string typeName, List properties) { - var propertyModels = properties as PropertyModel[] ?? properties.ToArray(); PropertyModel? propertyWithSameNameAsClass = null; - foreach (var p in propertyModels) + foreach (var p in properties) { if (p.PropertyName == typeName) { @@ -139,12 +137,12 @@ private static void RenamePropertyWithSameNameAsClass(string typeName, IEnumerab { var number = 1; var candidate = typeName + number; - while (propertyModels.Any(p => p.PropertyName == candidate)) + while (properties.Exists(p => p.PropertyName == candidate)) { number++; } - propertyWithSameNameAsClass.PropertyName = propertyWithSameNameAsClass.PropertyName + number; + propertyWithSameNameAsClass.PropertyName += number; } } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs index ef3983f1b..8f4234ee4 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs @@ -18,6 +18,8 @@ public class ClassTemplateModel : ClassTemplateModelBase private readonly CSharpTypeResolver _resolver; private readonly JsonSchema _schema; private readonly CSharpGeneratorSettings _settings; + internal readonly List _properties; + private readonly List _allProperties; /// Initializes a new instance of the class. /// Name of the type. @@ -34,19 +36,32 @@ public ClassTemplateModel(string typeName, CSharpGeneratorSettings settings, _settings = settings; ClassName = typeName; - Properties = _schema.ActualProperties.Values - .Where(p => !p.IsInheritanceDiscriminator) - .Select(property => new PropertyModel(this, property, _resolver, _settings)) - .ToArray(); + + AdditionalPropertiesPropertyName = "AdditionalProperties"; + var actualProperties = _schema.ActualProperties; + _properties = new List(actualProperties.Count); + foreach (var property in actualProperties.Values) + { + if (!property.IsInheritanceDiscriminator) + { + _properties.Add(new PropertyModel(this, property, _resolver, _settings)); + if (property.Name == AdditionalPropertiesPropertyName) + { + AdditionalPropertiesPropertyName += "2"; + } + } + } if (schema.InheritedSchema != null) { BaseClass = new ClassTemplateModel(BaseClassName!, settings, resolver, schema.InheritedSchema, rootObject); - AllProperties = Properties.Concat(BaseClass.AllProperties).ToArray(); + _allProperties = new List(_properties.Count + BaseClass._allProperties.Count); + _allProperties.AddRange(_properties); + _allProperties.AddRange(BaseClass._allProperties); } else { - AllProperties = Properties; + _allProperties = _properties; } } @@ -85,11 +100,14 @@ public ClassTemplateModel(string typeName, CSharpGeneratorSettings settings, // _schema.AdditionalPropertiesSchema.IsNullable(_settings.SchemaType), // string.Empty) : null; + /// Gets property name for the additional properties. + public string? AdditionalPropertiesPropertyName { get; private set; } + /// Gets the property models. - public IEnumerable Properties { get; } + public IEnumerable Properties => _properties; /// Gets the property models with inherited properties. - public IEnumerable AllProperties { get; } + public IEnumerable AllProperties => _allProperties; /// Gets a value indicating whether the class has description. public bool HasDescription => !(_schema is JsonSchemaProperty) && diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid index 41666803f..e3104e024 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid @@ -130,7 +130,7 @@ {%- else -%} [Newtonsoft.Json.JsonExtensionData] {%- endif -%} - public System.Collections.Generic.IDictionary AdditionalProperties + public System.Collections.Generic.IDictionary {{ AdditionalPropertiesPropertyName }} { get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } {{PropertySetterAccessModifier}}set { _additionalProperties = value; }