private void LoadEnumerations(Type type, JsonSchema4 schema, JsonTypeDescription typeDescription) { schema.Type = typeDescription.Type; schema.Enumeration.Clear(); schema.EnumerationNames.Clear(); var underlyingType = Enum.GetUnderlyingType(type); foreach (var enumName in Enum.GetNames(type)) { if (typeDescription.Type == JsonObjectType.Integer) { var value = Convert.ChangeType(Enum.Parse(type, enumName), underlyingType); schema.Enumeration.Add(value); } else { var attributes = type.GetTypeInfo().GetDeclaredField(enumName).GetCustomAttributes(); // EnumMember only checked if StringEnumConverter is used dynamic enumMemberAttribute = attributes.TryGetIfAssignableTo("System.Runtime.Serialization.EnumMemberAttribute"); if (enumMemberAttribute != null && !string.IsNullOrEmpty(enumMemberAttribute.Value)) { schema.Enumeration.Add((string)enumMemberAttribute.Value); } else { schema.Enumeration.Add(enumName); } } schema.EnumerationNames.Add(enumName); } }
private async Task GenerateEnum <TSchemaType>( TSchemaType schema, Type type, IEnumerable <Attribute> parentAttributes, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { if (type.Name == "Nullable`1") #if !LEGACY { type = type.GenericTypeArguments[0]; } #else { type = type.GetGenericArguments()[0]; } #endif var isIntegerEnumeration = typeDescription.Type == JsonObjectType.Integer; if (schemaResolver.HasSchema(type, isIntegerEnumeration)) { schema.Reference = schemaResolver.GetSchema(type, isIntegerEnumeration); } else if (schema.GetType() == typeof(JsonSchema4)) { LoadEnumerations(type, schema, typeDescription); typeDescription.ApplyType(schema); schema.Description = await type.GetXmlSummaryAsync().ConfigureAwait(false); schemaResolver.AddSchema(type, isIntegerEnumeration, schema); } else { schema.Reference = await GenerateAsync(type, parentAttributes, schemaResolver).ConfigureAwait(false); } }
private async Task GenerateArray <TSchemaType>( TSchemaType schema, Type type, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { typeDescription.ApplyType(schema); var itemType = type.GetEnumerableItemType(); if (itemType != null) { schema.Item = await GenerateWithReferenceAndNullability <JsonSchema4>( #pragma warning disable 1998 itemType, null, false, schemaResolver, async (s, r) => #pragma warning restore 1998 { if (Settings.GenerateXmlObjects) { s.GenerateXmlObjectForItemType(itemType); } }).ConfigureAwait(false); } else { schema.Item = JsonSchema4.CreateAnySchema(); } }
/// <summary>Creates a <see cref="JsonTypeDescription"/> from a <see cref="Type"/>. </summary> /// <param name="contextualType">The type.</param> /// <param name="defaultReferenceTypeNullHandling">The default reference type null handling used when no nullability information is available.</param> /// <param name="settings">The settings.</param> /// <returns>The <see cref="JsonTypeDescription"/>. </returns> public virtual JsonTypeDescription GetDescription(ContextualType contextualType, ReferenceTypeNullHandling defaultReferenceTypeNullHandling, JsonSchemaGeneratorSettings settings) { var type = contextualType.OriginalType; var isNullable = IsNullable(contextualType, defaultReferenceTypeNullHandling); var jsonSchemaTypeAttribute = contextualType.GetAttribute <JsonSchemaTypeAttribute>(); if (jsonSchemaTypeAttribute != null) { type = jsonSchemaTypeAttribute.Type; contextualType = type.ToContextualType(); if (jsonSchemaTypeAttribute.IsNullableRaw.HasValue) { isNullable = jsonSchemaTypeAttribute.IsNullableRaw.Value; } } var jsonSchemaAttribute = contextualType.GetAttribute <JsonSchemaAttribute>(); if (jsonSchemaAttribute != null) { var classType = jsonSchemaAttribute.Type != JsonObjectType.None ? jsonSchemaAttribute.Type : JsonObjectType.Object; var format = !string.IsNullOrEmpty(jsonSchemaAttribute.Format) ? jsonSchemaAttribute.Format : null; return(JsonTypeDescription.Create(contextualType, classType, isNullable, format)); } if (type.GetTypeInfo().IsEnum) { var isStringEnum = IsStringEnum(contextualType, settings.ActualSerializerSettings); return(JsonTypeDescription.CreateForEnumeration(contextualType, isStringEnum ? JsonObjectType.String : JsonObjectType.Integer, false)); } // Primitive types if (type == typeof(short) || type == typeof(uint) || type == typeof(ushort)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, null)); } if (type == typeof(int)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, JsonFormatStrings.Integer)); } if (type == typeof(long) || type == typeof(ulong)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, JsonFormatStrings.Long)); } if (type == typeof(double)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Number, false, JsonFormatStrings.Double)); } if (type == typeof(float)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Number, false, JsonFormatStrings.Float)); } if (type == typeof(decimal)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Number, false, JsonFormatStrings.Decimal)); } if (type == typeof(bool)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Boolean, false, null)); } if (type == typeof(string) || type == typeof(Type)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, null)); } if (type == typeof(char)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, null)); } if (type == typeof(Guid)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, JsonFormatStrings.Guid)); } // Date & time types if (type == typeof(DateTime) || type == typeof(DateTimeOffset) || type.FullName == "NodaTime.OffsetDateTime" || type.FullName == "NodaTime.LocalDateTime" || type.FullName == "NodaTime.ZonedDateTime" || type.FullName == "NodaTime.Instant") { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, JsonFormatStrings.DateTime)); } if (type == typeof(TimeSpan) || type.FullName == "NodaTime.Duration") { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, JsonFormatStrings.TimeSpan)); } if (type.FullName == "NodaTime.LocalDate") { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, JsonFormatStrings.Date)); } if (type.FullName == "NodaTime.LocalTime") { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, JsonFormatStrings.Time)); } // Special types if (type == typeof(Uri)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, JsonFormatStrings.Uri)); } if (type == typeof(byte)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, JsonFormatStrings.Byte)); } if (type == typeof(byte[])) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, JsonFormatStrings.Byte)); } if (type.IsAssignableToTypeName(nameof(JArray), TypeNameStyle.Name)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Array, isNullable, null)); } if (type.IsAssignableToTypeName(nameof(JToken), TypeNameStyle.Name) || type.FullName == "System.Dynamic.ExpandoObject" || type.FullName == "System.Text.Json.JsonElement" || type == typeof(object)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.None, isNullable, null)); } if (IsBinary(contextualType)) { if (settings.SchemaType == SchemaType.Swagger2) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.File, isNullable, null)); } else { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, JsonFormatStrings.Binary)); } } if (contextualType.IsNullableType) { var typeDescription = GetDescription(contextualType.OriginalGenericArguments[0], defaultReferenceTypeNullHandling, settings); typeDescription.IsNullable = true; return(typeDescription); } var contract = settings.ResolveContract(type); if (IsDictionaryType(contextualType) && contract is JsonDictionaryContract) { return(JsonTypeDescription.CreateForDictionary(contextualType, JsonObjectType.Object, isNullable)); } // TODO: Don't trust JsonArrayContract when contextualType is IAsyncEnumerable<T> until it is fixed if (IsIAsyncEnumerableType(contextualType) || (IsArrayType(contextualType) && contract is JsonArrayContract)) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.Array, isNullable, null)); } if (contract is JsonStringContract) { return(JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, null)); } return(JsonTypeDescription.Create(contextualType, JsonObjectType.Object, isNullable, null)); }
/// <summary>Applies the property annotations to the JSON property.</summary> /// <param name="schema">The schema.</param> /// <param name="typeDescription">The property type description.</param> /// <param name="parentAttributes">The attributes.</param> public virtual void ApplyDataAnnotations(JsonSchema4 schema, JsonTypeDescription typeDescription, IEnumerable <Attribute> parentAttributes) { // TODO: Refactor out dynamic displayAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.DisplayAttribute"); if (displayAttribute != null && displayAttribute.Name != null) { schema.Title = displayAttribute.Name; } dynamic defaultValueAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DefaultValueAttribute"); if (defaultValueAttribute != null) { schema.Default = defaultValueAttribute.Value; } dynamic regexAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RegularExpressionAttribute"); if (regexAttribute != null) { if (typeDescription.IsDictionary) { schema.AdditionalPropertiesSchema.Pattern = regexAttribute.Pattern; } else { schema.Pattern = regexAttribute.Pattern; } } if (typeDescription.Type == JsonObjectType.Number || typeDescription.Type == JsonObjectType.Integer) { dynamic rangeAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RangeAttribute"); if (rangeAttribute != null) { if (rangeAttribute.Minimum != null && rangeAttribute.Minimum > double.MinValue) { schema.Minimum = (decimal?)(double)rangeAttribute.Minimum; } if (rangeAttribute.Maximum != null && rangeAttribute.Maximum < double.MaxValue) { schema.Maximum = (decimal?)(double)rangeAttribute.Maximum; } } var multipleOfAttribute = parentAttributes.OfType <MultipleOfAttribute>().SingleOrDefault(); if (multipleOfAttribute != null) { schema.MultipleOf = multipleOfAttribute.MultipleOf; } } dynamic minLengthAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.MinLengthAttribute"); if (minLengthAttribute != null && minLengthAttribute.Length != null) { if (typeDescription.Type == JsonObjectType.String) { schema.MinLength = minLengthAttribute.Length; } else if (typeDescription.Type == JsonObjectType.Array) { schema.MinItems = minLengthAttribute.Length; } } dynamic maxLengthAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.MaxLengthAttribute"); if (maxLengthAttribute != null && maxLengthAttribute.Length != null) { if (typeDescription.Type == JsonObjectType.String) { schema.MaxLength = maxLengthAttribute.Length; } else if (typeDescription.Type == JsonObjectType.Array) { schema.MaxItems = maxLengthAttribute.Length; } } dynamic stringLengthAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.StringLengthAttribute"); if (stringLengthAttribute != null) { if (typeDescription.Type == JsonObjectType.String) { schema.MinLength = stringLengthAttribute.MinimumLength; schema.MaxLength = stringLengthAttribute.MaximumLength; } } dynamic dataTypeAttribute = parentAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.DataTypeAttribute"); if (dataTypeAttribute != null) { var dataType = dataTypeAttribute.DataType.ToString(); if (DataTypeFormats.ContainsKey(dataType)) { schema.Format = DataTypeFormats[dataType]; } } }
/// <summary>Creates a <see cref="JsonTypeDescription"/> from a <see cref="Type"/>. </summary> /// <param name="type">The type. </param> /// <param name="parentAttributes">The parent's attributes (i.e. parameter or property attributes).</param> /// <param name="defaultReferenceTypeNullHandling">The default reference type null handling used when no nullability information is available.</param> /// <param name="settings">The settings.</param> /// <returns>The <see cref="JsonTypeDescription"/>. </returns> public virtual JsonTypeDescription GetDescription(Type type, IEnumerable <Attribute> parentAttributes, ReferenceTypeNullHandling defaultReferenceTypeNullHandling, JsonSchemaGeneratorSettings settings) { var isNullable = IsNullable(type, parentAttributes, defaultReferenceTypeNullHandling); var jsonSchemaTypeAttribute = type.GetTypeInfo().GetCustomAttribute <JsonSchemaTypeAttribute>() ?? parentAttributes?.OfType <JsonSchemaTypeAttribute>().SingleOrDefault(); if (jsonSchemaTypeAttribute != null) { type = jsonSchemaTypeAttribute.Type; if (jsonSchemaTypeAttribute.IsNullableRaw.HasValue) { isNullable = jsonSchemaTypeAttribute.IsNullableRaw.Value; } } var jsonSchemaAttribute = type.GetTypeInfo().GetCustomAttribute <JsonSchemaAttribute>() ?? parentAttributes?.OfType <JsonSchemaAttribute>().SingleOrDefault(); if (jsonSchemaAttribute != null) { var classType = jsonSchemaAttribute.Type != JsonObjectType.None ? jsonSchemaAttribute.Type : JsonObjectType.Object; var format = !string.IsNullOrEmpty(jsonSchemaAttribute.Format) ? jsonSchemaAttribute.Format : null; return(JsonTypeDescription.Create(type, classType, isNullable, format)); } if (type.GetTypeInfo().IsEnum) { var isStringEnum = IsStringEnum(type, parentAttributes, settings); return(JsonTypeDescription.CreateForEnumeration(type, isStringEnum ? JsonObjectType.String : JsonObjectType.Integer, false)); } if (type == typeof(short) || type == typeof(uint) || type == typeof(ushort)) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, null)); } if (type == typeof(int)) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, JsonFormatStrings.Integer)); } if (type == typeof(long) || type == typeof(ulong)) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, JsonFormatStrings.Long)); } if (type == typeof(double) || type == typeof(float)) { return(JsonTypeDescription.Create(type, JsonObjectType.Number, false, JsonFormatStrings.Double)); } if (type == typeof(decimal)) { return(JsonTypeDescription.Create(type, JsonObjectType.Number, false, JsonFormatStrings.Decimal)); } if (type == typeof(bool)) { return(JsonTypeDescription.Create(type, JsonObjectType.Boolean, false, null)); } if (type == typeof(string) || type == typeof(Type)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, null)); } if (type == typeof(char)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, null)); } if (type == typeof(Guid)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.Guid)); } if (type == typeof(DateTime) || type == typeof(DateTimeOffset) || type.FullName == "NodaTime.OffsetDateTime" || type.FullName == "NodaTime.LocalDateTime" || type.FullName == "NodaTime.ZonedDateTime" || type.FullName == "NodaTime.Instant") { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.DateTime)); } if (type == typeof(TimeSpan) || type.FullName == "NodaTime.Duration") { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.TimeSpan)); } if (type.FullName == "NodaTime.LocalDate") { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.Date)); } if (type.FullName == "NodaTime.LocalTime") { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.Time)); } if (type == typeof(Uri)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, JsonFormatStrings.Uri)); } if (type == typeof(byte)) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, JsonFormatStrings.Byte)); } if (type == typeof(byte[])) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, JsonFormatStrings.Byte)); } if (type.IsAssignableTo(nameof(JArray), TypeNameStyle.Name)) { return(JsonTypeDescription.Create(type, JsonObjectType.Array, isNullable, null)); } if (type.IsAssignableTo(nameof(JToken), TypeNameStyle.Name) || type.FullName == "System.Dynamic.ExpandoObject" || type == typeof(object)) { return(JsonTypeDescription.Create(type, JsonObjectType.None, isNullable, null)); } if (IsBinary(type, parentAttributes)) { if (settings.SchemaType == SchemaType.Swagger2) { return(JsonTypeDescription.Create(type, JsonObjectType.File, isNullable, null)); } else { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, JsonFormatStrings.Binary)); } } var contract = settings.ResolveContract(type); if (IsDictionaryType(type, parentAttributes) && contract is JsonDictionaryContract) { return(JsonTypeDescription.CreateForDictionary(type, JsonObjectType.Object, isNullable)); } if (IsArrayType(type, parentAttributes) && contract is JsonArrayContract) { return(JsonTypeDescription.Create(type, JsonObjectType.Array, isNullable, null)); } if (type.Name == "Nullable`1") { #if !LEGACY // Remove JsonSchemaTypeAttributes to avoid stack overflows var typeDescription = GetDescription(type.GenericTypeArguments[0], parentAttributes?.Where(a => !(a is JsonSchemaTypeAttribute)), defaultReferenceTypeNullHandling, settings); #else var typeDescription = GetDescription(type.GetGenericArguments()[0], parentAttributes?.Where(a => !(a is JsonSchemaTypeAttribute)), defaultReferenceTypeNullHandling, settings); #endif typeDescription.IsNullable = true; return(typeDescription); } if (contract is JsonStringContract) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, null)); } return(JsonTypeDescription.Create(type, JsonObjectType.Object, isNullable, null)); }
/// <summary>Creates a <see cref="JsonTypeDescription"/> from a <see cref="Type"/>. </summary> /// <param name="type">The type. </param> /// <param name="parentAttributes">The parent's attributes (i.e. parameter or property attributes).</param> /// <param name="settings">The settings.</param> /// <returns>The <see cref="JsonTypeDescription"/>. </returns> public virtual JsonTypeDescription GetDescription(Type type, IEnumerable <Attribute> parentAttributes, JsonSchemaGeneratorSettings settings) { if (type.GetTypeInfo().IsEnum) { var isStringEnum = IsStringEnum(type, parentAttributes, settings.DefaultEnumHandling); return(JsonTypeDescription.CreateForEnumeration(type, isStringEnum ? JsonObjectType.String : JsonObjectType.Integer, false)); } if (type == typeof(short) || type == typeof(uint) || type == typeof(ushort)) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, null)); } if (type == typeof(int)) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, JsonFormatStrings.Integer)); } if ((type == typeof(long)) || (type == typeof(ulong))) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, JsonFormatStrings.Long)); } if (type == typeof(double) || type == typeof(float)) { return(JsonTypeDescription.Create(type, JsonObjectType.Number, false, JsonFormatStrings.Double)); } if (type == typeof(decimal)) { return(JsonTypeDescription.Create(type, JsonObjectType.Number, false, JsonFormatStrings.Decimal)); } if (type == typeof(bool)) { return(JsonTypeDescription.Create(type, JsonObjectType.Boolean, false, null)); } var isNullable = IsNullable(type, parentAttributes, settings); if (type == typeof(string) || type == typeof(Type)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, null)); } if (type == typeof(char)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, null)); } if (type == typeof(Guid)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.Guid)); } if (type == typeof(DateTime) || type == typeof(DateTimeOffset) || type.FullName == "NodaTime.OffsetDateTime" || type.FullName == "NodaTime.ZonedDateTime") { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.DateTime)); } if (type == typeof(TimeSpan) || type.FullName == "NodaTime.Duration") { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.TimeSpan)); } if (type.FullName == "NodaTime.LocalDate") { return(JsonTypeDescription.Create(type, JsonObjectType.String, false, JsonFormatStrings.Date)); } if (type == typeof(Uri)) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, JsonFormatStrings.Uri)); } if (type == typeof(byte)) { return(JsonTypeDescription.Create(type, JsonObjectType.Integer, false, JsonFormatStrings.Byte)); } if (type == typeof(byte[])) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, JsonFormatStrings.Byte)); } if (type == typeof(JObject) || type == typeof(JToken) || type == typeof(object)) { return(JsonTypeDescription.Create(type, JsonObjectType.None, isNullable, null)); } if (IsFileType(type, parentAttributes)) { return(JsonTypeDescription.Create(type, JsonObjectType.File, isNullable, null)); } var contract = settings.ResolveContract(type); if (IsDictionaryType(type, parentAttributes) && contract is JsonDictionaryContract) { return(JsonTypeDescription.CreateForDictionary(type, JsonObjectType.Object, isNullable)); } if (IsArrayType(type, parentAttributes) && contract is JsonArrayContract) { return(JsonTypeDescription.Create(type, JsonObjectType.Array, isNullable, null)); } if (type.Name == "Nullable`1") { #if !LEGACY var typeDescription = GetDescription(type.GenericTypeArguments[0], parentAttributes, settings); #else var typeDescription = GetDescription(type.GetGenericArguments()[0], parentAttributes, settings); #endif typeDescription.IsNullable = true; return(typeDescription); } var jsonSchemaAttribute = type.GetTypeInfo().GetCustomAttribute <JsonSchemaAttribute>() ?? parentAttributes?.OfType <JsonSchemaAttribute>().SingleOrDefault(); if (jsonSchemaAttribute != null) { var classType = jsonSchemaAttribute.Type != JsonObjectType.None ? jsonSchemaAttribute.Type : JsonObjectType.Object; var format = !string.IsNullOrEmpty(jsonSchemaAttribute.Format) ? jsonSchemaAttribute.Format : null; return(JsonTypeDescription.Create(type, classType, isNullable, format)); } if (contract is JsonStringContract) { return(JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, null)); } return(JsonTypeDescription.Create(type, JsonObjectType.Object, isNullable, null)); }