Skip to content

Commit

Permalink
Dereference actual type schema (#798)
Browse files Browse the repository at this point in the history
* Dereference actual type schema

* Fix and improve resolvers
  • Loading branch information
RicoSuter authored Nov 2, 2018
1 parent 3f9e6be commit 05bd865
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ public async Task When_property_is_object_then_object_property_is_generated()
{
//// Arrange
var schema = await JsonSchema4.FromTypeAsync<ObjectTestClass>();
var json = schema.ToJson();

//// Act
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, object>", code);
}
Expand Down
10 changes: 7 additions & 3 deletions src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// <author>Rico Suter, [email protected]</author>
//-----------------------------------------------------------------------

using System;
using System.Linq;

namespace NJsonSchema.CodeGeneration.CSharp
Expand Down Expand Up @@ -54,12 +55,15 @@ public override string Resolve(JsonSchema4 schema, bool isNullable, string typeN
/// <returns>The type name.</returns>
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;
Expand Down Expand Up @@ -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) + ">");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public PropertyModel(
public string Name => _property.Name;

/// <summary>Gets the type of the property.</summary>
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());

/// <summary>Gets a value indicating whether the property has a description.</summary>
public bool HasDescription => !string.IsNullOrEmpty(_property.Description);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ public async Task When_property_is_required_and_abstract_then_it_is_not_instanti
{
/// Arrange
var schema = await JsonSchema4.FromTypeAsync<ContainerClass>();
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\"]) : <any>undefined;", code);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public async Task When_constructor_interface_and_conversion_code_is_generated_th
{
//// Arrange
var schema = await JsonSchema4.FromTypeAsync<Person>(new JsonSchemaGeneratorSettings());
var json = schema.ToJson();

//// Act
var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ public PropertyModel(
public string Description => _property.Description;

/// <summary>Gets the type of the property.</summary>
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());

/// <summary>Gets the type of the property in the initializer interface.</summary>
public string ConstructorInterfaceType => _settings.ConvertConstructorInterfaceData ?
_resolver.ResolveConstructorInterfaceName(_property.ActualTypeSchema, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()) :
_resolver.ResolveConstructorInterfaceName(_property, _property.IsNullable(_settings.SchemaType), GetTypeNameHint()) :
Type;

/// <summary>Gets a value indicating whether constructor conversion is supported.</summary>
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -129,7 +129,7 @@ public string ConvertToClassCode
Variable = typeStyle == TypeScriptTypeStyle.Class ?
(IsReadOnly ? "(<any>this)." : "this.") + PropertyName : PropertyName + "_",
Value = "data[\"" + _property.Name + "\"]",
Schema = _property.ActualTypeSchema,
Schema = _property.ActualSchema,
IsPropertyNullable = _property.IsNullable(_settings.SchemaType),
TypeNameHint = PropertyName,
Resolver = _resolver,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
8 changes: 8 additions & 0 deletions src/NJsonSchema.CodeGeneration/TypeResolverBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ public void RegisterSchemaDefinitions(IDictionary<string, JsonSchema4> definitio
}
}

/// <summary>Gets the actual schema and removes a nullable oneOf reference if available.</summary>
/// <param name="schema">The schema.</param>
/// <returns>The actually resolvable schema</returns>
protected JsonSchema4 GetResolvableSchema(JsonSchema4 schema)
{
return schema.OneOf.FirstOrDefault(o => !o.IsNullable(SchemaType.JsonSchema))?.ActualSchema ?? schema.ActualSchema;
}

/// <summary>Checks whether the given schema should generate a type.</summary>
/// <param name="schema">The schema.</param>
/// <returns>True if the schema should generate a type.</returns>
Expand Down
7 changes: 4 additions & 3 deletions src/NJsonSchema/JsonSchema4.Reference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down

0 comments on commit 05bd865

Please sign in to comment.