From 05bd865bfc9e885950e89de02ffde8cee26ddf06 Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Fri, 2 Nov 2018 10:07:49 +0100 Subject: [PATCH] Dereference actual type schema (#798) * Dereference actual type schema * Fix and improve resolvers --- .../GeneralGeneratorTests.cs | 1 + .../InheritanceTests.cs | 1 + .../CSharpTypeResolver.cs | 10 ++-- .../Models/PropertyModel.cs | 2 +- .../AbstractGenerationTests.cs | 2 + .../ConstructorInterfaceTests.cs | 1 + .../DataConversionGenerator.cs | 49 ++++++++++--------- .../Models/PropertyModel.cs | 10 ++-- .../TypeScriptTypeResolver.cs | 2 +- .../TypeResolverBase.cs | 8 +++ src/NJsonSchema/JsonSchema4.Reference.cs | 7 +-- 11 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs index 5b6fa72ec..b3c02e5ba 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/GeneralGeneratorTests.cs @@ -419,6 +419,7 @@ public async Task When_property_is_object_then_object_property_is_generated() { //// Arrange var schema = await JsonSchema4.FromTypeAsync(); + var json = schema.ToJson(); //// Act var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco }); diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs index 2bb212293..0d2e717e7 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/InheritanceTests.cs @@ -34,6 +34,7 @@ public async Task When_empty_class_inherits_from_dictionary_then_allOf_inheritan Assert.Equal(0, dschema.AllOf.Count); Assert.True(dschema.IsDictionary); + Assert.DoesNotContain("class CustomDictionary :", code); Assert.Contains("public EmptyClassInheritingDictionary CustomDictionary", code); Assert.Contains("public partial class EmptyClassInheritingDictionary : System.Collections.Generic.Dictionary", code); } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs index 6660dcbf4..5eb73c297 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs @@ -6,6 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System; using System.Linq; namespace NJsonSchema.CodeGeneration.CSharp @@ -54,12 +55,15 @@ public override string Resolve(JsonSchema4 schema, bool isNullable, string typeN /// The type name. public string Resolve(JsonSchema4 schema, bool isNullable, string typeNameHint, bool checkForExistingSchema) { - schema = schema.ActualSchema; + if (schema == null) + throw new ArgumentNullException(nameof(schema)); + + schema = GetResolvableSchema(schema); if (schema == ExceptionSchema) return "System.Exception"; - if (schema.IsAnyType) + if (schema.ActualTypeSchema.IsAnyType) return "object"; var type = schema.Type; @@ -178,7 +182,7 @@ private string ResolveArrayOrTuple(JsonSchema4 schema) if (schema.Items != null && schema.Items.Count > 0) { var tupleTypes = schema.Items - .Select(i => Resolve(i.ActualSchema, false, null)) + .Select(i => Resolve(i, false, null)) .ToArray(); return string.Format("System.Tuple<" + string.Join(", ", tupleTypes) + ">"); diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs index 5789f5dbb..038eee1db 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/PropertyModel.cs @@ -39,7 +39,7 @@ public PropertyModel( public string Name => _property.Name; /// Gets the type of the property. - public override string Type => _resolver.Resolve(_property.ActualTypeSchema, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()); + public override string Type => _resolver.Resolve(_property, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()); /// Gets a value indicating whether the property has a description. public bool HasDescription => !string.IsNullOrEmpty(_property.Description); diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/AbstractGenerationTests.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/AbstractGenerationTests.cs index 74c8b5a8c..14b309c80 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/AbstractGenerationTests.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/AbstractGenerationTests.cs @@ -44,12 +44,14 @@ public async Task When_property_is_required_and_abstract_then_it_is_not_instanti { /// Arrange var schema = await JsonSchema4.FromTypeAsync(); + var json = schema.ToJson(); /// Act var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings { TypeScriptVersion = 2.0m }); var code = generator.GenerateFile("ContainerClass"); /// Assert + Assert.Contains("foo: AbstractClass", code); Assert.Contains("this.foo = data[\"Foo\"] ? AbstractClass.fromJS(data[\"Foo\"]) : undefined;", code); } diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ConstructorInterfaceTests.cs b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ConstructorInterfaceTests.cs index 8827374eb..058f618f5 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ConstructorInterfaceTests.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/ConstructorInterfaceTests.cs @@ -54,6 +54,7 @@ public async Task When_constructor_interface_and_conversion_code_is_generated_th { //// Arrange var schema = await JsonSchema4.FromTypeAsync(new JsonSchemaGeneratorSettings()); + var json = schema.ToJson(); //// Act var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs b/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs index dce48f0e9..70b2a3702 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs @@ -34,10 +34,11 @@ private static object CreateModel(DataConversionParameters parameters) var type = parameters.Resolver.Resolve(parameters.Schema, parameters.IsPropertyNullable, parameters.TypeNameHint); var valueGenerator = parameters.Settings.ValueGenerator; - var dictionaryValueType = parameters.Resolver.TryResolve(parameters.Schema.AdditionalPropertiesSchema, parameters.TypeNameHint) ?? "any"; - var dictionaryValueDefaultValue = parameters.Schema.AdditionalPropertiesSchema != null - ? valueGenerator.GetDefaultValue(parameters.Schema.AdditionalPropertiesSchema, - parameters.Schema.AdditionalPropertiesSchema.IsNullable(parameters.Settings.SchemaType), dictionaryValueType, parameters.TypeNameHint, + var typeSchema = parameters.Schema.ActualTypeSchema; + var dictionaryValueType = parameters.Resolver.TryResolve(typeSchema.AdditionalPropertiesSchema, parameters.TypeNameHint) ?? "any"; + var dictionaryValueDefaultValue = typeSchema.AdditionalPropertiesSchema != null + ? valueGenerator.GetDefaultValue(typeSchema.AdditionalPropertiesSchema, + typeSchema.AdditionalPropertiesSchema.IsNullable(parameters.Settings.SchemaType), dictionaryValueType, parameters.TypeNameHint, parameters.Settings.GenerateDefaultValues, parameters.Resolver) : null; @@ -48,47 +49,47 @@ private static object CreateModel(DataConversionParameters parameters) Variable = parameters.Variable, Value = parameters.Value, - HasDefaultValue = valueGenerator.GetDefaultValue(parameters.Schema, + HasDefaultValue = valueGenerator.GetDefaultValue(typeSchema, parameters.IsPropertyNullable, type, parameters.TypeNameHint, parameters.Settings.GenerateDefaultValues, parameters.Resolver) != null, - DefaultValue = valueGenerator.GetDefaultValue(parameters.Schema, + DefaultValue = valueGenerator.GetDefaultValue(typeSchema, parameters.IsPropertyNullable, type, parameters.TypeNameHint, parameters.Settings.GenerateDefaultValues, parameters.Resolver), Type = type, - IsNewableObject = IsNewableObject(parameters.Schema), - IsDate = IsDate(parameters.Schema.Format, parameters.Settings.DateTimeType), - IsDateTime = IsDateTime(parameters.Schema.Format, parameters.Settings.DateTimeType), + IsNewableObject = IsNewableObject(typeSchema), + IsDate = IsDate(typeSchema.Format, parameters.Settings.DateTimeType), + IsDateTime = IsDateTime(typeSchema.Format, parameters.Settings.DateTimeType), - IsDictionary = parameters.Schema.IsDictionary, + IsDictionary = typeSchema.IsDictionary, DictionaryValueType = dictionaryValueType, DictionaryValueDefaultValue = dictionaryValueDefaultValue, HasDictionaryValueDefaultValue = dictionaryValueDefaultValue != null, - IsDictionaryValueNewableObject = parameters.Schema.AdditionalPropertiesSchema != null && IsNewableObject(parameters.Schema.AdditionalPropertiesSchema), - IsDictionaryValueDate = IsDate(parameters.Schema.AdditionalPropertiesSchema?.ActualSchema?.Format, parameters.Settings.DateTimeType), - IsDictionaryValueDateTime = IsDateTime(parameters.Schema.AdditionalPropertiesSchema?.ActualSchema?.Format, parameters.Settings.DateTimeType), - IsDictionaryValueNewableArray = parameters.Schema.AdditionalPropertiesSchema?.ActualSchema?.IsArray == true && - IsNewableObject(parameters.Schema.AdditionalPropertiesSchema.Item), - DictionaryValueArrayItemType = parameters.Schema.AdditionalPropertiesSchema?.ActualSchema?.IsArray == true ? - parameters.Resolver.TryResolve(parameters.Schema.AdditionalPropertiesSchema.Item, "Anonymous") ?? "any" : "any", + IsDictionaryValueNewableObject = typeSchema.AdditionalPropertiesSchema != null && IsNewableObject(typeSchema.AdditionalPropertiesSchema), + IsDictionaryValueDate = IsDate(typeSchema.AdditionalPropertiesSchema?.ActualSchema?.Format, parameters.Settings.DateTimeType), + IsDictionaryValueDateTime = IsDateTime(typeSchema.AdditionalPropertiesSchema?.ActualSchema?.Format, parameters.Settings.DateTimeType), + IsDictionaryValueNewableArray = typeSchema.AdditionalPropertiesSchema?.ActualSchema?.IsArray == true && + IsNewableObject(typeSchema.AdditionalPropertiesSchema.Item), + DictionaryValueArrayItemType = typeSchema.AdditionalPropertiesSchema?.ActualSchema?.IsArray == true ? + parameters.Resolver.TryResolve(typeSchema.AdditionalPropertiesSchema.Item, "Anonymous") ?? "any" : "any", - IsArray = parameters.Schema.IsArray, - ArrayItemType = parameters.Resolver.TryResolve(parameters.Schema.Item, parameters.TypeNameHint) ?? "any", - IsArrayItemNewableObject = parameters.Schema.Item != null && IsNewableObject(parameters.Schema.Item), - IsArrayItemDate = IsDate(parameters.Schema.Item?.Format, parameters.Settings.DateTimeType), - IsArrayItemDateTime = IsDateTime(parameters.Schema.Item?.Format, parameters.Settings.DateTimeType), + IsArray = typeSchema.IsArray, + ArrayItemType = parameters.Resolver.TryResolve(typeSchema.Item, parameters.TypeNameHint) ?? "any", + IsArrayItemNewableObject = typeSchema.Item != null && IsNewableObject(typeSchema.Item), + IsArrayItemDate = IsDate(typeSchema.Item?.Format, parameters.Settings.DateTimeType), + IsArrayItemDateTime = IsDateTime(typeSchema.Item?.Format, parameters.Settings.DateTimeType), //StringToDateCode is used for date and date-time formats UseJsDate = parameters.Settings.DateTimeType == TypeScriptDateTimeType.Date, StringToDateCode = parameters.Settings.DateTimeType == TypeScriptDateTimeType.Date ? "new Date" : (parameters.Settings.DateTimeType == TypeScriptDateTimeType.MomentJS || parameters.Settings.DateTimeType == TypeScriptDateTimeType.OffsetMomentJS) && - parameters.Schema.Format == JsonFormatStrings.TimeSpan ? "moment.duration" : + typeSchema.Format == JsonFormatStrings.TimeSpan ? "moment.duration" : parameters.Settings.DateTimeType == TypeScriptDateTimeType.OffsetMomentJS ? "moment.parseZone" : "moment", DateTimeToStringCode = (parameters.Settings.DateTimeType == TypeScriptDateTimeType.MomentJS || parameters.Settings.DateTimeType == TypeScriptDateTimeType.OffsetMomentJS) && - parameters.Schema.Format == JsonFormatStrings.TimeSpan ? "format('d.hh:mm:ss.SS', { trim: false })" : + typeSchema.Format == JsonFormatStrings.TimeSpan ? "format('d.hh:mm:ss.SS', { trim: false })" : parameters.Settings.DateTimeType == TypeScriptDateTimeType.OffsetMomentJS ? "toISOString(true)" : "toISOString()", HandleReferences = parameters.Settings.HandleReferences diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/Models/PropertyModel.cs b/src/NJsonSchema.CodeGeneration.TypeScript/Models/PropertyModel.cs index 3c4518822..ebd6c7bd1 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/Models/PropertyModel.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/Models/PropertyModel.cs @@ -51,11 +51,11 @@ public PropertyModel( public string Description => _property.Description; /// Gets the type of the property. - public override string Type => _resolver.Resolve(_property.ActualTypeSchema, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()); + public override string Type => _resolver.Resolve(_property, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()); /// Gets the type of the property in the initializer interface. public string ConstructorInterfaceType => _settings.ConvertConstructorInterfaceData ? - _resolver.ResolveConstructorInterfaceName(_property.ActualTypeSchema, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()) : + _resolver.ResolveConstructorInterfaceName(_property, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()) : Type; /// Gets a value indicating whether constructor conversion is supported. @@ -78,7 +78,7 @@ public bool SupportsConstructorConversion _property.ActualTypeSchema?.AdditionalPropertiesSchema.ActualSchema.Type.HasFlag(JsonObjectType.Object) == true; } - return _resolver.SupportsConstructorConversion(_property.ActualTypeSchema) && + return _resolver.SupportsConstructorConversion(_property) && !_property.ActualTypeSchema.IsTuple; } } @@ -129,7 +129,7 @@ public string ConvertToClassCode Variable = typeStyle == TypeScriptTypeStyle.Class ? (IsReadOnly ? "(this)." : "this.") + PropertyName : PropertyName + "_", Value = "data[\"" + _property.Name + "\"]", - Schema = _property.ActualTypeSchema, + Schema = _property.ActualSchema, IsPropertyNullable = _property.IsNullable(_settings.SchemaType), TypeNameHint = PropertyName, Resolver = _resolver, @@ -154,7 +154,7 @@ public string ConvertToJavaScriptCode { Variable = "data[\"" + _property.Name + "\"]", Value = typeStyle == TypeScriptTypeStyle.Class ? "this." + PropertyName : PropertyName + "_", - Schema = _property.ActualTypeSchema, + Schema = _property.ActualSchema, IsPropertyNullable = _property.IsNullable(_settings.SchemaType), TypeNameHint = PropertyName, Resolver = _resolver, diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs index d1873b397..149df471b 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs @@ -63,7 +63,7 @@ private string Resolve(JsonSchema4 schema, string typeNameHint, bool addInterfac if (schema == null) throw new ArgumentNullException(nameof(schema)); - schema = schema.ActualSchema; + schema = GetResolvableSchema(schema); if (schema.IsAnyType) return "any"; diff --git a/src/NJsonSchema.CodeGeneration/TypeResolverBase.cs b/src/NJsonSchema.CodeGeneration/TypeResolverBase.cs index d67feac60..ac8c96452 100644 --- a/src/NJsonSchema.CodeGeneration/TypeResolverBase.cs +++ b/src/NJsonSchema.CodeGeneration/TypeResolverBase.cs @@ -79,6 +79,14 @@ public void RegisterSchemaDefinitions(IDictionary definitio } } + /// Gets the actual schema and removes a nullable oneOf reference if available. + /// The schema. + /// The actually resolvable schema + protected JsonSchema4 GetResolvableSchema(JsonSchema4 schema) + { + return schema.OneOf.FirstOrDefault(o => !o.IsNullable(SchemaType.JsonSchema))?.ActualSchema ?? schema.ActualSchema; + } + /// Checks whether the given schema should generate a type. /// The schema. /// True if the schema should generate a type. diff --git a/src/NJsonSchema/JsonSchema4.Reference.cs b/src/NJsonSchema/JsonSchema4.Reference.cs index cc19d9495..864486f3e 100644 --- a/src/NJsonSchema/JsonSchema4.Reference.cs +++ b/src/NJsonSchema/JsonSchema4.Reference.cs @@ -32,12 +32,13 @@ public virtual JsonSchema4 ActualTypeSchema { get { - if (AllOf.Count > 1 && AllOf.Count(s => !s.HasReference && !s.IsDictionary) == 1) + var schema = Reference != null ? Reference : this; + if (schema.AllOf.Count > 1 && schema.AllOf.Count(s => !s.HasReference && !s.IsDictionary) == 1) { - return AllOf.First(s => !s.HasReference && !s.IsDictionary); + return schema.AllOf.First(s => !s.HasReference && !s.IsDictionary).ActualSchema; } - return OneOf.FirstOrDefault(o => !o.IsNullable(SchemaType.JsonSchema))?.ActualSchema ?? ActualSchema; + return schema.OneOf.FirstOrDefault(o => !o.IsNullable(SchemaType.JsonSchema))?.ActualSchema ?? ActualSchema; } }