Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsorted constructor parameters #1399

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -14,14 +14,14 @@ public abstract class AbstractClass
[Fact]
public async Task When_class_is_abstract_then_is_abstract_CSharp_keyword_is_generated()
{
/// Arrange
//// Arrange
var schema = JsonSchema.FromType<AbstractClass>();

/// Act
//// Act
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings());
var code = generator.GenerateFile("AbstractClass");

/// Assert
//// Assert
Assert.Contains("public abstract partial class AbstractClass", code);
}
}
Expand Down
20 changes: 10 additions & 10 deletions src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs
Expand Up @@ -190,7 +190,7 @@ public async Task When_class_has_enum_array_property_then_enum_name_is_preserved
[Fact]
public async Task When_type_name_hint_has_generics_then_they_are_converted()
{
/// Arrange
//// Arrange
var json = @"
{
""properties"": {
Expand All @@ -215,15 +215,15 @@ public async Task When_type_name_hint_has_generics_then_they_are_converted()
}
}
}";
/// Act
//// Act
var schema = await JsonSchema.FromJsonAsync(json);

var settings = new CSharpGeneratorSettings();
var generator = new CSharpGenerator(schema, settings);

var code = generator.GenerateFile("Foo");

/// Assert
//// Assert
Assert.Contains("public enum FirstMetdodOfMetValueGroupChar", code);
}

Expand Down Expand Up @@ -293,15 +293,15 @@ public async Task When_enum_contains_operator_convert_to_string_equivalent()
}
}";

/// Act
//// Act
var schema = await JsonSchema.FromJsonAsync(json);

var settings = new CSharpGeneratorSettings();
var generator = new CSharpGenerator(schema, settings);

var code = generator.GenerateFile("Foo");

/// Assert
//// Assert
Assert.DoesNotContain("__", code);
Assert.Contains("Eq = 0", code);

Expand Down Expand Up @@ -330,15 +330,15 @@ public async Task When_enum_starts_with_plus_or_minus_convert_to_string_equivale
}
}";

/// Act
//// Act
var schema = await JsonSchema.FromJsonAsync(json);

var settings = new CSharpGeneratorSettings();
var generator = new CSharpGenerator(schema, settings);

var code = generator.GenerateFile("Foo");

/// Assert
//// Assert
Assert.DoesNotContain("__", code);
Assert.Contains("MinusFoo = 0", code);
Assert.Contains("PlusFoo = 1", code);
Expand All @@ -348,7 +348,7 @@ public async Task When_enum_starts_with_plus_or_minus_convert_to_string_equivale
[Fact]
public async Task When_array_item_enum_is_not_referenced_then_type_name_hint_is_property_name()
{
/// Arrange
//// Arrange
var json = @"
{
""properties"": {
Expand Down Expand Up @@ -382,15 +382,15 @@ public async Task When_array_item_enum_is_not_referenced_then_type_name_hint_is_
}
}
}";
/// Act
//// Act
var schema = await JsonSchema.FromJsonAsync(json);

var settings = new CSharpGeneratorSettings();
var generator = new CSharpGenerator(schema, settings);

var code = generator.GenerateFile("Foo");

/// Assert
//// Assert
Assert.DoesNotContain("public enum Anonymous", code);
Assert.Contains("public enum Status", code);
}
Expand Down
Expand Up @@ -1620,11 +1620,68 @@ public async Task When_record_no_setter_in_class_and_constructor_provided()
Assert.Contains(@"public string Street { get; }", output);
Assert.DoesNotContain(@"public string Street { get; set; }", output);

Assert.Contains("public partial class Address", output);

Assert.Contains("public Address(string @city, string @street)", output);

AssertCompile(output);
}

#if NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER
[Fact]
public async Task When_csharp_record_no_setter_in_record_and_constructor_provided()
{
//// Arrange
var schema = JsonSchema.FromType<Address>();
var data = schema.ToJson();
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
ClassStyle = CSharpClassStyle.CSharpRecord,
SortConstructorParameters = false
});

//// Act
var output = generator.GenerateFile("Address");

//// Assert
Assert.Contains(@"public string Street { get; }", output);
Assert.DoesNotContain(@"public string Street { get; set; }", output);

Assert.Contains("public partial record Address", output);

Assert.Contains("public Address(string @street, string @city)", output);

AssertCompile(output, new CSharpParseOptions(LanguageVersion.CSharp9));
}

[Fact]
public async Task When_csharp_record_init_in_record_and_constructor_provided()
{
//// Arrange
var schema = JsonSchema.FromType<Address>();
var data = schema.ToJson();
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
ClassStyle = CSharpClassStyle.CSharpRecord,
SortConstructorParameters = false,
GenerateInitProperties = true,
});

//// Act
var output = generator.GenerateFile("Address");

//// Assert
Assert.Contains(@"public string Street { get; init; }", output);
Assert.DoesNotContain(@"public string Street { get; set; }", output);

Assert.Contains("public partial record Address", output);

Assert.Contains("public Address(string @street, string @city)", output);

AssertCompile(output, new CSharpParseOptions(LanguageVersion.CSharp9));
}
#endif

public abstract class AbstractAddress
{
[JsonProperty("city")]
Expand Down Expand Up @@ -1753,9 +1810,9 @@ public void When_schema_has_negative_value_of_enum_it_is_generated_in_CSharp_and
Assert.Contains("__1 = -1", types.First().Code);
}

private static void AssertCompile(string code)
private static void AssertCompile(string code, CSharpParseOptions options = null)
{
var syntaxTree = CSharpSyntaxTree.ParseText(code);
var syntaxTree = CSharpSyntaxTree.ParseText(code, options);
var errors = syntaxTree
.GetDiagnostics()
.Where(_ => _.Severity == DiagnosticSeverity.Error);
Expand Down
24 changes: 24 additions & 0 deletions src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs
Expand Up @@ -155,6 +155,30 @@ public async Task When_definitions_inherit_from_root_schema()
Assert.Contains("[JsonInheritanceAttribute(\"PersianCat\", typeof(PersianCat))]", code);
}

[Fact]
public async Task When_definition_inherits_parameters_from_base_come_first()
{
//// Arrange
var path = GetTestDirectory() + "/References/Car.json";

//// Act
var schema = await JsonSchema.FromFileAsync(path);
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
ClassStyle = CSharpClassStyle.Record,
SortConstructorParameters = false,
});

//// Act
var code = generator.GenerateFile();

//// Assert
Assert.Contains("public partial class Car : Vehicle", code);
Assert.Contains("Vehicle(string @id)", code);
Assert.Contains("Car(string @id, int @wheels)", code);
Assert.Contains("base(id)", code);
}

private string GetTestDirectory()
{
var codeBase = Assembly.GetExecutingAssembly().CodeBase;
Expand Down
Expand Up @@ -10,13 +10,21 @@
<None Remove="References\Animal.json" />
<None Remove="References\B.json" />
<None Remove="References\C.json" />
<None Remove="References\Car.json" />
<None Remove="References\D.json" />
<None Remove="References\E.json" />
<None Remove="References\Vehicle.json" />
</ItemGroup>
<ItemGroup>
<Content Include="References\A.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="References\Car.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="References\Vehicle.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="References\Animal.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Expand Down
16 changes: 16 additions & 0 deletions src/NJsonSchema.CodeGeneration.CSharp.Tests/References/Car.json
@@ -0,0 +1,16 @@
{
"title": "Car",
"type": "object",
"description": "This is a car",
"allOf": [
{
"$ref": "Vehicle.json"
}
],
"properties": {
"wheels": {
"type": "integer",
"description": "The number of wheels on the car"
}
}
}
@@ -0,0 +1,12 @@
{
"title": "Vehicle",
"type": "object",
"x-abstract": true,
"description": "This is a vehicle",
"properties": {
"id": {
"type": "string",
"description": "This is the ID of Vehicle"
}
}
}
Expand Up @@ -35,7 +35,7 @@ public async Task When_schema_contains_range_then_code_is_correctly_generated()
[Fact]
public async Task When_property_is_integer_and_no_format_is_available_then_default_value_is_int32()
{
/// Arrange
//// Arrange
var json = @"{
""type"": ""object"",
""properties"": {
Expand All @@ -53,22 +53,22 @@ public async Task When_property_is_integer_and_no_format_is_available_then_defau
}";
var schema = await JsonSchema.FromJsonAsync(json);

/// Act
//// Act
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
ClassStyle = CSharpClassStyle.Poco,
SchemaType = SchemaType.Swagger2
});
var code = generator.GenerateFile("MyClass");

/// Assert
//// Assert
Assert.Contains("public int? PageSize { get; set; } = 10;", code);
}

[Fact]
public async Task When_property_is_string_and_format_is_date_time_then_assign_default_value()
{
/// Arrange
//// Arrange
var json = @"{
""type"": ""object"",
""properties"": {
Expand All @@ -81,7 +81,7 @@ public async Task When_property_is_string_and_format_is_date_time_then_assign_de
}";
var schema = await JsonSchema.FromJsonAsync(json);

/// Act
//// Act
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
ClassStyle = CSharpClassStyle.Poco,
Expand All @@ -90,7 +90,7 @@ public async Task When_property_is_string_and_format_is_date_time_then_assign_de
});
var code = generator.GenerateFile("MyClass");

/// Assert
//// Assert
Assert.Contains("public System.DateTime? DateTime { get; set; } = System.DateTime.Parse(\"31.12.9999 23:59:59\");", code);
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/NJsonSchema.CodeGeneration.CSharp/CSharpClassStyle.cs
Expand Up @@ -23,6 +23,9 @@ public enum CSharpClassStyle
Prism,

/// <summary>Generates Records - read only POCOs (Plain Old C# Objects).</summary>
Record
Record,

/// <summary>Generates C# Records.</summary>
CSharpRecord
}
}
Expand Up @@ -52,6 +52,7 @@ public CSharpGeneratorSettings()
InlineNamedArrays = false;
InlineNamedDictionaries = false;
InlineNamedTuples = true;
SortConstructorParameters = true;
}

/// <summary>Gets or sets the .NET namespace of the generated types (default: MyNamespace).</summary>
Expand Down Expand Up @@ -144,5 +145,11 @@ public CSharpGeneratorSettings()

/// <summary>Gets or sets a value indicating whether to generate Nullable Reference Type annotations (default: false).</summary>
public bool GenerateNullableReferenceTypes { get; set; }

/// <summary>Gets a value indicating whether the constructor parameters should be sorted alphabetically (default: true).</summary>
public bool SortConstructorParameters { get; set; }

/// <summary>Gets a value indicating whether the properties should have an 'init' part. (default: false).</summary>
public bool GenerateInitProperties { get; set; }
}
}
16 changes: 14 additions & 2 deletions src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs
Expand Up @@ -42,7 +42,7 @@ public class ClassTemplateModel : ClassTemplateModelBase
if (schema.InheritedSchema != null)
{
BaseClass = new ClassTemplateModel(BaseClassName, settings, resolver, schema.InheritedSchema, rootObject);
AllProperties = Properties.Concat(BaseClass.AllProperties).ToArray();
AllProperties = BaseClass.AllProperties.Concat(Properties).ToArray();
}
else
{
Expand Down Expand Up @@ -100,7 +100,12 @@ public class ClassTemplateModel : ClassTemplateModelBase
public bool RenderPrism => _settings.ClassStyle == CSharpClassStyle.Prism;

/// <summary>Gets a value indicating whether the class style is Record.</summary>
public bool RenderRecord => _settings.ClassStyle == CSharpClassStyle.Record;
public bool RenderRecord => _settings.ClassStyle == CSharpClassStyle.Record ||
_settings.ClassStyle == CSharpClassStyle.CSharpRecord;

/// <summary>Gets the class type.</summary>
public string ClassType => _settings.ClassStyle == CSharpClassStyle.CSharpRecord ?
"record" : "class";

/// <summary>Gets a value indicating whether to render ToJson() and FromJson() methods.</summary>
public bool GenerateJsonMethods => _settings.GenerateJsonMethods;
Expand All @@ -122,6 +127,13 @@ public class ClassTemplateModel : ClassTemplateModelBase
/// <summary>Gets a value indicating whether the class has a parent class.</summary>
public bool HasInheritance => _schema.InheritedTypeSchema != null;

/// <summary>Gets a value indicating whether the constructor parameters should be sorted.</summary>
public bool SortConstructorParameters => _settings.SortConstructorParameters;

/// <summary>Gets a value indicating whether the properties should have an 'init' part. (default: false).</summary>
public bool GenerateInitProperties => _settings.ClassStyle == CSharpClassStyle.CSharpRecord &&
_settings.GenerateInitProperties;

/// <summary>Gets the base class name.</summary>
public string BaseClassName => HasInheritance ? _resolver.Resolve(_schema.InheritedTypeSchema, false, string.Empty, false)
.Replace(_settings.ArrayType + "<", _settings.ArrayBaseType + "<")
Expand Down