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

Add customizable number types in code generation #1650

Merged
merged 2 commits into from
Nov 28, 2023
Merged
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
263 changes: 263 additions & 0 deletions src/NJsonSchema.CodeGeneration.CSharp.Tests/NumberTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
using System.Threading.Tasks;
using NJsonSchema.CodeGeneration.CSharp;
using Xunit;

namespace NJsonSchema.CodeGeneration.Tests.CSharp;

public class NumberTests
{
[Fact]
public async Task When_number_has_no_format_then_default_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number""
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema);

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

//// Assert
Assert.Contains("public double Amount", code);
}

[Fact]
public async Task When_number_has_decimal_format_then_decimal_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number"",
""format"": ""decimal""
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema);

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

//// Assert
Assert.Contains("public decimal Amount", code);
}

[Fact]
public async Task When_number_has_double_format_then_double_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number"",
""format"": ""double""
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema);

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

//// Assert
Assert.Contains("public double Amount", code);
}

[Fact]
public async Task When_number_has_float_format_then_float_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number"",
""format"": ""float""
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema);

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

//// Assert
Assert.Contains("public float Amount", code);
}

[Fact]
public async Task When_number_type_setting_is_defined_then_setting_type_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number""
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
NumberType = "customNumberType"
});

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

//// Assert
Assert.Contains("public customNumberType Amount", code);
}

[Fact]
public async Task When_number_type_setting_is_whitespace_then_double_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number""
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
NumberType = " \t\n"
});

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

//// Assert
Assert.Contains("public double Amount", code);
}

[Fact]
public async Task When_number_type_setting_is_null_then_double_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number"",
""nullable"": true
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
NumberType = null!
});

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

//// Assert
Assert.Contains("public double? Amount", code);
}

[Fact]
public async Task When_number_float_type_setting_is_defined_then_setting_type_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number"",
""format"":""float"",
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
NumberFloatType = "customFloatType"
});

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

//// Assert
Assert.Contains("public customFloatType Amount", code);
}

[Fact]
public async Task When_number_double_type_setting_is_defined_then_setting_type_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number"",
""format"":""double"",
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
NumberDoubleType = "customDoubleType"
});

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

//// Assert
Assert.Contains("public customDoubleType Amount", code);
}

[Fact]
public async Task When_number_decimal_type_setting_is_defined_then_setting_type_is_generated()
{
//// Arrange
var json =
@"{
""type"": ""object"",
""properties"": {
""amount"" : {
""type"":""number"",
""format"":""decimal"",
}
}
}";
var schema = await JsonSchema.FromJsonAsync(json);
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings
{
NumberDecimalType = "customDecimalType"
});

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

//// Assert
Assert.Contains("public customDecimalType Amount", code);
}
}
23 changes: 20 additions & 3 deletions src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ public CSharpGeneratorSettings()
DateTimeType = "System.DateTimeOffset";
TimeType = "System.TimeSpan";
TimeSpanType = "System.TimeSpan";


NumberType = "double";
NumberFloatType = "float";
NumberDoubleType = "double";
NumberDecimalType = "decimal";

ArrayType = "System.Collections.Generic.ICollection";
ArrayInstanceType = "System.Collections.ObjectModel.Collection";
ArrayBaseType = "System.Collections.ObjectModel.Collection";
Expand Down Expand Up @@ -78,7 +83,19 @@ public CSharpGeneratorSettings()

/// <summary>Gets or sets the time span .NET type (default: 'TimeSpan').</summary>
public string TimeSpanType { get; set; }


/// <summary>Gets or sets the number .NET type (default: "double").</summary>
public string NumberType { get; set; }

/// <summary>Gets or sets the number with double format .NET type (default: "double").</summary>
public string NumberDoubleType { get; set; }

/// <summary>Gets or sets the number with float format .NET type (default: "float").</summary>
public string NumberFloatType { get; set; }

/// <summary>Gets or sets the number with decimal format .NET type (default: "decimal").</summary>
public string NumberDecimalType { get; set; }

/// <summary>Gets or sets the generic array .NET type (default: 'ICollection').</summary>
public string ArrayType { get; set; }

Expand All @@ -96,7 +113,7 @@ public CSharpGeneratorSettings()

/// <summary>Gets or sets the generic dictionary .NET type which is used as base class (default: 'Dictionary').</summary>
public string DictionaryBaseType { get; set; }

/// <summary>Gets or sets the CSharp class style (default: 'Poco').</summary>
public CSharpClassStyle ClassStyle { get; set; }

Expand Down
42 changes: 25 additions & 17 deletions src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// <author>Rico Suter, mail@rsuter.com</author>
//-----------------------------------------------------------------------

using NJsonSchema.Annotations;
using System;
using System.Linq;

Expand Down Expand Up @@ -91,9 +90,9 @@ public string Resolve(JsonSchema schema, bool isNullable, string? typeNameHint,
var type = schema.ActualTypeSchema.Type;
if (type == JsonObjectType.None && schema.ActualTypeSchema.IsEnumeration)
{
type = schema.ActualTypeSchema.Enumeration.All(v => v is int) ?
JsonObjectType.Integer :
JsonObjectType.String;
type = schema.ActualTypeSchema.Enumeration.All(v => v is int)
? JsonObjectType.Integer
: JsonObjectType.String;
}

if (type.IsNumber())
Expand Down Expand Up @@ -165,22 +164,30 @@ private string ResolveString(JsonSchema schema, bool isNullable, string? typeNam

if (schema.Format == JsonFormatStrings.Date)
{
return isNullable && Settings.DateType?.ToLowerInvariant() != "string" ? Settings.DateType + "?" : Settings.DateType + nullableReferenceType;
return isNullable && Settings.DateType?.ToLowerInvariant() != "string"
? Settings.DateType + "?"
: Settings.DateType + nullableReferenceType;
}

if (schema.Format == JsonFormatStrings.DateTime)
{
return isNullable && Settings.DateTimeType?.ToLowerInvariant() != "string" ? Settings.DateTimeType + "?" : Settings.DateTimeType + nullableReferenceType;
return isNullable && Settings.DateTimeType?.ToLowerInvariant() != "string"
? Settings.DateTimeType + "?"
: Settings.DateTimeType + nullableReferenceType;
}

if (schema.Format == JsonFormatStrings.Time)
{
return isNullable && Settings.TimeType?.ToLowerInvariant() != "string" ? Settings.TimeType + "?" : Settings.TimeType + nullableReferenceType;
return isNullable && Settings.TimeType?.ToLowerInvariant() != "string"
? Settings.TimeType + "?"
: Settings.TimeType + nullableReferenceType;
}

if (schema.Format is JsonFormatStrings.Duration or JsonFormatStrings.TimeSpan)
{
return isNullable && Settings.TimeSpanType?.ToLowerInvariant() != "string" ? Settings.TimeSpanType + "?" : Settings.TimeSpanType + nullableReferenceType;
return isNullable && Settings.TimeSpanType?.ToLowerInvariant() != "string"
? Settings.TimeSpanType + "?"
: Settings.TimeSpanType + nullableReferenceType;
}

if (schema.Format == JsonFormatStrings.Uri)
Expand Down Expand Up @@ -250,19 +257,20 @@ private string ResolveInteger(JsonSchema schema, bool isNullable, string? typeNa
return isNullable ? "int?" : "int";
}

private static string ResolveNumber(JsonSchema schema, bool isNullable)
private string ResolveNumber(JsonSchema schema, bool isNullable)
{
if (schema.Format == JsonFormatStrings.Decimal)
var numberType = schema.Format switch
{
return isNullable ? "decimal?" : "decimal";
}
JsonFormatStrings.Decimal => Settings.NumberDecimalType,
JsonFormatStrings.Double => Settings.NumberDoubleType,
JsonFormatStrings.Float => Settings.NumberFloatType,
_ => Settings.NumberType
};

if (schema.Format == JsonFormatStrings.Float)
{
return isNullable ? "float?" : "float";
}
if (string.IsNullOrWhiteSpace(numberType))
numberType = "double";

return isNullable ? "double?" : "double";
return isNullable ? numberType + "?" : numberType;
}

private string ResolveArrayOrTuple(JsonSchema schema)
Expand Down