From 530ce7929bc132bc3c795b4d7529befc9a57e8c4 Mon Sep 17 00:00:00 2001 From: mwallace Date: Tue, 17 Aug 2021 10:51:19 -0400 Subject: [PATCH 1/3] Added additional options for property naming. --- .../CSharpGeneratorSettings.cs | 7 +- .../CSharpNamingStyle.cs | 32 +++++ .../CSharpPropertyNameGenerator.cs | 57 +++++--- .../ConversionUtilitiesTests.cs | 122 +++++++++++++++++ src/NJsonSchema/ConversionUtilities.cs | 128 ++++++++++++++++++ 5 files changed, 327 insertions(+), 19 deletions(-) create mode 100644 src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyle.cs create mode 100644 src/NJsonSchema.Tests/ConversionUtilities/ConversionUtilitiesTests.cs diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs index a1f7e2e66..6db15605f 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs @@ -43,7 +43,7 @@ public CSharpGeneratorSettings() EnforceFlagEnums = false; ValueGenerator = new CSharpValueGenerator(this); - PropertyNameGenerator = new CSharpPropertyNameGenerator(); + PropertyNameGenerator = new CSharpPropertyNameGenerator(this); TemplateFactory = new DefaultTemplateFactory(this, new Assembly[] { typeof(CSharpGeneratorSettings).GetTypeInfo().Assembly @@ -52,6 +52,8 @@ public CSharpGeneratorSettings() InlineNamedArrays = false; InlineNamedDictionaries = false; InlineNamedTuples = true; + + PropertyNamingStyle = CSharpNamingStyle.PascalSnakeCase; } /// Gets or sets the .NET namespace of the generated types (default: MyNamespace). @@ -144,5 +146,8 @@ public CSharpGeneratorSettings() /// Gets or sets a value indicating whether to generate Nullable Reference Type annotations (default: false). public bool GenerateNullableReferenceTypes { get; set; } + + /// Gets or sets a value indicating what type of naming style to use (default: PascalSnakeCase). + public CSharpNamingStyle PropertyNamingStyle { get; set; } } } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyle.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyle.cs new file mode 100644 index 000000000..79aa0754c --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyle.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Rico Suter. All rights reserved. +// +// https://github.com/RicoSuter/NJsonSchema/blob/master/LICENSE.md +// Rico Suter, mail@rsuter.com +//----------------------------------------------------------------------- + +namespace NJsonSchema.CodeGeneration.CSharp +{ + /// The CSharp naming styles. + public enum CSharpNamingStyle + { + /// Generate Names with flat case (twowords). + FlatCase, + + /// Generate Names with upper flat case (TWOWORDS). + UpperFlatCase, + + /// Generate Names with camel case (twoWords). + CamelCase, + + /// Generate Names with pascal case (TwoWords). + PascalCase, + + /// Generate Names with snake case (two_words). + SnakeCase, + + /// Generate Names with pascal snake case (Two_Words). + PascalSnakeCase + } +} diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs index cfaaa37db..5809a2789 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs @@ -6,32 +6,53 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System; + namespace NJsonSchema.CodeGeneration.CSharp { /// Generates the property name for a given CSharp . public class CSharpPropertyNameGenerator : IPropertyNameGenerator { + private readonly Func _propertyNameGenerator; + + /// Initializes a new instance of the class. + /// The settings. + public CSharpPropertyNameGenerator(CSharpGeneratorSettings settings) + { + _propertyNameGenerator = GetPropertyNameGenerator(settings); + } + + private static Func GetPropertyNameGenerator(CSharpGeneratorSettings settings) + { + switch (settings.PropertyNamingStyle) + { + case CSharpNamingStyle.FlatCase: + return ConversionUtilities.ConvertNameToFlatCase; + + case CSharpNamingStyle.UpperFlatCase: + return ConversionUtilities.ConvertNameToUpperFlatCase; + + case CSharpNamingStyle.CamelCase: + return ConversionUtilities.ConvertNameToCamelCase; + + case CSharpNamingStyle.PascalCase: + return ConversionUtilities.ConvertNameToPascalCase; + + case CSharpNamingStyle.SnakeCase: + return ConversionUtilities.ConvertNameToSnakeCase; + + case CSharpNamingStyle.PascalSnakeCase: + return ConversionUtilities.ConvertNameToPascalSnakeCase; + + default: + throw new NotImplementedException(); + } + } + /// Generates the property name. /// The property. /// The new name. public virtual string Generate(JsonSchemaProperty property) - { - return ConversionUtilities.ConvertToUpperCamelCase(property.Name - .Replace("\"", string.Empty) - .Replace("@", string.Empty) - .Replace("?", string.Empty) - .Replace("$", string.Empty) - .Replace("[", string.Empty) - .Replace("]", string.Empty) - .Replace("(", "_") - .Replace(")", string.Empty) - .Replace(".", "-") - .Replace("=", "-") - .Replace("+", "plus"), true) - .Replace("*", "Star") - .Replace(":", "_") - .Replace("-", "_") - .Replace("#", "_"); - } + => _propertyNameGenerator(property.Name); } } diff --git a/src/NJsonSchema.Tests/ConversionUtilities/ConversionUtilitiesTests.cs b/src/NJsonSchema.Tests/ConversionUtilities/ConversionUtilitiesTests.cs new file mode 100644 index 000000000..ddef308ca --- /dev/null +++ b/src/NJsonSchema.Tests/ConversionUtilities/ConversionUtilitiesTests.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; + +namespace NJsonSchema.Tests.ConversionUtilities +{ + public class ConversionUtilitiesTests + { + [Theory] + [MemberData(nameof(GetTestCaseData))] + public async Task Flat_Case_Data_Should_Match(TestCaseData data) + { + //// Act + var actual = NJsonSchema.ConversionUtilities.ConvertNameToFlatCase(data.Input); + + //// Assert + Assert.Equal(data.FlatCase, actual); + } + + [Theory] + [MemberData(nameof(GetTestCaseData))] + public async Task Upper_Flat_Case_Data_Should_Match(TestCaseData data) + { + //// Act + var actual = NJsonSchema.ConversionUtilities.ConvertNameToUpperFlatCase(data.Input); + + //// Assert + Assert.Equal(data.UpperFlatCase, actual); + } + + [Theory] + [MemberData(nameof(GetTestCaseData))] + public async Task Camel_Case_Data_Should_Match(TestCaseData data) + { + //// Act + var actual = NJsonSchema.ConversionUtilities.ConvertNameToCamelCase(data.Input); + + //// Assert + Assert.Equal(data.CamelCase, actual); + } + + [Theory] + [MemberData(nameof(GetTestCaseData))] + public async Task Pascal_Case_Data_Should_Match(TestCaseData data) + { + //// Act + var actual = NJsonSchema.ConversionUtilities.ConvertNameToPascalCase(data.Input); + + //// Assert + Assert.Equal(data.PascalCase, actual); + } + + [Theory] + [MemberData(nameof(GetTestCaseData))] + public async Task Snake_Case_Data_Should_Match(TestCaseData data) + { + //// Act + var actual = NJsonSchema.ConversionUtilities.ConvertNameToSnakeCase(data.Input); + + //// Assert + Assert.Equal(data.SnakeCase, actual); + } + + [Theory] + [MemberData(nameof(GetTestCaseData))] + public async Task Pascal_Snake_Case_Data_Should_Match(TestCaseData data) + { + //// Act + var actual = NJsonSchema.ConversionUtilities.ConvertNameToPascalSnakeCase(data.Input); + + //// Assert + Assert.Equal(data.PascalSnakeCase, actual); + } + + public static IEnumerable GetTestCaseData() + { + yield return new[] { new TestCaseData("TwoWords", "twowords", "TWOWORDS", "twoWords", "TwoWords", "two_words", "Two_Words") }; + + yield return new[] { new TestCaseData("Two_Words", "twowords", "TWOWORDS", "twoWords", "TwoWords", "two_words", "Two_Words") }; + + yield return new[] { new TestCaseData("twoWords", "twowords", "TWOWORDS", "twoWords", "TwoWords", "two_words", "Two_Words") }; + + yield return new[] { new TestCaseData("two_words", "twowords", "TWOWORDS", "twoWords", "TwoWords", "two_words", "Two_Words") }; + + yield return new[] { new TestCaseData("two_words", "twowords", "TWOWORDS", "twoWords", "TwoWords", "two_words", "Two_Words") }; + + yield return new[] { new TestCaseData("class", "@class", "CLASS", "@class", "Class", "@class", "Class") }; + + yield return new[] { new TestCaseData("%yield", "yield", "YIELD", "yield", "Yield", "yield", "Yield") }; + + yield return new[] { new TestCaseData("2+", "_2plus", "_2PLUS", "_2plus", "_2plus", "_2plus", "_2plus") }; + } + + public class TestCaseData + { + public string Input { get; private set; } + + public string FlatCase { get; private set; } + + public string UpperFlatCase { get; private set; } + + public string CamelCase { get; private set; } + + public string PascalCase { get; private set; } + + public string SnakeCase { get; private set; } + + public string PascalSnakeCase { get; private set; } + + public TestCaseData(string input, string flatCase, string upperFlatCase, string camelCase, string pascalCase, string snakeCase, string pascalSnakeCase) + { + Input = input; + FlatCase = flatCase; + UpperFlatCase = upperFlatCase; + CamelCase = camelCase; + PascalCase = pascalCase; + SnakeCase = snakeCase; + PascalSnakeCase = pascalSnakeCase; + } + } + } +} diff --git a/src/NJsonSchema/ConversionUtilities.cs b/src/NJsonSchema/ConversionUtilities.cs index 48fa4bc6c..0bf937305 100644 --- a/src/NJsonSchema/ConversionUtilities.cs +++ b/src/NJsonSchema/ConversionUtilities.cs @@ -6,6 +6,8 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -179,6 +181,132 @@ public static string ConvertCSharpDocs(string input, int tabCount) return Regex.Replace(xml, @"^( *)/// ", m => m.Groups[1] + "///
", RegexOptions.Multiline); } + private static Dictionary _wordReplacements = new Dictionary + { + { '+', "plus" }, + { '*', "star" } + }; + + private static HashSet _reservedKeywords = new HashSet + { + "abstract", "as", "base", "bool", "break", + "byte", "case", "catch", "char", "checked", + "class", "const", "continue", "decimal", "default", + "delegate", "do", "double", "else", "enum", + "event", "explicit", "extern", "false", "finally", + "fixed", "float", "for", "foreach","goto", + "if", "implicit", "in", "int", "interface", + "internal", "is", "lock", "long", "namespace", + "new", "null", "object", "operator", "out", + "override", "params", "private", "protected", "public", + "readonly", "ref", "return", "sbyte", + "sealed", "short", "sizeof", "stackalloc", "static", "string", + "struct", "switch", "this", "throw", "true", + "try", "typeof", "uint", "ulong", "unchecked", + "unsafe", "ushort", "using", "virtual", "void", + "volatile", "while" + }; + + private static IEnumerable SplitWords(string input) + { + var sb = new StringBuilder(); + + foreach (var c in input) + { + var isLetterOrDigit = char.IsLetterOrDigit(c); + var isUpper = char.IsUpper(c); + var isReplaceable = _wordReplacements.ContainsKey(c); + + if (!isLetterOrDigit || isUpper || isReplaceable) + { + if (isReplaceable) + sb.Append(_wordReplacements[c]); + + if (sb.Length > 0) + { + yield return sb.ToString(); + sb.Clear(); + } + + if (!isUpper) + continue; + } + + sb.Append(c); + } + + if (sb.Length > 0) + yield return sb.ToString(); + } + + private static string JoinWords(IEnumerable inputs, string join, bool pascal) + { + var adjustedInputs = inputs.Select(x => x.ToLower()) + .Select((x, i) => pascal || i > 0 ? char.ToUpper(x[0]) + x.Substring(1) : x); + + // Join our Inputs by the join identifier + var output = string.Join(join, adjustedInputs); + + // If our first character is a number, then we need to prepend it with an underscore + if (char.IsNumber(output[0])) + output = $"_{output}"; + + return output; + } + + private static string SplitAndJoin(string input, string join, bool pascal, Func postprocess = null) + { + var split = SplitWords(input); + + var joined = JoinWords(split, join, pascal); + + var output = postprocess != null + ? postprocess(joined) + : joined; + + // If we got a C# reserved keyword, then we need to prepend it with an amperstand + if (_reservedKeywords.Contains(output)) + output = $"@{output}"; + + return output; + } + + /// Converts the given input to flat case (twowords). + /// The input. + /// The output. + public static string ConvertNameToFlatCase(string input) + => SplitAndJoin(input, string.Empty, false, postprocess: (x) => x.ToLower()); + + /// Converts the given input to upper flat case (TWOWORDS). + /// The input. + /// The output. + public static string ConvertNameToUpperFlatCase(string input) + => SplitAndJoin(input, string.Empty, false, postprocess: (x) => x.ToUpper()); + + /// Converts the given input to camel case (twoWords). + /// The input. + /// The output. + public static string ConvertNameToCamelCase(string input) + => SplitAndJoin(input, string.Empty, false); + + /// Converts the given input to pascal case (TwoWords). + /// The input. + /// The output. + public static string ConvertNameToPascalCase(string input) + => SplitAndJoin(input, string.Empty, true); + + /// Converts the given input to snake case (two_words). + /// The input. + /// The output. + public static string ConvertNameToSnakeCase(string input) + => SplitAndJoin(input, "_", false, postprocess: (x) => x.ToLower()); + + /// Converts the given input to pascal snake case (Two_Words). + /// The input. + /// The output. + public static string ConvertNameToPascalSnakeCase(string input) + => SplitAndJoin(input, "_", true); + private static string ConvertDashesToCamelCase(string input) { var sb = new StringBuilder(); From 0096109af97524e0ce9afd462c16919c294437dd Mon Sep 17 00:00:00 2001 From: mwallace Date: Tue, 17 Aug 2021 13:47:52 -0400 Subject: [PATCH 2/3] Fix for broken unit tests. --- .../AllOfTests.cs | 2 +- .../ArrayTests.cs | 3 +- .../DefaultPropertyTests.cs | 11 +++-- .../DictionaryTests.cs | 5 +- .../EnumTests.cs | 5 +- .../GeneralGeneratorTests.cs | 49 +++++++++++++------ .../InheritanceTests.cs | 5 +- .../InterfaceTests.cs | 6 ++- .../UriTests.cs | 5 +- .../ValueGeneratorTests.cs | 6 ++- .../CSharpGeneratorSettings.cs | 4 +- .../CSharpPropertyNameGenerator.cs | 6 +-- src/NJsonSchema/ConversionUtilities.cs | 3 +- 13 files changed, 74 insertions(+), 36 deletions(-) diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/AllOfTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/AllOfTests.cs index fe37d09de..78b9c134c 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/AllOfTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/AllOfTests.cs @@ -151,7 +151,7 @@ public async Task When_all_of_has_multiple_refs_then_the_properties_should_expan //// Act var schema = await JsonSchema.FromJsonAsync(json); - var settings = new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, Namespace = "ns" }; + var settings = new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, Namespace = "ns", PropertyNamingStyle = CSharpNamingStyle.PascalCase }; var generator = new CSharpGenerator(schema, settings); var output = generator.GenerateFile("Foo"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs index 29ee776ee..b102aa792 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs @@ -26,7 +26,8 @@ public async Task When_array_property_is_required_then_array_instance_can_be_cha { ClassStyle = CSharpClassStyle.Poco, ArrayType = "Foo", - ArrayInstanceType = "Bar" + ArrayInstanceType = "Bar", + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/DefaultPropertyTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/DefaultPropertyTests.cs index 3368ed239..26b2f91d5 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/DefaultPropertyTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/DefaultPropertyTests.cs @@ -21,7 +21,8 @@ public async Task When_property_has_interger_default_it_is_reflected_in_the_poco { ClassStyle = CSharpClassStyle.Poco, Namespace = "ns", - GenerateDefaultValues = true + GenerateDefaultValues = true, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }; var gen = new CSharpGenerator(schema, settings); var output = gen.GenerateFile("MyClass"); @@ -44,7 +45,8 @@ public async Task When_property_has_boolean_default_it_is_reflected_in_the_poco( { ClassStyle = CSharpClassStyle.Poco, Namespace = "ns", - GenerateDefaultValues = true + GenerateDefaultValues = true, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }; var gen = new CSharpGenerator(schema, settings); var output = gen.GenerateFile("MyClass"); @@ -67,7 +69,8 @@ public async Task When_property_has_boolean_default_and_default_value_generation { ClassStyle = CSharpClassStyle.Poco, Namespace = "ns", - GenerateDefaultValues = false + GenerateDefaultValues = false, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }; var gen = new CSharpGenerator(schema, settings); var output = gen.GenerateFile("MyClass"); @@ -93,6 +96,7 @@ public async Task When_generating_CSharp_code_then_default_value_generates_expec //// Act var settings = new CSharpGeneratorSettings(); settings.GenerateDefaultValues = true; + settings.PropertyNamingStyle = CSharpNamingStyle.PascalCase; var generator = new CSharpGenerator(document, settings); var code = generator.GenerateFile(); @@ -119,6 +123,7 @@ public async Task When_generating_CSharp_code_then_default_value_with_decimal_ge //// Act var settings = new CSharpGeneratorSettings(); settings.GenerateDefaultValues = true; + settings.PropertyNamingStyle = CSharpNamingStyle.PascalCase; var generator = new CSharpGenerator(document, settings); var code = generator.GenerateFile(); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/DictionaryTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/DictionaryTests.cs index d375ceb31..0d963d2ac 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/DictionaryTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/DictionaryTests.cs @@ -29,7 +29,7 @@ public async Task When_dictionary_key_is_enum_then_csharp_has_enum_key() var data = schema.ToJson(); //// Act - var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings()); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); //// Assert @@ -49,7 +49,8 @@ public async Task When_dictionary_property_is_required_then_dictionary_instance_ { ClassStyle = CSharpClassStyle.Poco, DictionaryType = "Foo", - DictionaryInstanceType = "Bar" + DictionaryInstanceType = "Bar", + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs index 2e1a62088..ddf48392e 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs @@ -219,6 +219,7 @@ public async Task When_type_name_hint_has_generics_then_they_are_converted() var schema = await JsonSchema.FromJsonAsync(json); var settings = new CSharpGeneratorSettings(); + settings.PropertyNamingStyle = CSharpNamingStyle.PascalCase; var generator = new CSharpGenerator(schema, settings); var code = generator.GenerateFile("Foo"); @@ -485,7 +486,7 @@ public async Task When_enum_is_nullable_not_required_it_should_be_nullable_with_ //// Act var schema = await JsonSchema.FromJsonAsync(json); - var settings = new CSharpGeneratorSettings {EnforceFlagEnums = true}; + var settings = new CSharpGeneratorSettings { EnforceFlagEnums = true, PropertyNamingStyle = CSharpNamingStyle.PascalCase }; var generator = new CSharpGenerator(schema, settings); var code = generator.GenerateFile("Foo"); @@ -523,7 +524,7 @@ public async Task When_enum_is_nullable_required_it_should_be_nullable_with_conv //// Act var schema = await JsonSchema.FromJsonAsync(json); - var settings = new CSharpGeneratorSettings { EnforceFlagEnums = true }; + var settings = new CSharpGeneratorSettings { EnforceFlagEnums = true, PropertyNamingStyle = CSharpNamingStyle.PascalCase }; var generator = new CSharpGenerator(schema, settings); var code = generator.GenerateFile("Foo"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs index a5f9dbfe9..f41507e5b 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs @@ -33,7 +33,7 @@ public async Task When_type_is_array_and_items_and_item_is_not_defined_then_any_ var schema = await JsonSchema.FromJsonAsync(json); //// Act - var settings = new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, Namespace = "ns", }; + var settings = new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, Namespace = "ns", PropertyNamingStyle = CSharpNamingStyle.PascalCase }; var generator = new CSharpGenerator(schema, settings); var output = generator.GenerateFile("MyClass"); @@ -232,7 +232,10 @@ public async Task When_property_is_timespan_than_csharp_timespan_is_used() //// Arrange var schema = JsonSchema.FromType(); var data = schema.ToJson(); - var generator = new CSharpGenerator(schema); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + PropertyNamingStyle = CSharpNamingStyle.PascalCase + }); //// Act var output = generator.GenerateFile("MyClass"); @@ -357,7 +360,10 @@ public void When_name_contains_dash_then_it_is_converted_to_upper_case() Type = JsonObjectType.String }; - var generator = new CSharpGenerator(schema); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + PropertyNamingStyle = CSharpNamingStyle.PascalCase + }); // Act var output = generator.GenerateFile("MyClass"); @@ -370,14 +376,14 @@ public void When_name_contains_dash_then_it_is_converted_to_upper_case() } [Theory] - [InlineData("foo@bar", "Foobar")] - [InlineData("foo$bar", "Foobar")] + [InlineData("foo@bar", "FooBar")] + [InlineData("foo$bar", "FooBar")] [InlineData("foobars[]", "Foobars")] [InlineData("foo.bar", "FooBar")] [InlineData("foo=bar", "FooBar")] - [InlineData("foo+bar", "Fooplusbar")] - [InlineData("foo*bar", "FooStarbar")] - [InlineData("foo:bar", "Foo_bar")] + [InlineData("foo+bar", "FooplusBar")] + [InlineData("foo*bar", "FoostarBar")] + [InlineData("foo:bar", "Foo_Bar")] public void When_name_contains_unallowed_characters_then_they_are_converted_to_valid_csharp(string jsonPropertyName, string expectedCSharpName) { // Arrange @@ -387,7 +393,10 @@ public void When_name_contains_unallowed_characters_then_they_are_converted_to_v Type = JsonObjectType.String }; - var generator = new CSharpGenerator(schema); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + PropertyNamingStyle = CSharpNamingStyle.PascalCase + }); // Act var output = generator.GenerateFile("MyClass"); @@ -421,6 +430,7 @@ private static async Task CreateGeneratorAsync() var schemaData = schema.ToJson(); var settings = new CSharpGeneratorSettings(); settings.Namespace = "MyNamespace"; + settings.PropertyNamingStyle = CSharpNamingStyle.PascalCase; var generator = new CSharpGenerator(schema, settings); return generator; } @@ -489,7 +499,8 @@ public async Task When_enum_property_has_default_and_int_serialization_then_corr var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, - Namespace = "Foo" + Namespace = "Foo", + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); @@ -510,7 +521,8 @@ public async Task When_enum_property_has_default_and_string_serialization_then_c var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, - Namespace = "Foo" + Namespace = "Foo", + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); @@ -552,7 +564,8 @@ public async Task When_enum_type_name_is_missing_then_default_value_is_still_cor var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, - Namespace = "Foo" + Namespace = "Foo", + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); @@ -662,7 +675,10 @@ public async Task When_property_is_required_then_CSharp_code_is_correct() var schemaJson = schema.ToJson(); //// Act - var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco }); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { + ClassStyle = CSharpClassStyle.Poco, + PropertyNamingStyle = CSharpNamingStyle.PascalCase + }); var code = generator.GenerateFile("MyClass"); //// Assert @@ -961,6 +977,7 @@ public async Task When_property_has_not_supported_characters_then_they_are_remov var schema = await JsonSchema.FromJsonAsync(schemaJson); var settings = new CSharpGeneratorSettings(); + settings.PropertyNamingStyle = CSharpNamingStyle.PascalCase; var generator = new CSharpGenerator(schema, settings); //// Act @@ -1654,7 +1671,8 @@ public async Task When_class_is_abstract_constructor_is_protected_for_record() var data = schema.ToJson(); var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { - ClassStyle = CSharpClassStyle.Record + ClassStyle = CSharpClassStyle.Record, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); //// Act @@ -1681,7 +1699,8 @@ public async Task When_record_has_inheritance() var data = schema.ToJson(); var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { - ClassStyle = CSharpClassStyle.Record + ClassStyle = CSharpClassStyle.Record, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); //// Act diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs index 066cdc0c7..397f4281c 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs @@ -32,7 +32,10 @@ public async Task When_empty_class_inherits_from_dictionary_then_allOf_inheritan var schema = JsonSchema.FromType(); var data = schema.ToJson(); - var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings()); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + PropertyNamingStyle = CSharpNamingStyle.PascalCase + }); //// Act var code = generator.GenerateFile(); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/InterfaceTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/InterfaceTests.cs index 9210d1371..9fd093353 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/InterfaceTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/InterfaceTests.cs @@ -28,7 +28,8 @@ public async Task When_interface_has_properties_then_properties_are_included_in_ var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, - SchemaType = SchemaType.Swagger2 + SchemaType = SchemaType.Swagger2, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("Person"); @@ -48,7 +49,8 @@ public async Task When_class_implements_interface_then_properties_are_included_i var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, - SchemaType = SchemaType.Swagger2 + SchemaType = SchemaType.Swagger2, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("Person"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/UriTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/UriTests.cs index 316fd1646..a95e5a9f1 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/UriTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/UriTests.cs @@ -18,7 +18,10 @@ public async Task When_property_is_uri_then_csharp_output_is_also_uri() //// Arrange var schema = JsonSchema.FromType(); var json = schema.ToJson(); - var generator = new CSharpGenerator(schema); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + PropertyNamingStyle = CSharpNamingStyle.PascalCase + }); //// Act var code = generator.GenerateFile("MyClass"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/ValueGeneratorTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ValueGeneratorTests.cs index 06c0278af..6c10270fe 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/ValueGeneratorTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ValueGeneratorTests.cs @@ -57,7 +57,8 @@ public async Task When_property_is_integer_and_no_format_is_available_then_defau var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco, - SchemaType = SchemaType.Swagger2 + SchemaType = SchemaType.Swagger2, + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); @@ -86,7 +87,8 @@ public async Task When_property_is_string_and_format_is_date_time_then_assign_de { ClassStyle = CSharpClassStyle.Poco, SchemaType = SchemaType.Swagger2, - DateTimeType = "System.DateTime" + DateTimeType = "System.DateTime", + PropertyNamingStyle = CSharpNamingStyle.PascalCase }); var code = generator.GenerateFile("MyClass"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs index 6db15605f..8582018e0 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs @@ -42,6 +42,8 @@ public CSharpGeneratorSettings() GenerateJsonMethods = false; EnforceFlagEnums = false; + PropertyNamingStyle = CSharpNamingStyle.PascalSnakeCase; + ValueGenerator = new CSharpValueGenerator(this); PropertyNameGenerator = new CSharpPropertyNameGenerator(this); TemplateFactory = new DefaultTemplateFactory(this, new Assembly[] @@ -52,8 +54,6 @@ public CSharpGeneratorSettings() InlineNamedArrays = false; InlineNamedDictionaries = false; InlineNamedTuples = true; - - PropertyNamingStyle = CSharpNamingStyle.PascalSnakeCase; } /// Gets or sets the .NET namespace of the generated types (default: MyNamespace). diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs index 5809a2789..9dc65d6b2 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs @@ -13,13 +13,13 @@ namespace NJsonSchema.CodeGeneration.CSharp /// Generates the property name for a given CSharp . public class CSharpPropertyNameGenerator : IPropertyNameGenerator { - private readonly Func _propertyNameGenerator; + private readonly CSharpGeneratorSettings _settings; /// Initializes a new instance of the class. /// The settings. public CSharpPropertyNameGenerator(CSharpGeneratorSettings settings) { - _propertyNameGenerator = GetPropertyNameGenerator(settings); + _settings = settings; } private static Func GetPropertyNameGenerator(CSharpGeneratorSettings settings) @@ -53,6 +53,6 @@ private static Func GetPropertyNameGenerator(CSharpGeneratorSett /// The property. /// The new name. public virtual string Generate(JsonSchemaProperty property) - => _propertyNameGenerator(property.Name); + => GetPropertyNameGenerator(_settings)(property.Name); } } diff --git a/src/NJsonSchema/ConversionUtilities.cs b/src/NJsonSchema/ConversionUtilities.cs index 0bf937305..5a1de6662 100644 --- a/src/NJsonSchema/ConversionUtilities.cs +++ b/src/NJsonSchema/ConversionUtilities.cs @@ -184,7 +184,8 @@ public static string ConvertCSharpDocs(string input, int tabCount) private static Dictionary _wordReplacements = new Dictionary { { '+', "plus" }, - { '*', "star" } + { '*', "star" }, + { ':', "_" } }; private static HashSet _reservedKeywords = new HashSet From c12814ca3a9389c0a0e64573211ee158b1891936 Mon Sep 17 00:00:00 2001 From: mwallace Date: Wed, 18 Aug 2021 11:19:34 -0400 Subject: [PATCH 3/3] Added options to change Class Naming style as well. --- .../CSharpGeneratorSettings.cs | 6 +++- .../CSharpNamingStyleExtensions.cs | 29 +++++++++++++++++++ .../CSharpPropertyNameGenerator.cs | 29 +------------------ .../Models/ClassTemplateModel.cs | 2 +- 4 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyleExtensions.cs diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs index 8582018e0..556a84673 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs @@ -43,6 +43,7 @@ public CSharpGeneratorSettings() EnforceFlagEnums = false; PropertyNamingStyle = CSharpNamingStyle.PascalSnakeCase; + ClassNamingStyle = CSharpNamingStyle.PascalCase; ValueGenerator = new CSharpValueGenerator(this); PropertyNameGenerator = new CSharpPropertyNameGenerator(this); @@ -147,7 +148,10 @@ public CSharpGeneratorSettings() /// Gets or sets a value indicating whether to generate Nullable Reference Type annotations (default: false). public bool GenerateNullableReferenceTypes { get; set; } - /// Gets or sets a value indicating what type of naming style to use (default: PascalSnakeCase). + /// Gets or sets a value indicating what type of naming style to use for properties (default: PascalSnakeCase). public CSharpNamingStyle PropertyNamingStyle { get; set; } + + /// Gets or sets a value indicating what type of naming style to use for classes (default: PascalCase). + public CSharpNamingStyle ClassNamingStyle { get; set; } } } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyleExtensions.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyleExtensions.cs new file mode 100644 index 000000000..e29e3f097 --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpNamingStyleExtensions.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Rico Suter. All rights reserved. +// +// https://github.com/RicoSuter/NJsonSchema/blob/master/LICENSE.md +// Rico Suter, mail@rsuter.com +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; + +namespace NJsonSchema.CodeGeneration.CSharp +{ + internal static class CSharpNamingStyleExtensions + { + private static Dictionary> _mapping = new Dictionary> + { + { CSharpNamingStyle.FlatCase, ConversionUtilities.ConvertNameToFlatCase }, + { CSharpNamingStyle.UpperFlatCase, ConversionUtilities.ConvertNameToUpperFlatCase }, + { CSharpNamingStyle.CamelCase, ConversionUtilities.ConvertNameToCamelCase }, + { CSharpNamingStyle.PascalCase, ConversionUtilities.ConvertNameToPascalCase }, + { CSharpNamingStyle.SnakeCase, ConversionUtilities.ConvertNameToSnakeCase }, + { CSharpNamingStyle.PascalSnakeCase, ConversionUtilities.ConvertNameToPascalSnakeCase }, + }; + + public static string RunConversion(this CSharpNamingStyle namingStyle, string value) + => _mapping[namingStyle](value); + } +} diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs index 9dc65d6b2..ffed08a2c 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs @@ -22,37 +22,10 @@ public CSharpPropertyNameGenerator(CSharpGeneratorSettings settings) _settings = settings; } - private static Func GetPropertyNameGenerator(CSharpGeneratorSettings settings) - { - switch (settings.PropertyNamingStyle) - { - case CSharpNamingStyle.FlatCase: - return ConversionUtilities.ConvertNameToFlatCase; - - case CSharpNamingStyle.UpperFlatCase: - return ConversionUtilities.ConvertNameToUpperFlatCase; - - case CSharpNamingStyle.CamelCase: - return ConversionUtilities.ConvertNameToCamelCase; - - case CSharpNamingStyle.PascalCase: - return ConversionUtilities.ConvertNameToPascalCase; - - case CSharpNamingStyle.SnakeCase: - return ConversionUtilities.ConvertNameToSnakeCase; - - case CSharpNamingStyle.PascalSnakeCase: - return ConversionUtilities.ConvertNameToPascalSnakeCase; - - default: - throw new NotImplementedException(); - } - } - /// Generates the property name. /// The property. /// The new name. public virtual string Generate(JsonSchemaProperty property) - => GetPropertyNameGenerator(_settings)(property.Name); + => _settings.PropertyNamingStyle.RunConversion(property.Name); } } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs index 706f466e6..c39ad46ed 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs @@ -33,7 +33,7 @@ public ClassTemplateModel(string typeName, CSharpGeneratorSettings settings, _schema = schema; _settings = settings; - ClassName = typeName; + ClassName = settings.ClassNamingStyle.RunConversion(typeName); Properties = _schema.ActualProperties.Values .Where(p => !p.IsInheritanceDiscriminator) .Select(property => new PropertyModel(this, property, _resolver, _settings))