Skip to content
This repository was archived by the owner on Jun 10, 2026. It is now read-only.
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Json.Schema.ToDotNet.Cli/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ public class Options
Required = false)]
public GenerateJsonIntegerOption GenerateJsonIntegerAs { get; set; }

[Option(
"generate-json-number-as",
HelpText = "Generate Json number as C# type: double|float|decimal.",
Default = GenerateJsonNumberOption.Double,
Required = false)]
public GenerateJsonNumberOption GenerateJsonNumberAs { get; set; }

[Option(
"seal-classes",
HelpText = "Seal generated classes.",
Expand Down
1 change: 1 addition & 0 deletions src/Json.Schema.ToDotNet.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ private static int Run(Options options)
GenerateComparers = options.GenerateComparers,
GenerateCloningCode = options.GenerateCloningCode,
GenerateJsonIntegerAs = options.GenerateJsonIntegerAs,
GenerateJsonNumberAs = options.GenerateJsonNumberAs,
SealClasses = options.SealClasses,
VirtualMembers = options.VirtualMembers,
ProtectedInitMethods = options.ProtectedInitMethods
Expand Down
64 changes: 64 additions & 0 deletions src/Json.Schema.ToDotNet.UnitTests/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ internal GenerateJsonIntegerAsTestCase(string inputGenerateJsonIntegerAs, Genera
}
}

private class GenerateJsonNumberAsTestCase
{
internal string InputGenerateJsonNumberAs { get; }
internal string ArgsString { get; }
internal GenerateJsonNumberOption? ExpectedGenerateJsonNumberAs { get; }
internal string ExpectedErrorParameter { get; }

internal GenerateJsonNumberAsTestCase(string inputGenerateJsonNumberAs, GenerateJsonNumberOption? expectedGenerateJsonNumberAs, string expectedErrorParameter)
{
InputGenerateJsonNumberAs = inputGenerateJsonNumberAs;
ArgsString = inputGenerateJsonNumberAs == null ? argsStringBase : argsStringBase + " --generate-json-number-as=" + inputGenerateJsonNumberAs;
ExpectedGenerateJsonNumberAs = expectedGenerateJsonNumberAs;
ExpectedErrorParameter = expectedErrorParameter;
}
}

[Fact]
public void CorrectlyParseGenerateJsonIntegerAs()
{
Expand Down Expand Up @@ -80,5 +96,53 @@ public void CorrectlyParseGenerateJsonIntegerAs()
builder.Length.Should().Be(0,
$"all test cases should pass, but the following test cases failed:\n{builder}");
}

[Fact]
public void CorrectlyParseGenerateJsonNumberAs()
{
var testCases = new List<GenerateJsonNumberAsTestCase>()
{
new GenerateJsonNumberAsTestCase(null, (GenerateJsonNumberOption?)GenerateJsonNumberOption.Double, null),
new GenerateJsonNumberAsTestCase("double", (GenerateJsonNumberOption?)GenerateJsonNumberOption.Double, null),
new GenerateJsonNumberAsTestCase("float", (GenerateJsonNumberOption?)GenerateJsonNumberOption.Float, null),
new GenerateJsonNumberAsTestCase("decimal", (GenerateJsonNumberOption?)GenerateJsonNumberOption.Decimal, null),
new GenerateJsonNumberAsTestCase("unknown", null, "generate-json-number-as"),
new GenerateJsonNumberAsTestCase("string", null, "generate-json-number-as")
};

var builder = new StringBuilder();

foreach (GenerateJsonNumberAsTestCase testCase in testCases)
{
var args = testCase.ArgsString.Split(' ');
var parser = new Parser(cfg => cfg.CaseInsensitiveEnumValues = true).ParseArguments<Options>(args)
.MapResult(
options =>
{
if (testCase.ExpectedGenerateJsonNumberAs == null
|| testCase.ExpectedErrorParameter != null
|| options.GenerateJsonNumberAs != testCase.ExpectedGenerateJsonNumberAs)
{
builder.AppendLine($"\u2022 {testCase.ArgsString}");
}
return true;
},
err =>
{
var allErrors = err.ToList();
if (testCase.ExpectedGenerateJsonNumberAs != null
|| testCase.ExpectedErrorParameter == null
|| allErrors.Count != 1
|| !(allErrors[0] is BadFormatConversionError)
|| ((NamedError)allErrors[0]).NameInfo.NameText != testCase.ExpectedErrorParameter)
{
builder.AppendLine($"\u2022 {testCase.ArgsString}");
}
return true;
});
}
builder.Length.Should().Be(0,
$"all test cases should pass, but the following test cases failed:\n{builder}");
}
}
}
158 changes: 158 additions & 0 deletions src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,143 @@ public partial class C
GeneratesPropertiesWithIntegerTypes_Helper(GenerateJsonIntegerOption.Int, ExpectedClass_Int);
}

[Fact(DisplayName = "DataModelGenerator generates properties with number types")]
public void GeneratesPropertiesWithNumberTypes()
{
const string ExpectedClass_Double =
@"using System;
using System.CodeDom.Compiler;
using System.Runtime.Serialization;

namespace N
{
[DataContract]
[GeneratedCode(""Microsoft.Json.Schema.ToDotNet"", ""2.1.0.0"")]
public partial class C
{
[DataMember(Name = ""numberProperty_default"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_default { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_na"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_0_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_minus1_max_na"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_minus1_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_1_max_na"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_1_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_na_max_0"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_na_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_floattimes10"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_0_max_floattimes10 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_float"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_0_max_float { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_decimaladd1"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_0_max_decimaladd1 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_decimal"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_0_max_decimal { get; set; }
[DataMember(Name = ""numberProperty_min_decimal_max_0"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_decimal_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_decimalminus1_max_0"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_decimalminus1_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_float_max_0"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_float_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_floattimes10_max_0"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_floattimes10_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_double_max_0"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_double_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_double"", IsRequired = false, EmitDefaultValue = false)]
public double NumberProperty_min_0_max_double { get; set; }
}
}";
const string ExpectedClass_Float =
@"using System;
using System.CodeDom.Compiler;
using System.Runtime.Serialization;

namespace N
{
[DataContract]
[GeneratedCode(""Microsoft.Json.Schema.ToDotNet"", ""2.1.0.0"")]
public partial class C
{
[DataMember(Name = ""numberProperty_default"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_default { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_na"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_0_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_minus1_max_na"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_minus1_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_1_max_na"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_1_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_na_max_0"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_na_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_floattimes10"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_0_max_floattimes10 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_float"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_0_max_float { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_decimaladd1"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_0_max_decimaladd1 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_decimal"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_0_max_decimal { get; set; }
[DataMember(Name = ""numberProperty_min_decimal_max_0"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_decimal_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_decimalminus1_max_0"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_decimalminus1_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_float_max_0"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_float_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_floattimes10_max_0"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_floattimes10_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_double_max_0"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_double_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_double"", IsRequired = false, EmitDefaultValue = false)]
public float NumberProperty_min_0_max_double { get; set; }
}
}";
const string ExpectedClass_Decimal =
@"using System;
using System.CodeDom.Compiler;
using System.Runtime.Serialization;

namespace N
{
[DataContract]
[GeneratedCode(""Microsoft.Json.Schema.ToDotNet"", ""2.1.0.0"")]
public partial class C
{
[DataMember(Name = ""numberProperty_default"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_default { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_na"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_0_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_minus1_max_na"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_minus1_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_1_max_na"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_1_max_na { get; set; }
[DataMember(Name = ""numberProperty_min_na_max_0"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_na_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_floattimes10"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_0_max_floattimes10 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_float"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_0_max_float { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_decimaladd1"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_0_max_decimaladd1 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_decimal"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_0_max_decimal { get; set; }
[DataMember(Name = ""numberProperty_min_decimal_max_0"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_decimal_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_decimalminus1_max_0"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_decimalminus1_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_float_max_0"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_float_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_floattimes10_max_0"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_floattimes10_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_double_max_0"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_double_max_0 { get; set; }
[DataMember(Name = ""numberProperty_min_0_max_double"", IsRequired = false, EmitDefaultValue = false)]
public decimal NumberProperty_min_0_max_double { get; set; }
}
}";
GeneratesPropertiesWithNumberTypes_Helper(GenerateJsonNumberOption.Double, ExpectedClass_Double);
GeneratesPropertiesWithNumberTypes_Helper(GenerateJsonNumberOption.Float, ExpectedClass_Float);
GeneratesPropertiesWithNumberTypes_Helper(GenerateJsonNumberOption.Decimal, ExpectedClass_Decimal);
}

[Fact(DisplayName = "DataModelGenerator generates properties with Uuid types")]
public void GeneratesPropertiesWithUuidTypes()
{
Expand Down Expand Up @@ -5259,5 +5396,26 @@ private void GeneratesPropertiesWithIntegerTypes_Helper(GenerateJsonIntegerOptio
VerifyGeneratedFileContents(expectedContentsDictionary);
_testFileSystem = new TestFileSystem();
}

private void GeneratesPropertiesWithNumberTypes_Helper(GenerateJsonNumberOption generateJsonNumberAs, string expectedClass)
{
_settings.GenerateEqualityComparers = false;
_settings.GenerateComparers = false;
_settings.GenerateJsonNumberAs = generateJsonNumberAs;
var generator = new DataModelGenerator(_settings, _testFileSystem.FileSystem);
JsonSchema schema = TestUtil.CreateSchemaFromTestDataFile("Number");
var expectedContentsDictionary = new Dictionary<string, ExpectedContents>
{
[_settings.RootClassName] = new ExpectedContents
{
ClassContents = expectedClass
}
};

generator.Generate(schema);

VerifyGeneratedFileContents(expectedContentsDictionary);
_testFileSystem = new TestFileSystem();
}
}
}
74 changes: 74 additions & 0 deletions src/Json.Schema.ToDotNet.UnitTests/TestData/Number.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"type": "object",
"properties": {
"numberProperty_default": {
"type": "number"
},
"numberProperty_min_0_max_na": {
"type": "number",
"minimum": 0
},
"numberProperty_min_minus1_max_na": {
"type": "number",
"minimum": -1
},
"numberProperty_min_1_max_na": {
"type": "number",
"minimum": 1
},
"numberProperty_min_na_max_0": {
"type": "number",
"maximum": 0
},
"numberProperty_min_0_max_floattimes10": {
"type": "number",
"minimum": 0,
"maximum": 3.40282347E+39
},
"numberProperty_min_0_max_float": {
"type": "number",
"minimum": 0,
"maximum": 3.40282347E+38
},
"numberProperty_min_0_max_decimaladd1": {
"type": "number",
"minimum": 0,
"maximum": 79228162514264337593543950336
},
"numberProperty_min_0_max_decimal": {
"type": "number",
"minimum": 0,
"maximum": 79228162514264337593543950335
},
"numberProperty_min_decimal_max_0": {
"type": "number",
"minimum": -79228162514264337593543950335,
"maximum": 0
},
"numberProperty_min_decimalminus1_max_0": {
"type": "number",
"minimum": -79228162514264337593543950336,
"maximum": 0
},
"numberProperty_min_float_max_0": {
"type": "number",
"minimum": -3.40282347E+38,
"maximum": 0
},
"numberProperty_min_floattimes10_max_0": {
"type": "number",
"minimum": -3.40282347E+39,
"maximum": 0
},
"numberProperty_min_double_max_0": {
"type": "number",
"minimum": -1.7976931348623157E+308,
"maximum": 0
},
"numberProperty_min_0_max_double": {
"type": "number",
"minimum": 0,
"maximum": 1.7976931348623157E+308
}
}
}
3 changes: 2 additions & 1 deletion src/Json.Schema.ToDotNet/DataModelGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ internal string GenerateClass(
schema,
_settings.HintDictionary,
OnAdditionalTypeRequired,
_settings.GenerateJsonIntegerAs);
_settings.GenerateJsonIntegerAs,
_settings.GenerateJsonNumberAs);

_classInfoDictionary.Add(suffixedClassName, propertyInfoDictionary);

Expand Down
5 changes: 5 additions & 0 deletions src/Json.Schema.ToDotNet/DataModelGeneratorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public class DataModelGeneratorSettings
/// </summary>
public GenerateJsonIntegerOption GenerateJsonIntegerAs { get; set; }

/// <summary>
/// Gets or sets a value indicating what C# type to emit for Json numbers.
/// </summary>
public GenerateJsonNumberOption GenerateJsonNumberAs { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the generated classes should
/// be declared <code>sealed</code>. If <code>false</code>, the generated
Expand Down
27 changes: 27 additions & 0 deletions src/Json.Schema.ToDotNet/GenerateNumberOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.Json.Schema.ToDotNet
{
/// <summary>
/// Values that specify the C# type to be generated
/// for each Json Number property.
/// </summary>
public enum GenerateJsonNumberOption
{
/// <summary>
/// C# double type
/// </summary>
Double,

/// <summary>
/// C# float type
/// </summary>
Float,

/// <summary>
/// C# decimal type
/// </summary>
Decimal,
}
}
Loading