/// <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);
            }
        }
Пример #4
0
        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);
            }
        }
Пример #7
0
        /// <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);
        }
Пример #15
0
        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);
            }
        }
Пример #17
0
        /// <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);
        }
Пример #19
0
 /// <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));
 }
Пример #21
0
        /// <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));
 }
Пример #23
0
        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 });
                }
            }
        }