/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <param name="type">The type.</param> /// <param name="parentAttributes">The parent property or parameter attributes.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public async Task <TSchemaType> GenerateAsync <TSchemaType>(Type type, IEnumerable <Attribute> parentAttributes, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { var schema = new TSchemaType(); await GenerateAsync(type, parentAttributes, schema, schemaResolver).ConfigureAwait(false); return(schema); }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <typeparam name="TSchemaType">The type of the schema.</typeparam> /// <param name="type">The type.</param> /// <param name="parentAttributes">The parent property or parameter attributes.</param> /// <param name="schema">The schema.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public virtual async Task GenerateAsync <TSchemaType>(Type type, IEnumerable <Attribute> parentAttributes, TSchemaType schema, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { if (await TryHandleSpecialTypesAsync(type, schema, schemaResolver, parentAttributes).ConfigureAwait(false)) { await ApplySchemaProcessorsAsync(type, schema, schemaResolver).ConfigureAwait(false); return; } if (schemaResolver.RootObject == schema) { schema.Title = Settings.SchemaNameGenerator.Generate(type); } ApplyExtensionDataAttributes(type, schema, parentAttributes); var typeDescription = Settings.ReflectionService.GetDescription(type, parentAttributes, Settings); if (typeDescription.Type.HasFlag(JsonObjectType.Object)) { if (typeDescription.IsDictionary) { typeDescription.ApplyType(schema); await GenerateDictionaryAsync(schema, type, schemaResolver).ConfigureAwait(false); } else { if (schemaResolver.HasSchema(type, false)) { schema.Reference = schemaResolver.GetSchema(type, false); } else if (schema.GetType() == typeof(JsonSchema4)) { typeDescription.ApplyType(schema); schema.Description = await type.GetTypeInfo().GetDescriptionAsync(type.GetTypeInfo().GetCustomAttributes()).ConfigureAwait(false); await GenerateObjectAsync(type, schema, schemaResolver).ConfigureAwait(false); } else { schema.Reference = await GenerateAsync(type, parentAttributes, schemaResolver).ConfigureAwait(false); } } } else if (typeDescription.IsEnum) { await GenerateEnum(schema, type, parentAttributes, typeDescription, schemaResolver).ConfigureAwait(false); } else if (typeDescription.Type.HasFlag(JsonObjectType.Array)) // TODO: Add support for tuples? { await GenerateArray(schema, type, typeDescription, schemaResolver).ConfigureAwait(false); } else { typeDescription.ApplyType(schema); } await ApplySchemaProcessorsAsync(type, schema, schemaResolver).ConfigureAwait(false); }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <typeparam name="TSchemaType">The type of the schema.</typeparam> /// <param name="type">The type.</param> /// <param name="parentAttributes">The parent property or parameter attributes.</param> /// <param name="schema">The schema.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public virtual async Task GenerateAsync <TSchemaType>(Type type, IEnumerable <Attribute> parentAttributes, TSchemaType schema, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { if (TryHandleSpecialTypes(type, schema, schemaResolver)) { return; } if (schemaResolver.RootObject == schema) { schema.Title = Settings.SchemaNameGenerator.Generate(type); } ApplyExtensionDataAttributes(type, schema, parentAttributes); var typeDescription = JsonObjectTypeDescription.FromType(type, parentAttributes, Settings.DefaultEnumHandling); if (typeDescription.Type.HasFlag(JsonObjectType.Object)) { if (typeDescription.IsDictionary) { typeDescription.ApplyType(schema); await GenerateDictionaryAsync(type, schema, schemaResolver).ConfigureAwait(false); } else { if (schemaResolver.HasSchema(type, false)) { schema.SchemaReference = schemaResolver.GetSchema(type, false); } else if (schema.GetType() == typeof(JsonSchema4)) { typeDescription.ApplyType(schema); schema.Description = await GetDescriptionAsync(type.GetTypeInfo(), type.GetTypeInfo().GetCustomAttributes()).ConfigureAwait(false); await GenerateObjectAsync(type, schema, schemaResolver).ConfigureAwait(false); } else { schema.SchemaReference = await GenerateAsync(type, parentAttributes, schemaResolver).ConfigureAwait(false); } } } else if (type.GetTypeInfo().IsEnum) { var isIntegerEnumeration = typeDescription.Type == JsonObjectType.Integer; if (schemaResolver.HasSchema(type, isIntegerEnumeration)) { schema.SchemaReference = 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.SchemaReference = await GenerateAsync(type, parentAttributes, schemaResolver).ConfigureAwait(false); } } else if (typeDescription.Type.HasFlag(JsonObjectType.Array)) { typeDescription.ApplyType(schema); var itemType = type.GetEnumerableItemType(); if (itemType == null) { var jsonSchemaAttribute = type.GetTypeInfo().GetCustomAttribute <JsonSchemaAttribute>(); if (jsonSchemaAttribute?.ArrayItem != null) { schema.Item = await GenerateWithReferenceAsync(schemaResolver, itemType).ConfigureAwait(false); } else { schema.Item = JsonSchema4.CreateAnySchema(); } } else { schema.Item = await GenerateWithReferenceAsync(schemaResolver, itemType).ConfigureAwait(false); } } else { typeDescription.ApplyType(schema); } }
private async Task GenerateInheritanceAsync(Type type, JsonSchema4 schema, JsonSchemaResolver schemaResolver) { GenerateInheritanceDiscriminator(type, schema); var baseType = type.GetTypeInfo().BaseType; if (baseType != null && baseType != typeof(object)) { if (baseType.GetTypeInfo().GetCustomAttributes(false).TryGetIfAssignableTo("JsonSchemaIgnoreAttribute", TypeNameStyle.Name) == null && baseType.GetTypeInfo().GetCustomAttributes(false).TryGetIfAssignableTo("SwaggerIgnoreAttribute", TypeNameStyle.Name) == null && Settings.ExcludedTypeNames?.Contains(baseType.FullName) != true) { if (Settings.FlattenInheritanceHierarchy) { var typeDescription = Settings.ReflectionService.GetDescription(baseType, null, Settings); if (!typeDescription.IsDictionary && !type.IsArray) { await GeneratePropertiesAndInheritanceAsync(baseType, schema, schemaResolver).ConfigureAwait(false); } } else { var baseSchema = await GenerateAsync(baseType, schemaResolver).ConfigureAwait(false); var baseTypeInfo = Settings.ReflectionService.GetDescription(baseType, null, Settings); if (baseTypeInfo.RequiresSchemaReference(Settings.TypeMappers)) { if (schemaResolver.RootObject != baseSchema.ActualSchema) { schemaResolver.AppendSchema(baseSchema.ActualSchema, Settings.SchemaNameGenerator.Generate(baseType)); } schema.AllOf.Add(new JsonSchema4 { Reference = baseSchema.ActualSchema }); } else { schema.AllOf.Add(baseSchema); } } } } if (Settings.FlattenInheritanceHierarchy && Settings.GenerateAbstractProperties) { #if !LEGACY foreach (var i in type.GetTypeInfo().ImplementedInterfaces) #else foreach (var i in type.GetTypeInfo().GetInterfaces()) #endif { var typeDescription = Settings.ReflectionService.GetDescription(i, null, Settings); if (!typeDescription.IsDictionary && !type.IsArray && !typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(i.GetTypeInfo())) { await GeneratePropertiesAndInheritanceAsync(i, schema, schemaResolver).ConfigureAwait(false); } } } }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <param name="type">The type.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public Task <JsonSchema4> GenerateAsync(Type type, JsonSchemaResolver schemaResolver) { return(GenerateAsync <JsonSchema4>(type, schemaResolver)); }
private async Task LoadPropertyOrFieldAsync(Newtonsoft.Json.Serialization.JsonProperty property, MemberInfo propertyInfo, Type parentType, JsonSchema4 parentSchema, JsonSchemaResolver schemaResolver) { var propertyType = property.PropertyType; var propertyAttributes = property.AttributeProvider.GetAttributes(true).ToArray(); var propertyTypeDescription = Settings.ReflectionService.GetDescription(propertyType, propertyAttributes, Settings); if (property.Ignored == false && IsPropertyIgnoredBySettings(propertyType, parentType, propertyAttributes) == false) { if (propertyType.Name == "Nullable`1") #if !LEGACY { propertyType = propertyType.GenericTypeArguments[0]; } #else { propertyType = propertyType.GetGenericArguments()[0]; } #endif var propertyName = GetPropertyName(property, propertyInfo); if (parentSchema.Properties.ContainsKey(propertyName)) { throw new InvalidOperationException("The JSON property '" + propertyName + "' is defined multiple times on type '" + parentType.FullName + "'."); } var requiredAttribute = propertyAttributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RequiredAttribute"); var hasJsonNetAttributeRequired = property.Required == Required.Always || property.Required == Required.AllowNull; var isDataContractMemberRequired = GetDataMemberAttribute(parentType, propertyAttributes)?.IsRequired == true; var hasRequiredAttribute = requiredAttribute != null; if (hasRequiredAttribute || isDataContractMemberRequired || hasJsonNetAttributeRequired) { parentSchema.RequiredProperties.Add(propertyName); } var isNullable = propertyTypeDescription.IsNullable && hasRequiredAttribute == false && (bool)isDataContractMemberRequired == false && (property.Required == Required.Default || property.Required == Required.AllowNull); var jsonProperty = await GenerateWithReferenceAndNullability <JsonProperty>( propertyType, propertyAttributes, isNullable, schemaResolver, async (p, s) => { if (Settings.GenerateXmlObjects) { p.GenerateXmlObjectForProperty(parentType, propertyName, propertyAttributes); } if (!isNullable && Settings.SchemaType == SchemaType.Swagger2) { if (!parentSchema.RequiredProperties.Contains(propertyName)) { parentSchema.RequiredProperties.Add(propertyName); } } dynamic readOnlyAttribute = propertyAttributes.TryGetIfAssignableTo("System.ComponentModel.ReadOnlyAttribute"); if (readOnlyAttribute != null) { p.IsReadOnly = readOnlyAttribute.IsReadOnly; } p.Description = await propertyInfo.GetDescriptionAsync(propertyAttributes).ConfigureAwait(false); p.Default = ConvertDefaultValue(property); ApplyDataAnnotations(p, propertyTypeDescription, propertyAttributes); }).ConfigureAwait(false); parentSchema.Properties.Add(propertyName, jsonProperty); } }
/// <summary>Generates the properties for the given type and schema.</summary> /// <typeparam name="TSchemaType">The type of the schema type.</typeparam> /// <param name="type">The types.</param> /// <param name="schema">The properties</param> /// <param name="schemaResolver">The schema resolver.</param> protected virtual void GenerateObject <TSchemaType>(Type type, TSchemaType schema, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { schemaResolver.AddSchema(type, false, schema); schema.AllowAdditionalProperties = false; GeneratePropertiesAndInheritance(type, schema, schemaResolver); if (Settings.GenerateKnownTypes) { GenerateKnownTypes(type, schemaResolver); } }
/// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> private async Task GenerateDictionaryAsync <TSchemaType>(TSchemaType schema, Type type, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { var genericTypeArguments = type.GetGenericTypeArguments(); var valueType = genericTypeArguments.Length == 2 ? genericTypeArguments[1] : typeof(object); if (valueType == typeof(object)) { schema.AdditionalPropertiesSchema = JsonSchema4.CreateAnySchema(); } else { var additionalPropertiesSchema = await GenerateAsync(valueType, schemaResolver).ConfigureAwait(false); var valueTypeDescription = Settings.ReflectionService.GetDescription(valueType, null, Settings); if (valueTypeDescription.RequiresSchemaReference(Settings.TypeMappers)) { schema.AdditionalPropertiesSchema = new JsonSchema4 { Reference = additionalPropertiesSchema }; } else { schema.AdditionalPropertiesSchema = additionalPropertiesSchema; } } schema.AllowAdditionalProperties = true; }
private async Task GeneratePropertiesAndInheritanceAsync(Type type, JsonSchema4 schema, JsonSchemaResolver schemaResolver) { var properties = GetTypeProperties(type); #if !LEGACY var declaredFields = type.GetTypeInfo().DeclaredFields .Where(f => f.IsPublic); var declaredProperties = type.GetTypeInfo().DeclaredProperties .Where(p => p.GetMethod?.IsPublic == true || p.SetMethod?.IsPublic == true); #else var declaredFields = type.GetTypeInfo() .GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); var declaredProperties = type.GetTypeInfo() .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) .Where(p => p.GetGetMethod()?.IsPublic == true || p.GetSetMethod()?.IsPublic == true); #endif foreach (var property in declaredProperties.Where(p => properties == null || properties.Contains(p.Name))) { await LoadPropertyOrFieldAsync(property, property.PropertyType, type, schema, schemaResolver).ConfigureAwait(false); } foreach (var field in declaredFields.Where(p => properties == null || properties.Contains(p.Name))) { await LoadPropertyOrFieldAsync(field, field.FieldType, type, schema, schemaResolver).ConfigureAwait(false); } await GenerateInheritanceAsync(type, schema, schemaResolver).ConfigureAwait(false); }
private async Task LoadPropertyOrFieldAsync(MemberInfo property, Type propertyType, Type parentType, JsonSchema4 parentSchema, JsonSchemaResolver schemaResolver) { var attributes = property.GetCustomAttributes(inherit: true).OfType <Attribute>().ToArray(); var propertyTypeDescription = JsonObjectTypeDescription.FromType(propertyType, attributes, Settings.DefaultEnumHandling); if (IsPropertyIgnored(parentType, attributes) == false) { JsonProperty jsonProperty; if (propertyType.Name == "Nullable`1") #if !LEGACY { propertyType = propertyType.GenericTypeArguments[0]; } #else { propertyType = propertyType.GetGenericArguments()[0]; } #endif var requiresSchemaReference = RequiresSchemaReference(propertyType, attributes); if (requiresSchemaReference) { var propertySchema = await GenerateAsync(propertyType, attributes, schemaResolver).ConfigureAwait(false); // The schema is automatically added to Definitions if it is missing in JsonPathUtilities.GetJsonPath() if (Settings.NullHandling == NullHandling.JsonSchema) { jsonProperty = new JsonProperty(); jsonProperty.OneOf.Add(new JsonSchema4 { SchemaReference = propertySchema.ActualSchema }); } else { jsonProperty = new JsonProperty { SchemaReference = propertySchema.ActualSchema }; } } else { jsonProperty = await GenerateAsync <JsonProperty>(propertyType, attributes, schemaResolver).ConfigureAwait(false); } var propertyName = JsonReflectionUtilities.GetPropertyName(property, Settings.DefaultPropertyNameHandling); if (parentSchema.Properties.ContainsKey(propertyName)) { throw new InvalidOperationException("The JSON property '" + propertyName + "' is defined multiple times on type '" + parentType.FullName + "'."); } if (Settings.GenerateXmlObjects) { jsonProperty.GenerateXmlObjectForProperty(parentType, propertyName, attributes); } parentSchema.Properties.Add(propertyName, jsonProperty); var requiredAttribute = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RequiredAttribute"); var jsonPropertyAttribute = attributes.OfType <JsonPropertyAttribute>().SingleOrDefault(); var hasJsonNetAttributeRequired = jsonPropertyAttribute != null && ( jsonPropertyAttribute.Required == Required.Always || jsonPropertyAttribute.Required == Required.AllowNull); var isDataContractMemberRequired = GetDataMemberAttribute(parentType, attributes)?.IsRequired == true; var hasRequiredAttribute = requiredAttribute != null; if (hasRequiredAttribute || isDataContractMemberRequired || hasJsonNetAttributeRequired) { parentSchema.RequiredProperties.Add(propertyName); } var isJsonNetAttributeNullable = jsonPropertyAttribute != null && jsonPropertyAttribute.Required == Required.AllowNull; var isNullable = !hasRequiredAttribute && !isDataContractMemberRequired && (propertyTypeDescription.IsNullable || isJsonNetAttributeNullable); if (isNullable) { if (Settings.NullHandling == NullHandling.JsonSchema) { if (requiresSchemaReference) { jsonProperty.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.Null }); } else if (jsonProperty.Type == JsonObjectType.None) { jsonProperty.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.None }); jsonProperty.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.Null }); } else { jsonProperty.Type = jsonProperty.Type | JsonObjectType.Null; } } } else if (Settings.NullHandling == NullHandling.Swagger) { if (!parentSchema.RequiredProperties.Contains(propertyName)) { parentSchema.RequiredProperties.Add(propertyName); } } dynamic readOnlyAttribute = attributes.TryGetIfAssignableTo("System.ComponentModel.ReadOnlyAttribute"); if (readOnlyAttribute != null) { jsonProperty.IsReadOnly = readOnlyAttribute.IsReadOnly; } jsonProperty.Description = await GetDescriptionAsync(property, attributes).ConfigureAwait(false); ApplyPropertyAnnotations(jsonProperty, parentType, attributes, propertyTypeDescription); } }
/// <summary>Generates the properties for the given type and schema.</summary> /// <typeparam name="TSchemaType">The type of the schema type.</typeparam> /// <param name="type">The types.</param> /// <param name="schema">The properties</param> /// <param name="schemaResolver">The schema resolver.</param> protected virtual async Task GenerateObjectAsync <TSchemaType>(Type type, TSchemaType schema, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { schemaResolver.AddSchema(type, false, schema); schema.AllowAdditionalProperties = false; await GeneratePropertiesAndInheritanceAsync(type, schema, schemaResolver).ConfigureAwait(false); if (Settings.GenerateKnownTypes) { await GenerateKnownTypesAsync(type, schemaResolver).ConfigureAwait(false); } if (Settings.GenerateXmlObjects) { schema.GenerateXmlObjectForType(type); } }
/// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> private async Task GenerateDictionaryAsync <TSchemaType>(Type type, TSchemaType schema, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { var genericTypeArguments = ReflectionExtensions.GetGenericTypeArguments(type); var valueType = genericTypeArguments.Length == 2 ? genericTypeArguments[1] : typeof(object); if (valueType == typeof(object)) { schema.AdditionalPropertiesSchema = JsonSchema4.CreateAnySchema(); } else { var additionalPropertiesSchema = await GenerateAsync(valueType, schemaResolver).ConfigureAwait(false); if (RequiresSchemaReference(valueType, null)) { schema.AdditionalPropertiesSchema = new JsonSchema4 { SchemaReference = additionalPropertiesSchema }; } else { schema.AdditionalPropertiesSchema = additionalPropertiesSchema; } } schema.AllowAdditionalProperties = true; }
private bool TryHandleSpecialTypes <TSchemaType>(Type type, TSchemaType schema, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { var typeMapper = Settings.TypeMappers.FirstOrDefault(m => m.MappedType == type); if (typeMapper != null) { typeMapper.GenerateSchema(schema, this, schemaResolver); return(true); } if (type == typeof(JObject) || type == typeof(JToken) || type == typeof(object)) { return(true); } return(false); }
/// <summary>Generetes a schema directly or referenced for the requested schema type; also adds nullability if required.</summary> /// <typeparam name="TSchemaType">The resulted schema type which may reference the actual schema.</typeparam> /// <param name="type">The type of the schema to generate.</param> /// <param name="parentAttributes">The parent attributes (e.g. property or paramter attributes).</param> /// <param name="isNullable">Specifies whether the property, parameter or requested schema type is nullable.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <param name="transformation">An action to transform the resulting schema (e.g. property or parameter) before the type of reference is determined (with $ref or allOf/oneOf).</param> /// <returns>The requested schema object.</returns> public virtual async Task <TSchemaType> GenerateWithReferenceAndNullability <TSchemaType>( Type type, IEnumerable <Attribute> parentAttributes, bool isNullable, JsonSchemaResolver schemaResolver, Func <TSchemaType, JsonSchema4, Task> transformation = null) where TSchemaType : JsonSchema4, new() { var typeDescription = Settings.ReflectionService.GetDescription(type, parentAttributes, Settings); var requiresSchemaReference = typeDescription.RequiresSchemaReference(Settings.TypeMappers); JsonSchema4 referencedSchema; if (!requiresSchemaReference) { var schema = await GenerateAsync <TSchemaType>(type, parentAttributes, schemaResolver).ConfigureAwait(false); if (!schema.HasReference) { if (transformation != null) { await transformation(schema, schema).ConfigureAwait(false); } if (isNullable) { if (Settings.SchemaType != SchemaType.Swagger2) { if (schema.Type == JsonObjectType.None) { schema.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.None }); schema.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.Null }); } else { schema.Type = schema.Type | JsonObjectType.Null; } } } return(schema); } else { referencedSchema = schema.ActualSchema; } } else { referencedSchema = await GenerateAsync <JsonSchema4>(type, parentAttributes, schemaResolver).ConfigureAwait(false); } var referencingSchema = new TSchemaType(); if (transformation != null) { await transformation(referencingSchema, referencedSchema).ConfigureAwait(false); } if (isNullable && Settings.SchemaType != SchemaType.Swagger2) { referencingSchema.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.Null }); } // See https://github.com/RSuter/NJsonSchema/issues/531 var useDirectReference = Settings.AllowReferencesWithProperties || !JsonConvert.DeserializeObject <JObject>(JsonConvert.SerializeObject(referencingSchema)).Properties().Any(); // TODO: Improve performance if (useDirectReference && referencingSchema.OneOf.Count == 0) { referencingSchema.Reference = referencedSchema.ActualSchema; } else if (Settings.SchemaType != SchemaType.Swagger2) { referencingSchema.OneOf.Add(new JsonSchema4 { Reference = referencedSchema.ActualSchema }); } else { referencingSchema.AllOf.Add(new JsonSchema4 { Reference = referencedSchema.ActualSchema }); } return(referencingSchema); }
private void LoadPropertyOrField(MemberInfo property, Type propertyType, Type parentType, JsonSchema4 parentSchema, JsonSchemaResolver schemaResolver) { var attributes = property.GetCustomAttributes(inherit: true).OfType <Attribute>().ToArray(); var propertyTypeDescription = JsonObjectTypeDescription.FromType(propertyType, attributes, Settings.DefaultEnumHandling); if (IsPropertyIgnored(parentType, attributes) == false) { JsonProperty jsonProperty; if (propertyType.Name == "Nullable`1") #if !LEGACY { propertyType = propertyType.GenericTypeArguments[0]; } #else { propertyType = propertyType.GetGenericArguments()[0]; } #endif var requiresSchemaReference = RequiresSchemaReference(propertyType, attributes); if (requiresSchemaReference) { var propertySchema = Generate <JsonSchema4>(propertyType, attributes, schemaResolver); // The schema is automatically added to Definitions if it is missing in JsonPathUtilities.GetJsonPath() if (Settings.NullHandling == NullHandling.JsonSchema) { jsonProperty = new JsonProperty(); jsonProperty.OneOf.Add(new JsonSchema4 { SchemaReference = propertySchema.ActualSchema }); } else { jsonProperty = new JsonProperty { SchemaReference = propertySchema.ActualSchema }; } } else { jsonProperty = Generate <JsonProperty>(propertyType, attributes, schemaResolver); } var propertyName = JsonPathUtilities.GetPropertyName(property, Settings.DefaultPropertyNameHandling); if (parentSchema.Properties.ContainsKey(propertyName)) { throw new InvalidOperationException("The JSON property '" + propertyName + "' is defined multiple times on type '" + parentType.FullName + "'."); } parentSchema.Properties.Add(propertyName, jsonProperty); var requiredAttribute = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RequiredAttribute"); var jsonPropertyAttribute = attributes.OfType <JsonPropertyAttribute>().SingleOrDefault(); //PT HACK - zusätzlich wenn ein jsonproperty vorhanden ist wird der propertyname (ungekürzt) in das fullName Property geschrieben if (jsonPropertyAttribute != null) { jsonProperty.fullName = property.Name; } //END PT HACK parentSchema.Properties.Add(propertyName, jsonProperty); //<-- musste für den PT Hack aus Zeile 427 nach unten verschoben werden var hasJsonNetAttributeRequired = jsonPropertyAttribute != null && ( jsonPropertyAttribute.Required == Required.Always || jsonPropertyAttribute.Required == Required.AllowNull); var isDataContractMemberRequired = GetDataMemberAttribute(parentType, attributes)?.IsRequired == true; var hasRequiredAttribute = requiredAttribute != null; if (hasRequiredAttribute || isDataContractMemberRequired || hasJsonNetAttributeRequired) { parentSchema.RequiredProperties.Add(propertyName); } var isJsonNetAttributeNullable = jsonPropertyAttribute != null && jsonPropertyAttribute.Required == Required.AllowNull; var isNullable = !hasRequiredAttribute && !isDataContractMemberRequired && (propertyTypeDescription.IsNullable || isJsonNetAttributeNullable); if (isNullable) { if (Settings.NullHandling == NullHandling.JsonSchema) { if (requiresSchemaReference) { jsonProperty.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.Null }); } else if (jsonProperty.Type == JsonObjectType.None) { jsonProperty.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.None }); jsonProperty.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.Null }); } else { jsonProperty.Type = jsonProperty.Type | JsonObjectType.Null; } } } else if (Settings.NullHandling == NullHandling.Swagger) { if (!parentSchema.RequiredProperties.Contains(propertyName)) { parentSchema.RequiredProperties.Add(propertyName); } } dynamic readOnlyAttribute = attributes.TryGetIfAssignableTo("System.ComponentModel.ReadOnlyAttribute"); if (readOnlyAttribute != null) { jsonProperty.IsReadOnly = readOnlyAttribute.IsReadOnly; } jsonProperty.Description = GetDescription(property, attributes); ApplyPropertyAnnotations(jsonProperty, parentType, attributes, propertyTypeDescription); } }
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); } }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <param name="type">The type.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public JsonSchema4 Generate(Type type) { var schemaResolver = new JsonSchemaResolver(Settings); return(Generate <JsonSchema4>(type, null, schemaResolver)); }
private async Task GeneratePropertiesAndInheritanceAsync(Type type, JsonSchema4 schema, JsonSchemaResolver schemaResolver) { #if !LEGACY var propertiesAndFields = type.GetTypeInfo() .DeclaredFields .Where(f => f.IsPublic) .OfType <MemberInfo>() .Concat( type.GetTypeInfo().DeclaredProperties .Where(p => p.GetMethod?.IsPublic == true || p.SetMethod?.IsPublic == true) ) .ToList(); #else var propertiesAndFields = type.GetTypeInfo() .GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) .OfType <MemberInfo>() .Concat( type.GetTypeInfo() .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) .Where(p => p.GetGetMethod()?.IsPublic == true || p.GetSetMethod()?.IsPublic == true) ) .ToList(); #endif var contract = Settings.ResolveContract(type); var allowedProperties = GetTypeProperties(type); var objectContract = contract as JsonObjectContract; if (objectContract != null && allowedProperties == null) { foreach (var property in objectContract.Properties.Where(p => p.DeclaringType == type)) { bool shouldSerialize; try { shouldSerialize = property.ShouldSerialize?.Invoke(null) != false; } catch { shouldSerialize = true; } if (shouldSerialize) { var info = propertiesAndFields.FirstOrDefault(p => p.Name == property.UnderlyingName); var propertyInfo = info as PropertyInfo; #if !LEGACY if (Settings.GenerateAbstractProperties || propertyInfo == null || (propertyInfo.GetMethod?.IsAbstract != true && propertyInfo.SetMethod?.IsAbstract != true)) #else if (Settings.GenerateAbstractProperties || propertyInfo == null || (propertyInfo.GetGetMethod()?.IsAbstract != true && propertyInfo.GetSetMethod()?.IsAbstract != true)) #endif { await LoadPropertyOrFieldAsync(property, info, type, schema, schemaResolver).ConfigureAwait(false); } } } } else { // TODO: Remove this hacky code (used to support serialization of exceptions and restore the old behavior [pre 9.x]) foreach (var info in propertiesAndFields.Where(m => allowedProperties == null || allowedProperties.Contains(m.Name))) { var attribute = info.GetCustomAttributes(true).OfType <JsonPropertyAttribute>().SingleOrDefault(); var propertyType = (info as PropertyInfo)?.PropertyType ?? ((FieldInfo)info).FieldType; var property = new Newtonsoft.Json.Serialization.JsonProperty { AttributeProvider = new ReflectionAttributeProvider(info), PropertyType = propertyType, Ignored = IsPropertyIgnored(propertyType, type, info.GetCustomAttributes(true).OfType <Attribute>().ToArray()) }; if (attribute != null) { property.PropertyName = attribute.PropertyName ?? info.Name; property.Required = attribute.Required; property.DefaultValueHandling = attribute.DefaultValueHandling; property.TypeNameHandling = attribute.TypeNameHandling; property.NullValueHandling = attribute.NullValueHandling; property.TypeNameHandling = attribute.TypeNameHandling; } else { property.PropertyName = info.Name; } await LoadPropertyOrFieldAsync(property, info, type, schema, schemaResolver).ConfigureAwait(false); } } await GenerateInheritanceAsync(type, schema, schemaResolver).ConfigureAwait(false); }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <param name="type">The type.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public JsonSchema4 Generate(Type type, JsonSchemaResolver schemaResolver) { return(Generate <JsonSchema4>(type, null, schemaResolver)); }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <param name="type">The type.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public Task <TSchemaType> GenerateAsync <TSchemaType>(Type type, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { return(GenerateAsync <TSchemaType>(type, null, schemaResolver)); }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <typeparam name="TSchemaType">The type of the schema.</typeparam> /// <param name="type">The type.</param> /// <param name="parentAttributes">The parent property or parameter attributes.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public virtual TSchemaType Generate <TSchemaType>(Type type, IEnumerable <Attribute> parentAttributes, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new() { var schema = HandleSpecialTypes <TSchemaType>(type, schemaResolver); if (schema != null) { return(schema); } schema = new TSchemaType(); if (!schemaResolver.Schemas.Any() && type.GetTypeInfo().IsClass) { schema.Title = Settings.SchemaNameGenerator.Generate(type); } ApplyExtensionDataAttributes(schema, type, parentAttributes); var typeDescription = JsonObjectTypeDescription.FromType(type, parentAttributes, Settings.DefaultEnumHandling); if (typeDescription.Type.HasFlag(JsonObjectType.Object)) { if (typeDescription.IsDictionary) { typeDescription.ApplyType(schema); GenerateDictionary(type, schema, schemaResolver); } else { if (schemaResolver.HasSchema(type, false)) { schema.SchemaReference = schemaResolver.GetSchema(type, false); return(schema); } if (schema.GetType() == typeof(JsonSchema4)) { typeDescription.ApplyType(schema); schema.Description = GetDescription(type.GetTypeInfo(), type.GetTypeInfo().GetCustomAttributes()); GenerateObject(type, schema, schemaResolver); } else { schema.SchemaReference = Generate <JsonSchema4>(type, parentAttributes, schemaResolver); return(schema); } } } else if (type.GetTypeInfo().IsEnum) { var isIntegerEnumeration = typeDescription.Type == JsonObjectType.Integer; if (schemaResolver.HasSchema(type, isIntegerEnumeration)) { schema.SchemaReference = schemaResolver.GetSchema(type, isIntegerEnumeration); return(schema); } if (schema.GetType() == typeof(JsonSchema4)) { LoadEnumerations(type, schema, typeDescription); typeDescription.ApplyType(schema); schema.Description = type.GetXmlSummary(); schemaResolver.AddSchema(type, isIntegerEnumeration, schema); } else { schema.SchemaReference = Generate <JsonSchema4>(type, parentAttributes, schemaResolver); return(schema); } } else if (typeDescription.Type.HasFlag(JsonObjectType.Array)) { typeDescription.ApplyType(schema); var itemType = type.GetEnumerableItemType(); if (itemType == null) { var jsonSchemaAttribute = type.GetTypeInfo().GetCustomAttribute <JsonSchemaAttribute>(); if (jsonSchemaAttribute?.ArrayItem != null) { schema.Item = GenerateWithReference(schemaResolver, itemType); } else { schema.Item = JsonSchema4.CreateAnySchema(); } } else { schema.Item = GenerateWithReference(schemaResolver, itemType); } } else { typeDescription.ApplyType(schema); } return(schema); }
/// <summary>Generates a <see cref="JsonSchema4" /> object for the given type and adds the mapping to the given resolver.</summary> /// <param name="type">The type.</param> /// <param name="parentAttributes">The parent property or parameter attributes.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <returns>The schema.</returns> /// <exception cref="InvalidOperationException">Could not find value type of dictionary type.</exception> public async Task <JsonSchema4> GenerateAsync(Type type, IEnumerable <Attribute> parentAttributes, JsonSchemaResolver schemaResolver) { return(await GenerateAsync <JsonSchema4>(type, parentAttributes, schemaResolver).ConfigureAwait(false)); }
private void TryPostProcessSchema <TSchemaType>(Type type, IEnumerable <Attribute> parentAttributes, TSchemaType schema, JsonSchemaResolver schemaResolver) where TSchemaType : JsonSchema4 { JsonSchemaPostProcessAttribute[] postProcessAttributes; if (parentAttributes == null) { // class postProcessAttributes = type.GetTypeInfo().GetCustomAttributes <JsonSchemaPostProcessAttribute>().ToArray(); } else { // property or parameter postProcessAttributes = parentAttributes.OfType <JsonSchemaPostProcessAttribute>().ToArray(); } if (postProcessAttributes.Any()) { foreach (var attribute in postProcessAttributes) { #if !LEGACY var methodInfo = attribute.Type.GetTypeInfo().GetDeclaredMethod(attribute.MethodName); #else var methodInfo = attribute.Type.GetMethod( attribute.MethodName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); #endif methodInfo.Invoke(null, new object[] { schema }); } } }