diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs index 164e904ea..1eeea9cbe 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs @@ -24,6 +24,7 @@ public async Task When_type_is_array_and_items_and_item_is_not_defined_then_any_ { //// Arrange var json = @"{ + 'required': [ 'emptySchema' ], 'properties': { 'emptySchema': { 'type': 'array' } } @@ -560,6 +561,7 @@ public async Task When_patternProperties_is_set_with_string_value_type_then_corr { //// Arrange var schemaJson = @"{ + ""required"": [ ""dict"" ], ""properties"": { ""dict"": { ""type"": ""object"", diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj index 7d3d5ce99..018ba21e2 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj @@ -4,6 +4,30 @@ false true + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/A.json b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/A.json new file mode 100644 index 000000000..de0ec150f --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/A.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "required": [ + "signalPaths" + ], + "description": "This is the class A", + "properties": { + "type": { + "$ref": "./C.json", + "description": "This is the type of A" + }, + "signalPaths": { + "children": "array", + "items": { + "$ref": "./B.json", + "description": "This is a list of children" + } + } + } +} \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/B.json b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/B.json new file mode 100644 index 000000000..632a7a95f --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/B.json @@ -0,0 +1,18 @@ +{ + "type": "object", + "description": "This is the class B", + "properties": { + "id": { + "type": "string", + "description": "This is the ID of B" + }, + "type": { + "$ref": "./C.json", + "description": "This is the type of B" + }, + "empty": { + "$ref": "./D.json", + "description": "This one should be of type D and not object" + } + } +} diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/C.json b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/C.json new file mode 100644 index 000000000..09250df0b --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/C.json @@ -0,0 +1,8 @@ +{ + "type":"string", + "description":"Possible types", + "enum":[ + "First", + "Second" + ] +} \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/D.json b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/D.json new file mode 100644 index 000000000..b8df2006a --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/D.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "description": "Possible types" +} \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/E.json b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/E.json new file mode 100644 index 000000000..78654eae1 --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/References/E.json @@ -0,0 +1,52 @@ +{ + "definitions": { + "B": { + "type": "object", + "description": "This is the class B", + "properties": { + "id": { + "type": "string", + "description": "This is the ID of B" + }, + "type": { + "$ref": "#/definitions/C", + "description": "This is the type of B" + }, + "empty": { + "$ref": "#/definitions/D", + "description": "This one should be of type D and not object" + } + } + }, + "C": { + "type": "string", + "description": "Possible types", + "enum": [ + "First", + "Second" + ] + }, + "D": { + "type": "object", + "description": "Possible types" + } + }, + "type": "object", + "required": [ + "signalPaths" + ], + "description": "This is the class E", + "properties": { + "type": { + "$ref": "#/definitions/C", + "description": "This is the type of E" + }, + "signalPaths": { + "children": "array", + "items": { + "$ref": "#/definitions/B", + "description": "This is a list of children" + } + } + } +} \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/ReferencesTest.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ReferencesTest.cs new file mode 100644 index 000000000..b92723519 --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ReferencesTest.cs @@ -0,0 +1,55 @@ +using NJsonSchema.CodeGeneration.CSharp; +using System; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using Xunit; + +namespace NJsonSchema.CodeGeneration.Tests.CSharp +{ + public class ReferencesTest + { + [Fact] + public async Task When_ref_is_definitions_no_types_are_duplicated() + { + //// Arrange + var path = GetTestDirectory() + "/References/E.json"; + + //// Act + var schema = await JsonSchema4.FromFileAsync(path); + var generator = new CSharpGenerator(schema); + + //// Act + var code = generator.GenerateFile("MyClass"); + + //// Assert + Assert.Contains("public enum C", code); + Assert.DoesNotContain("public enum C2", code); + } + + [Fact] + public async Task When_ref_is_file_no_types_are_duplicated() + { + //// Arrange + var path = GetTestDirectory() + "/References/A.json"; + + //// Act + var schema = await JsonSchema4.FromFileAsync(path); + var generator = new CSharpGenerator(schema); + + //// Act + var code = generator.GenerateFile("MyClass"); + + //// Assert + Assert.Contains("public enum C", code); + Assert.DoesNotContain("public enum C2", code); + } + + private string GetTestDirectory() + { + var codeBase = Assembly.GetExecutingAssembly().CodeBase; + var uri = new UriBuilder(codeBase); + return Path.GetDirectoryName(Uri.UnescapeDataString(uri.Path)); + } + } +} diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs index 4b18628b4..31dd78b0b 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs @@ -37,8 +37,10 @@ public override string GetDefaultValue(JsonSchema4 schema, bool allowsNull, stri var value = base.GetDefaultValue(schema, allowsNull, targetType, typeNameHint, useSchemaDefault, typeResolver); if (value == null) { + var isOptional = (schema as JsonProperty)?.IsRequired == false; + schema = schema.ActualSchema; - if (schema != null && allowsNull == false) + if (schema != null && allowsNull == false && isOptional == false) { if (schema.Type.HasFlag(JsonObjectType.Array) || schema.Type.HasFlag(JsonObjectType.Object)) diff --git a/src/NJsonSchema.CodeGeneration.CSharp/NJsonSchema.CodeGeneration.CSharp.csproj b/src/NJsonSchema.CodeGeneration.CSharp/NJsonSchema.CodeGeneration.CSharp.csproj index 679e5aa1d..a18465d14 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/NJsonSchema.CodeGeneration.CSharp.csproj +++ b/src/NJsonSchema.CodeGeneration.CSharp/NJsonSchema.CodeGeneration.CSharp.csproj @@ -2,7 +2,7 @@ netstandard1.3;netstandard2.0;net451 JSON Schema reader, generator and validator for .NET - 9.13.15 + 9.13.16 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ClassGenerationTests.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ClassGenerationTests.cs index 40f6e755c..ccdaa42d1 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ClassGenerationTests.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ClassGenerationTests.cs @@ -1,6 +1,11 @@ -using System; +using Newtonsoft.Json; +using NJsonSchema.Converters; +using NJsonSchema.Generation; +using System; using System.Collections.Generic; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; using System.Threading.Tasks; using Xunit; @@ -305,5 +310,45 @@ public async Task When_Knockout_class_is_generated_then_initializers_are_correct //// Assert Assert.DoesNotContain("let firstName_ = data[\"FirstName\"];", code); } + + [Fact] + public async Task When_GenerateConstructorInterface_is_disabled_then_data_is_not_checked_and_default_initialization_is_always_exectued() + { + // Assert + var schema = JsonSchema4.FromTypeAsync( + typeof(MyDerivedClass), + new JsonSchemaGeneratorSettings + { + GenerateAbstractProperties = true + }).Result; + + var generator = new TypeScriptGenerator(schema); + generator.Settings.GenerateConstructorInterface = false; + generator.Settings.MarkOptionalProperties = true; + generator.Settings.TypeStyle = TypeScriptTypeStyle.Class; + + // Act + var output = generator.GenerateFile(); + + // Assert + Assert.DoesNotContain("if (!data) {", output); + } + + [JsonConverter(typeof(JsonInheritanceConverter))] + [KnownType(typeof(MyDerivedClass))] + public abstract class MyBaseClass + { + [Required] + public MyPropertyClass MyProperty { get; set; } + } + + public sealed class MyDerivedClass : MyBaseClass + { + } + + public sealed class MyPropertyClass + { + public string MyStringProperty { get; set; } + } } } \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/DictionaryTests.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/DictionaryTests.cs index 14d571fc8..48d15d027 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/DictionaryTests.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/DictionaryTests.cs @@ -104,6 +104,7 @@ public async Task When_property_is_dto_dictionary_then_assignment_may_create_new { //// Arrange var json = @"{ + ""required"": [ ""resource"" ], ""properties"": { ""resource"": { ""type"": ""object"", diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/Models/Person.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/Models/Person.cs index 5c956b97e..43cd5f078 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/Models/Person.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/Models/Person.cs @@ -26,6 +26,7 @@ public class Person public Gender? GenderOrNull { get; set; } + [JsonProperty("address", Required = Required.Always)] public Address Address { get; set; } [CanBeNull] diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs index 9bb5fe42d..627f3351c 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs @@ -11,12 +11,13 @@ public class NullabilityTests public async Task Strict_nullability_in_TypeScript2() { var schema = await JsonSchema4.FromTypeAsync( - new JsonSchemaGeneratorSettings { + new JsonSchemaGeneratorSettings + { DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull }); - var generator = new TypeScriptGenerator(schema, - new TypeScriptGeneratorSettings { TypeScriptVersion = 2 }); + var json = schema.ToJson(); + var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings { TypeScriptVersion = 2 }); var output = generator.GenerateFile("MyClass"); @@ -29,5 +30,134 @@ public async Task Strict_nullability_in_TypeScript2() Assert.Contains("genderOrNull: Gender | undefined;", output); Assert.Contains("addressOrNull: Address | undefined;", output); } + + [Fact] + public async Task When_a_complex_property_is_not_required_and_not_nullable_then_default_is_undefined() + { + // Arrange + var schema = await JsonSchema4.FromJsonAsync(@"{ + ""type"": ""object"", + ""properties"": { + ""parent"": { + ""$ref"": ""#/definitions/ParentDto"" + } + }, + ""definitions"": { + ""ParentDto"": { + ""type"": ""object"", + ""properties"": { + ""child"": { + ""$ref"": ""#/definitions/ChildDto"" + } + } + }, + ""ChildDto"": { + ""type"": ""object"", + ""properties"": { + ""property"": { + ""type"": ""string"" + } + } + } + } +}"); + + var generator = new TypeScriptGenerator(schema, + new TypeScriptGeneratorSettings { TypeScriptVersion = 2, SchemaType = SchemaType.OpenApi3 }); + + // Act + var output = generator.GenerateFile("MyClass"); + + // Assert + Assert.DoesNotContain(": new ChildDto();", output); + } + + [Fact] + public async Task When_a_complex_property_is_required_and_not_nullable_then_default_is_new_instance() + { + // Arrange + var schema = await JsonSchema4.FromJsonAsync(@"{ + ""type"": ""object"", + ""properties"": { + ""parent"": { + ""$ref"": ""#/definitions/ParentDto"" + } + }, + ""definitions"": { + ""ParentDto"": { + ""type"": ""object"", + ""required"": [ ""child"" ], + ""properties"": { + ""child"": { + ""$ref"": ""#/definitions/ChildDto"" + } + } + }, + ""ChildDto"": { + ""type"": ""object"", + ""properties"": { + ""property"": { + ""type"": ""string"" + } + } + } + } +}"); + + var generator = new TypeScriptGenerator(schema, + new TypeScriptGeneratorSettings { TypeScriptVersion = 2, SchemaType = SchemaType.OpenApi3 }); + + // Act + var output = generator.GenerateFile("MyClass"); + + // Assert + Assert.Contains(": new ChildDto();", output); + } + + [Fact] + public async Task When_a_complex_property_is_nullable_then_default_is_null() + { + // Arrange + var schema = await JsonSchema4.FromJsonAsync(@"{ + ""type"": ""object"", + ""properties"": { + ""parent"": { + ""$ref"": ""#/definitions/ParentDto"" + } + }, + ""definitions"": { + ""ParentDto"": { + ""type"": ""object"", + ""properties"": { + ""child"": { + ""nullable"": true, + ""allOf"": [ + { + ""$ref"": ""#/definitions/ChildDto"" + } + ] + } + } + }, + ""ChildDto"": { + ""type"": ""object"", + ""properties"": { + ""property"": { + ""type"": ""string"" + } + } + } + } +}"); + + var generator = new TypeScriptGenerator(schema, + new TypeScriptGeneratorSettings { TypeScriptVersion = 2, SchemaType = SchemaType.OpenApi3 }); + + // Act + var output = generator.GenerateFile("MyClass"); + + // Assert + Assert.DoesNotContain(": new ChildDto();", output); + } } } diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/TypeScriptGeneratorTests.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/TypeScriptGeneratorTests.cs index 5ca3fddbc..36a429355 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/TypeScriptGeneratorTests.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/TypeScriptGeneratorTests.cs @@ -231,6 +231,7 @@ public async Task When_patternProperties_is_set_with_string_value_type_then_corr { //// Arrange var schemaJson = @"{ + ""required"": [ ""dict"" ], ""properties"": { ""dict"": { ""type"": ""object"", diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/NJsonSchema.CodeGeneration.TypeScript.csproj b/src/NJsonSchema.CodeGeneration.TypeScript/NJsonSchema.CodeGeneration.TypeScript.csproj index 7719bf3cf..87314c8eb 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/NJsonSchema.CodeGeneration.TypeScript.csproj +++ b/src/NJsonSchema.CodeGeneration.TypeScript/NJsonSchema.CodeGeneration.TypeScript.csproj @@ -2,7 +2,7 @@ netstandard1.3;netstandard2.0;net451 JSON Schema reader, generator and validator for .NET - 9.13.15 + 9.13.16 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/Templates/Class.liquid b/src/NJsonSchema.CodeGeneration.TypeScript/Templates/Class.liquid index fdb0461bf..259b08cad 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/Templates/Class.liquid +++ b/src/NJsonSchema.CodeGeneration.TypeScript/Templates/Class.liquid @@ -60,7 +60,7 @@ } {% endif -%} {% if HasDefaultValues -%} - if (!data) { + {% if GenerateConstructorInterface %}if (!data) {% endif %}{ {% for property in Properties -%} {% if property.HasDefaultValue -%} this.{{ property.PropertyName }} = {{ property.DefaultValue }}; diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs index a183f9916..25aab20e6 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs @@ -31,7 +31,8 @@ public override string GetDefaultValue(JsonSchema4 schema, bool allowsNull, stri var value = base.GetDefaultValue(schema, allowsNull, targetType, typeNameHint, useSchemaDefault, typeResolver); if (value == null) { - if (schema != null && allowsNull == false) + var isOptional = (schema as JsonProperty)?.IsRequired == false; + if (schema != null && allowsNull == false && isOptional == false) { if (typeResolver.GeneratesType(schema) && !schema.ActualTypeSchema.IsEnumeration && diff --git a/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj b/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj index 249b6da66..cbcc6fbdc 100644 --- a/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj +++ b/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj @@ -2,7 +2,7 @@ netstandard1.3;netstandard2.0;net451 JSON Schema reader, generator and validator for .NET - 9.13.15 + 9.13.16 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md diff --git a/src/NJsonSchema.Yaml/NJsonSchema.Yaml.csproj b/src/NJsonSchema.Yaml/NJsonSchema.Yaml.csproj index 903c3da45..babec7ceb 100644 --- a/src/NJsonSchema.Yaml/NJsonSchema.Yaml.csproj +++ b/src/NJsonSchema.Yaml/NJsonSchema.Yaml.csproj @@ -2,7 +2,7 @@ netstandard1.3;netstandard2.0;net45 JSON Schema reader, generator and validator for .NET - 9.13.15 + 9.13.16 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md diff --git a/src/NJsonSchema/Infrastructure/DynamicApis.cs b/src/NJsonSchema/Infrastructure/DynamicApis.cs index 8743f4a2d..b81082d06 100644 --- a/src/NJsonSchema/Infrastructure/DynamicApis.cs +++ b/src/NJsonSchema/Infrastructure/DynamicApis.cs @@ -180,6 +180,18 @@ public static string PathCombine(string path1, string path2) return (string)PathType.GetRuntimeMethod("Combine", new[] { typeof(string), typeof(string) }).Invoke(null, new object[] { path1, path2 }); } + /// Gets the full path from a given path + /// The path + /// The full path + /// The System.IO.Path API is not available on this platform. + public static string GetFullPath(string path) + { + if (!SupportsPathApis) + throw new NotSupportedException("The System.IO.Path API is not available on this platform."); + + return (string)PathType.GetRuntimeMethod("GetFullPath", new[] { typeof(string) }).Invoke(null, new object[] { path }); + } + /// Gets the directory path of a file path. /// The file path. /// The directory name. diff --git a/src/NJsonSchema/JsonReferenceResolver.cs b/src/NJsonSchema/JsonReferenceResolver.cs index d6e26c068..beb5409ac 100644 --- a/src/NJsonSchema/JsonReferenceResolver.cs +++ b/src/NJsonSchema/JsonReferenceResolver.cs @@ -146,7 +146,7 @@ private async Task ResolveFileReferenceWithAlreadyResolvedCheckA try { var arr = Regex.Split(fullJsonPath, @"(?=#)"); - var filePath = arr[0]; + var filePath = DynamicApis.GetFullPath(arr[0]); if (!_resolvedObjects.ContainsKey(filePath)) { var schema = await ResolveFileReferenceAsync(filePath).ConfigureAwait(false); diff --git a/src/NJsonSchema/NJsonSchema.csproj b/src/NJsonSchema/NJsonSchema.csproj index a6d5af667..5ce6eed16 100644 --- a/src/NJsonSchema/NJsonSchema.csproj +++ b/src/NJsonSchema/NJsonSchema.csproj @@ -2,7 +2,7 @@ netstandard1.0;netstandard2.0;net40;net45 JSON Schema reader, generator and validator for .NET - 9.13.15 + 9.13.16 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md diff --git a/src/NJsonSchema/NJsonSchema.nuspec b/src/NJsonSchema/NJsonSchema.nuspec index 5b75a2c24..728ab8034 100644 --- a/src/NJsonSchema/NJsonSchema.nuspec +++ b/src/NJsonSchema/NJsonSchema.nuspec @@ -7,7 +7,7 @@ $description$ json schema validation generator .net http://NJsonSchema.org - https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md + MIT @@ -26,4 +26,4 @@ - \ No newline at end of file +