diff --git a/src/Json.Schema.ToDotNet.Cli/Options.cs b/src/Json.Schema.ToDotNet.Cli/Options.cs index 7da91db..b2c9985 100644 --- a/src/Json.Schema.ToDotNet.Cli/Options.cs +++ b/src/Json.Schema.ToDotNet.Cli/Options.cs @@ -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.", diff --git a/src/Json.Schema.ToDotNet.Cli/Program.cs b/src/Json.Schema.ToDotNet.Cli/Program.cs index 1536b4a..a33935a 100644 --- a/src/Json.Schema.ToDotNet.Cli/Program.cs +++ b/src/Json.Schema.ToDotNet.Cli/Program.cs @@ -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 diff --git a/src/Json.Schema.ToDotNet.UnitTests/CommandLineTests.cs b/src/Json.Schema.ToDotNet.UnitTests/CommandLineTests.cs index 5894c1e..d0626f0 100644 --- a/src/Json.Schema.ToDotNet.UnitTests/CommandLineTests.cs +++ b/src/Json.Schema.ToDotNet.UnitTests/CommandLineTests.cs @@ -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() { @@ -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() + { + 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(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}"); + } } } diff --git a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs index 4e62995..c21d728 100644 --- a/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs +++ b/src/Json.Schema.ToDotNet.UnitTests/DataModelGeneratorTests.cs @@ -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() { @@ -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 + { + [_settings.RootClassName] = new ExpectedContents + { + ClassContents = expectedClass + } + }; + + generator.Generate(schema); + + VerifyGeneratedFileContents(expectedContentsDictionary); + _testFileSystem = new TestFileSystem(); + } } } diff --git a/src/Json.Schema.ToDotNet.UnitTests/TestData/Number.schema.json b/src/Json.Schema.ToDotNet.UnitTests/TestData/Number.schema.json new file mode 100644 index 0000000..fe289da --- /dev/null +++ b/src/Json.Schema.ToDotNet.UnitTests/TestData/Number.schema.json @@ -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 + } + } +} \ No newline at end of file diff --git a/src/Json.Schema.ToDotNet/DataModelGenerator.cs b/src/Json.Schema.ToDotNet/DataModelGenerator.cs index 6e2e45e..31d9f48 100644 --- a/src/Json.Schema.ToDotNet/DataModelGenerator.cs +++ b/src/Json.Schema.ToDotNet/DataModelGenerator.cs @@ -309,7 +309,8 @@ internal string GenerateClass( schema, _settings.HintDictionary, OnAdditionalTypeRequired, - _settings.GenerateJsonIntegerAs); + _settings.GenerateJsonIntegerAs, + _settings.GenerateJsonNumberAs); _classInfoDictionary.Add(suffixedClassName, propertyInfoDictionary); diff --git a/src/Json.Schema.ToDotNet/DataModelGeneratorSettings.cs b/src/Json.Schema.ToDotNet/DataModelGeneratorSettings.cs index 871c4ae..b5d2e51 100644 --- a/src/Json.Schema.ToDotNet/DataModelGeneratorSettings.cs +++ b/src/Json.Schema.ToDotNet/DataModelGeneratorSettings.cs @@ -87,6 +87,11 @@ public class DataModelGeneratorSettings /// public GenerateJsonIntegerOption GenerateJsonIntegerAs { get; set; } + /// + /// Gets or sets a value indicating what C# type to emit for Json numbers. + /// + public GenerateJsonNumberOption GenerateJsonNumberAs { get; set; } + /// /// Gets or sets a value indicating whether the generated classes should /// be declared sealed. If false, the generated diff --git a/src/Json.Schema.ToDotNet/GenerateNumberOption.cs b/src/Json.Schema.ToDotNet/GenerateNumberOption.cs new file mode 100644 index 0000000..34b37b7 --- /dev/null +++ b/src/Json.Schema.ToDotNet/GenerateNumberOption.cs @@ -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 +{ + /// + /// Values that specify the C# type to be generated + /// for each Json Number property. + /// + public enum GenerateJsonNumberOption + { + /// + /// C# double type + /// + Double, + + /// + /// C# float type + /// + Float, + + /// + /// C# decimal type + /// + Decimal, + } +} diff --git a/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs b/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs index fb2b193..45ac8c3 100644 --- a/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs +++ b/src/Json.Schema.ToDotNet/PropertyInfoDictionary.cs @@ -59,6 +59,7 @@ public class PropertyInfoDictionary : IReadOnlyDictionary private readonly AdditionalTypeRequiredDelegate _additionalTypeRequiredDelegate; private readonly GenerateJsonIntegerOption _generateJsonIntegerAs; + private readonly GenerateJsonNumberOption _generateJsonNumberAs; private readonly string _typeNameSuffix; /// @@ -79,7 +80,8 @@ public PropertyInfoDictionary( JsonSchema schema, HintDictionary hintDictionary, AdditionalTypeRequiredDelegate additionalTypeRequiredDelegate, - GenerateJsonIntegerOption generateJsonIntegerAs) + GenerateJsonIntegerOption generateJsonIntegerAs, + GenerateJsonNumberOption generateJsonNumberAs) { _typeName = typeName; _typeNameSuffix = typeNameSuffix; @@ -87,6 +89,7 @@ public PropertyInfoDictionary( _hintDictionary = hintDictionary; _additionalTypeRequiredDelegate = additionalTypeRequiredDelegate; _generateJsonIntegerAs = generateJsonIntegerAs; + _generateJsonNumberAs = generateJsonNumberAs; _dictionary = PropertyInfoDictionaryFromSchema(); } @@ -296,12 +299,17 @@ private void AddPropertyInfoFromPropertySchema( switch (propertyType) { case SchemaType.Boolean: - case SchemaType.Number: comparisonKind = ComparisonKind.OperatorEquals; hashKind = HashKind.ScalarValueType; initializationKind = InitializationKind.SimpleAssign; type = MakePrimitiveType(propertyType); break; + case SchemaType.Number: + comparisonKind = ComparisonKind.OperatorEquals; + hashKind = HashKind.ScalarValueType; + initializationKind = InitializationKind.SimpleAssign; + type = MakeProperNumberType(_generateJsonNumberAs); + break; case SchemaType.Integer: comparisonKind = ComparisonKind.OperatorEquals; @@ -720,5 +728,18 @@ internal TypeSyntax MakeProperIntegerType(GenerateJsonIntegerOption generateJson return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)); } } + + internal TypeSyntax MakeProperNumberType(GenerateJsonNumberOption generateJsonNumberAs) + { + switch (generateJsonNumberAs) + { + case GenerateJsonNumberOption.Float: + return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.FloatKeyword)); + case GenerateJsonNumberOption.Decimal: + return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DecimalKeyword)); + default: + return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DoubleKeyword)); + } + } } } diff --git a/src/ReleaseHistory.md b/src/ReleaseHistory.md index ddc85fb..1c7e697 100644 --- a/src/ReleaseHistory.md +++ b/src/ReleaseHistory.md @@ -1,5 +1,8 @@ # Microsoft Json Schema Packages +## **Unreleased** +* FEATURE: Add new option for specifying .NET type to express Json numbers: `--generate-json-number-as = double | float | decimal` with a default of `double`. [#166](https://github.com/microsoft/jschema/pull/166) + ## **2.1.0** [Pointer](https://www.nuget.org/packages/Microsoft.Json.Pointer/2.1.0) | [Schema](https://www.nuget.org/packages/Microsoft.Json.Schema/2.1.0)| [Schema.ToDotNet](https://www.nuget.org/packages/Microsoft.Json.Schema.ToDotNet/2.1.0)| [Schema.Validation](https://www.nuget.org/packages/Microsoft.Json.Schema.Validation/2.1.0) * FEATURE: Add support for JSON Schema type `uuid` generate as C# nullable `Guid?`. [#164](https://github.com/microsoft/jschema/pull/164)