From 6dde59aefeb4dc00ada711979977c805b0d0c7fd Mon Sep 17 00:00:00 2001 From: WISNI4 Date: Fri, 1 Mar 2019 12:52:42 +0100 Subject: [PATCH 1/4] Update ValueGeneratorBase.cs (#913) * Update ValueGeneratorBase.cs string = datetime format error fix for c# * Update ValueGeneratorBase.cs * Update TypeScriptValueGenerator.cs * Code cleanup * Code cleanup --- .../TypeScriptValueGenerator.cs | 16 +++++++++- .../ValueGeneratorBase.cs | 29 +++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs index 25aab20e6..0283f9d44 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs @@ -6,11 +6,20 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System.Collections.Generic; + namespace NJsonSchema.CodeGeneration.TypeScript { /// Converts the default value to a TypeScript identifier. public class TypeScriptValueGenerator : ValueGeneratorBase { + private readonly List _formatCompatibleWithString = new List() + { + JsonFormatStrings.Uri, + JsonFormatStrings.Guid, + JsonFormatStrings.Uuid + }; + /// Initializes a new instance of the class. /// The settings. public TypeScriptValueGenerator(TypeScriptGeneratorSettings settings) @@ -31,6 +40,11 @@ public override string GetDefaultValue(JsonSchema4 schema, bool allowsNull, stri var value = base.GetDefaultValue(schema, allowsNull, targetType, typeNameHint, useSchemaDefault, typeResolver); if (value == null) { + if (schema.Type.HasFlag(JsonObjectType.String) && _formatCompatibleWithString.Contains(schema.Format)) + { + return "\"" + ConversionUtilities.ConvertToStringLiteral(value.ToString()) + "\""; + } + var isOptional = (schema as JsonProperty)?.IsRequired == false; if (schema != null && allowsNull == false && isOptional == false) { @@ -66,4 +80,4 @@ public override string GetNumericValue(JsonObjectType type, object value, string return ConvertNumberToString(value); } } -} \ No newline at end of file +} diff --git a/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs b/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs index be67921d8..ca548d98f 100644 --- a/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs +++ b/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs @@ -6,6 +6,8 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -15,7 +17,19 @@ namespace NJsonSchema.CodeGeneration public abstract class ValueGeneratorBase { private readonly CodeGeneratorSettingsBase _settings; - + private readonly List _formatNotCompatibleWithString = new List() + { + JsonFormatStrings.Date, + JsonFormatStrings.DateTime, + JsonFormatStrings.Time, + JsonFormatStrings.TimeSpan, + JsonFormatStrings.Uri, + JsonFormatStrings.Guid, + JsonFormatStrings.Uuid, + JsonFormatStrings.Base64, + JsonFormatStrings.Byte, + }; + /// Initializes a new instance of the class. /// The settings. protected ValueGeneratorBase(CodeGeneratorSettingsBase settings) @@ -41,7 +55,8 @@ public virtual string GetDefaultValue(JsonSchema4 schema, bool allowsNull, strin return GetEnumDefaultValue(schema, actualSchema, typeNameHint, typeResolver); if (schema.Type.HasFlag(JsonObjectType.String)) - return "\"" + ConversionUtilities.ConvertToStringLiteral(schema.Default.ToString()) + "\""; + return GetStringValue(schema.Type, schema.Default, schema.Format); + if (schema.Type.HasFlag(JsonObjectType.Boolean)) return schema.Default.ToString().ToLowerInvariant(); if (schema.Type.HasFlag(JsonObjectType.Integer) || @@ -51,6 +66,14 @@ public virtual string GetDefaultValue(JsonSchema4 schema, bool allowsNull, strin return null; } + private string GetStringValue(JsonObjectType type, object value, string format) + { + if(!_formatNotCompatibleWithString.Contains(format)) + return "\"" + ConversionUtilities.ConvertToStringLiteral(value.ToString()) + "\""; + else + return null; + } + /// Converts the default value to a number literal. /// The JSON type. /// The value to convert. @@ -117,4 +140,4 @@ protected string ConvertNumberToString(object value) return null; } } -} \ No newline at end of file +} From c3f1c3a91bc44a6564cdb06910a3763effa7134f Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Fri, 1 Mar 2019 13:31:32 +0100 Subject: [PATCH 2/4] Fix build errors, #913 --- .../TypeScriptValueGenerator.cs | 2 ++ src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs index 0283f9d44..c623d5157 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptValueGenerator.cs @@ -17,7 +17,9 @@ public class TypeScriptValueGenerator : ValueGeneratorBase { JsonFormatStrings.Uri, JsonFormatStrings.Guid, +#pragma warning disable CS0618 // Type or member is obsolete JsonFormatStrings.Uuid +#pragma warning restore CS0618 // Type or member is obsolete }; /// Initializes a new instance of the class. diff --git a/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs b/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs index ca548d98f..52ddd4eaf 100644 --- a/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs +++ b/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs @@ -25,9 +25,11 @@ public abstract class ValueGeneratorBase JsonFormatStrings.TimeSpan, JsonFormatStrings.Uri, JsonFormatStrings.Guid, + JsonFormatStrings.Byte, +#pragma warning disable CS0618 // Type or member is obsolete JsonFormatStrings.Uuid, JsonFormatStrings.Base64, - JsonFormatStrings.Byte, +#pragma warning restore CS0618 // Type or member is obsolete }; /// Initializes a new instance of the class. From 3cf93115a2195b42ab62cb75831515445a465b85 Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Fri, 1 Mar 2019 16:03:09 +0100 Subject: [PATCH 3/4] Add support for nullable array items (CS, TS) (#916) * Add support for nullable array items (CS, TS) * Rename attribute to ItemsCanBeNullAttribute --- .../ArrayTests.cs | 26 +++++++++++++++- .../CSharpTypeResolver.cs | 6 ++-- .../NullabilityTests.cs | 31 ++++++++++++++++++- .../TypeScriptTypeResolver.cs | 17 ++++++++-- .../Annotations/ItemsCanBeNullAttribute.cs | 18 +++++++++++ .../Generation/JsonSchemaGenerator.cs | 18 ++++++++--- 6 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 src/NJsonSchema/Annotations/ItemsCanBeNullAttribute.cs diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs index fa2ee006a..4afc0cd7c 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/ArrayTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using NJsonSchema.Annotations; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Xunit; @@ -32,5 +33,28 @@ public async Task When_array_property_is_required_then_array_instance_can_be_cha //// Assert Assert.Contains("public Foo ArrayProperty { get; set; } = new Bar();", code); } + + public class ClassWithNullableArrayItems + { + [NotNull] + [ItemsCanBeNull] + public List Items { get; set; } + } + + [Fact] + public async Task When_array_item_is_nullable_then_generated_CSharp_is_correct() + { + // Arrange + var schema = await JsonSchema4.FromTypeAsync(); + var json = schema.ToJson(); + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings()); + + // Act + var output = generator.GenerateFile("MyClass"); + + // Assert + Assert.True(schema.Properties["Items"].Item.IsNullable(SchemaType.JsonSchema)); + Assert.Contains("System.Collections.ObjectModel.ObservableCollection Items", output); + } } } \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs index 55fbf079c..5a39604c1 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs @@ -177,12 +177,14 @@ private static string ResolveNumber(JsonSchema4 schema, bool isNullable) private string ResolveArrayOrTuple(JsonSchema4 schema) { if (schema.Item != null) - return string.Format(Settings.ArrayType + "<{0}>", Resolve(schema.Item, false, null)); + { + return string.Format(Settings.ArrayType + "<{0}>", Resolve(schema.Item, schema.Item.IsNullable(Settings.SchemaType), null)); + } if (schema.Items != null && schema.Items.Count > 0) { var tupleTypes = schema.Items - .Select(i => Resolve(i, false, null)) + .Select(i => Resolve(i, i.IsNullable(Settings.SchemaType), null)) .ToArray(); return string.Format("System.Tuple<" + string.Join(", ", tupleTypes) + ">"); diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs index 627f3351c..960668a61 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NullabilityTests.cs @@ -1,5 +1,7 @@ -using NJsonSchema.CodeGeneration.TypeScript.Tests.Models; +using NJsonSchema.Annotations; +using NJsonSchema.CodeGeneration.TypeScript.Tests.Models; using NJsonSchema.Generation; +using System.Collections.Generic; using System.Threading.Tasks; using Xunit; @@ -159,5 +161,32 @@ public async Task When_a_complex_property_is_nullable_then_default_is_null() // Assert Assert.DoesNotContain(": new ChildDto();", output); } + + public class ClassWithNullableArrayItems + { + [NotNull] + [ItemsCanBeNull] + public List Items { get; set; } + } + + [Fact] + public async Task When_array_item_is_nullable_then_generated_TypeScript_is_correct() + { + // Arrange + var schema = await JsonSchema4.FromTypeAsync(); + var json = schema.ToJson(); + var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings + { + TypeScriptVersion = 2.7m, + NullValue = TypeScriptNullValue.Null + }); + + // Act + var output = generator.GenerateFile("MyClass"); + + // Assert + Assert.True(schema.Properties["Items"].Item.IsNullable(SchemaType.JsonSchema)); + Assert.Contains(": (string | null)[]", output); + } } } diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs index d083c56f1..e9468bd57 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs @@ -183,14 +183,17 @@ private string ResolveArrayOrTuple(JsonSchema4 schema, string typeNameHint, bool { var isObject = schema.Item?.ActualSchema.Type.HasFlag(JsonObjectType.Object) == true; var isDictionary = schema.Item?.ActualSchema.IsDictionary == true; + var prefix = addInterfacePrefix && SupportsConstructorConversion(schema.Item) && isObject && !isDictionary ? "I" : ""; - return string.Format("{0}[]", prefix + Resolve(schema.Item, true, typeNameHint)); // TODO: Make typeNameHint singular if possible + var itemType = prefix + Resolve(schema.Item, true, typeNameHint); + + return string.Format("{0}[]", GetNullableItemType(schema, itemType)); // TODO: Make typeNameHint singular if possible } if (schema.Items != null && schema.Items.Count > 0) { var tupleTypes = schema.Items - .Select(i => Resolve(i.ActualSchema, false, null)) + .Select(s => GetNullableItemType(s, Resolve(s, false, null))) .ToArray(); return string.Format("[" + string.Join(", ", tupleTypes) + "]"); @@ -198,5 +201,15 @@ private string ResolveArrayOrTuple(JsonSchema4 schema, string typeNameHint, bool return "any[]"; } + + private string GetNullableItemType(JsonSchema4 schema, string itemType) + { + if (Settings.SupportsStrictNullChecks && schema.Item.IsNullable(Settings.SchemaType)) + { + return string.Format("({0} | {1})", itemType, Settings.NullValue.ToString().ToLowerInvariant()); + } + + return itemType; + } } } \ No newline at end of file diff --git a/src/NJsonSchema/Annotations/ItemsCanBeNullAttribute.cs b/src/NJsonSchema/Annotations/ItemsCanBeNullAttribute.cs new file mode 100644 index 000000000..4717e5e48 --- /dev/null +++ b/src/NJsonSchema/Annotations/ItemsCanBeNullAttribute.cs @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Rico Suter. All rights reserved. +// +// https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md +// Rico Suter, mail@rsuter.com +//----------------------------------------------------------------------- + +using System; + +namespace NJsonSchema.Annotations +{ + /// Annotation to specify that array items are nullable. + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field)] + public class ItemsCanBeNullAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/src/NJsonSchema/Generation/JsonSchemaGenerator.cs b/src/NJsonSchema/Generation/JsonSchemaGenerator.cs index 3ee019f81..1cb09147b 100644 --- a/src/NJsonSchema/Generation/JsonSchemaGenerator.cs +++ b/src/NJsonSchema/Generation/JsonSchemaGenerator.cs @@ -159,7 +159,7 @@ public virtual async Task GenerateAsync(Type type, IEnumerable TryHandleSpecialTypesAsync(Type type, TSch } private async Task GenerateArray( - TSchemaType schema, Type type, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) + TSchemaType schema, Type type, IEnumerable parentAttributes, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { +#pragma warning disable 1998 + typeDescription.ApplyType(schema); var itemType = type.GetEnumerableItemType(); if (itemType != null) { schema.Item = await GenerateWithReferenceAndNullabilityAsync( -#pragma warning disable 1998 - itemType, null, false, schemaResolver, async (s, r) => -#pragma warning restore 1998 + itemType, null, parentAttributes?.OfType().Any() == true, schemaResolver, async (s, r) => { if (Settings.GenerateXmlObjects) + { s.GenerateXmlObjectForItemType(itemType); + } }).ConfigureAwait(false); if (Settings.GenerateXmlObjects) + { schema.GenerateXmlObjectForArrayType(type); + } } else + { schema.Item = JsonSchema4.CreateAnySchema(); + } + +#pragma warning restore 1998 } private async Task GenerateEnum( From d2a35cbf0a2015d7f2c76903268696aee37c6313 Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Fri, 1 Mar 2019 17:40:45 +0100 Subject: [PATCH 4/4] v9.13.20 --- .../NJsonSchema.CodeGeneration.CSharp.csproj | 2 +- .../NJsonSchema.CodeGeneration.TypeScript.csproj | 2 +- .../NJsonSchema.CodeGeneration.csproj | 2 +- src/NJsonSchema.Yaml/NJsonSchema.Yaml.csproj | 2 +- src/NJsonSchema/NJsonSchema.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NJsonSchema.CodeGeneration.CSharp/NJsonSchema.CodeGeneration.CSharp.csproj b/src/NJsonSchema.CodeGeneration.CSharp/NJsonSchema.CodeGeneration.CSharp.csproj index b8d06b67d..aea22d96f 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.19 + 9.13.20 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/NJsonSchema.CodeGeneration.TypeScript.csproj b/src/NJsonSchema.CodeGeneration.TypeScript/NJsonSchema.CodeGeneration.TypeScript.csproj index 98c0c7a9f..ae01cf0dd 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.19 + 9.13.20 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md diff --git a/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj b/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj index 5e1cff433..6407d29b5 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.19 + 9.13.20 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 561941bb1..f939d44ae 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.19 + 9.13.20 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md diff --git a/src/NJsonSchema/NJsonSchema.csproj b/src/NJsonSchema/NJsonSchema.csproj index ca3e43aa9..00b7fef3c 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.19 + 9.13.20 json schema validation generator .net Copyright © Rico Suter, 2018 https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md