<PackageReference Include="NJsonSchema" Version="4.15.6113.36675" />

JsonSchemaGenerator

public class JsonSchemaGenerator
Generates a JsonSchema4 object for a given type.
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NJsonSchema.Annotations; using NJsonSchema.Converters; using NJsonSchema.Generation.TypeMappers; using NJsonSchema.Infrastructure; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace NJsonSchema.Generation { public class JsonSchemaGenerator { private static readonly Dictionary<string, string> DataTypeFormats = new Dictionary<string, string> { { "DateTime", "date-time" }, { "Date", "date" }, { "Time", "time" }, { "EmailAddress", "email" }, { "PhoneNumber", "phone" }, { "Url", "uri" } }; public JsonSchemaGeneratorSettings Settings { get; } public JsonSchemaGenerator(JsonSchemaGeneratorSettings settings) { Settings = settings; } public JsonSchema4 Generate(Type type) { return Generate<JsonSchema4>(type, null, new SchemaResolver(), new JsonSchemaDefinitionAppender(null, null)); } public JsonSchema4 Generate(Type type, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) { return Generate<JsonSchema4>(type, null, schemaResolver, schemaDefinitionAppender); } public virtual TSchemaType Generate<TSchemaType>(Type type, IEnumerable<Attribute> parentAttributes, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) where TSchemaType : JsonSchema4, new { TSchemaType val = HandleSpecialTypes<TSchemaType>(type, schemaResolver); if (val != null) return val; val = new TSchemaType(); if (schemaDefinitionAppender.RootObject == null) schemaDefinitionAppender.RootObject = val; ApplyExtensionDataAttributes(val, type, parentAttributes); JsonObjectTypeDescription jsonObjectTypeDescription = JsonObjectTypeDescription.FromType(type, parentAttributes, Settings.DefaultEnumHandling); if (jsonObjectTypeDescription.Type.HasFlag(JsonObjectType.Object)) { if (jsonObjectTypeDescription.IsDictionary) { jsonObjectTypeDescription.ApplyType(val); GenerateDictionary(type, val, schemaResolver, schemaDefinitionAppender); } else { if (schemaResolver.HasSchema(type, false)) { val.SchemaReference = schemaResolver.GetSchema(type, false); return val; } if ((object)val.GetType() != typeof(JsonSchema4)) { val.SchemaReference = Generate<JsonSchema4>(type, parentAttributes, schemaResolver, schemaDefinitionAppender); return val; } jsonObjectTypeDescription.ApplyType(val); val.TypeNameRaw = ReflectionExtensions.GetSafeTypeName(type); val.Description = GetDescription(type.GetTypeInfo(), type.GetTypeInfo().GetCustomAttributes()); GenerateObject(type, val, schemaResolver, schemaDefinitionAppender); } } else if (type.GetTypeInfo().get_IsEnum()) { bool isIntegerEnumeration = jsonObjectTypeDescription.Type == JsonObjectType.Integer; if (schemaResolver.HasSchema(type, isIntegerEnumeration)) { val.SchemaReference = schemaResolver.GetSchema(type, isIntegerEnumeration); return val; } if ((object)val.GetType() != typeof(JsonSchema4)) { val.SchemaReference = Generate<JsonSchema4>(type, parentAttributes, schemaResolver, schemaDefinitionAppender); return val; } LoadEnumerations(type, val, jsonObjectTypeDescription); jsonObjectTypeDescription.ApplyType(val); val.TypeNameRaw = ReflectionExtensions.GetSafeTypeName(type); val.Description = type.GetXmlDocumentation(); schemaResolver.AddSchema(type, isIntegerEnumeration, val); } else if (jsonObjectTypeDescription.Type.HasFlag(JsonObjectType.Array)) { jsonObjectTypeDescription.ApplyType(val); Type[] genericTypeArguments = type.GetGenericTypeArguments(); Type type2 = (genericTypeArguments.Length == 0) ? type.GetElementType() : genericTypeArguments[0]; if ((object)type2 == null) { JsonSchemaAttribute customAttribute = type.GetTypeInfo().GetCustomAttribute<JsonSchemaAttribute>(); if ((object)customAttribute?.ArrayItem != null) val.Item = Generate(customAttribute.ArrayItem, schemaResolver, schemaDefinitionAppender); else val.Item = JsonSchema4.CreateAnySchema(); } else val.Item = Generate(type2, schemaResolver, schemaDefinitionAppender); } else { jsonObjectTypeDescription.ApplyType(val); } return val; } private void ApplyExtensionDataAttributes<TSchemaType>(TSchemaType schema, Type type, IEnumerable<Attribute> parentAttributes) where TSchemaType : JsonSchema4, new { if (parentAttributes == null) { JsonSchemaExtensionDataAttribute[] source = type.GetTypeInfo().GetCustomAttributes<JsonSchemaExtensionDataAttribute>().ToArray(); if (source.Any()) schema.ExtensionData = source.ToDictionary((JsonSchemaExtensionDataAttribute a) => a.Property, (JsonSchemaExtensionDataAttribute a) => a.Value); } else { JsonSchemaExtensionDataAttribute[] source2 = parentAttributes.OfType<JsonSchemaExtensionDataAttribute>().ToArray(); if (source2.Any()) schema.ExtensionData = source2.ToDictionary((JsonSchemaExtensionDataAttribute a) => a.Property, (JsonSchemaExtensionDataAttribute a) => a.Value); } } private TSchemaType HandleSpecialTypes<TSchemaType>(Type type, ISchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new { ITypeMapper typeMapper = Settings.TypeMappers.FirstOrDefault((ITypeMapper m) => (object)m.MappedType == type); if (typeMapper != null) { TSchemaType schema = typeMapper.GetSchema<TSchemaType>(this, schemaResolver); if (schema != null) return schema; } if ((object)type == typeof(JObject) || (object)type == typeof(JToken) || (object)type == typeof(object)) return JsonSchema4.CreateAnySchema<TSchemaType>(); return null; } private void GenerateDictionary<TSchemaType>(Type type, TSchemaType schema, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) where TSchemaType : JsonSchema4, new { Type[] genericTypeArguments = type.GetGenericTypeArguments(); Type type2 = (genericTypeArguments.Length == 2) ? genericTypeArguments[1] : typeof(object); if ((object)type2 == typeof(object)) schema.AdditionalPropertiesSchema = JsonSchema4.CreateAnySchema(); else schema.AdditionalPropertiesSchema = Generate(type2, schemaResolver, schemaDefinitionAppender); schema.AllowAdditionalProperties = true; } protected virtual void GenerateObject<TSchemaType>(Type type, TSchemaType schema, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) where TSchemaType : JsonSchema4, new { schemaResolver.AddSchema(type, false, schema); schema.AllowAdditionalProperties = false; GeneratePropertiesAndInheritance(type, schema, schemaResolver, schemaDefinitionAppender); if (Settings.GenerateKnownTypes) GenerateKnownTypes(type, schemaResolver, schemaDefinitionAppender); } private void GeneratePropertiesAndInheritance(Type type, JsonSchema4 schema, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) { string[] properties = GetTypeProperties(type); IEnumerable<FieldInfo> source = from f in type.GetTypeInfo().DeclaredFields where f.IsPublic select f; foreach (PropertyInfo item in type.GetTypeInfo().DeclaredProperties.Where(delegate(PropertyInfo p) { if (!(p.GetMethod?.IsPublic ?? false)) return p.SetMethod?.IsPublic ?? false; return true; }).Where(delegate(PropertyInfo p) { if (properties != null) return properties.Contains(p.Name); return true; })) { LoadPropertyOrField(item, item.PropertyType, type, schema, schemaResolver, schemaDefinitionAppender); } foreach (FieldInfo item2 in source.Where(delegate(FieldInfo p) { if (properties != null) return properties.Contains(p.Name); return true; })) { LoadPropertyOrField(item2, item2.FieldType, type, schema, schemaResolver, schemaDefinitionAppender); } GenerateInheritance(type, schema, schemaResolver, schemaDefinitionAppender); } private void GenerateKnownTypes(Type type, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) { foreach (dynamic item in from a in type.GetTypeInfo().GetCustomAttributes() where a.GetType().get_Name() == "KnownTypeAttribute" select a) { dynamic val = JsonObjectTypeDescription.FromType(item.Type, null, Settings.DefaultEnumHandling); dynamic val2 = val.Type == JsonObjectType.Integer; if ((!schemaResolver.HasSchema(item.Type, val2))) { dynamic val3 = this.Generate(item.Type, schemaResolver, schemaDefinitionAppender); schemaDefinitionAppender.Append(val3.ActualSchema); } } } private void GenerateInheritance(Type type, JsonSchema4 schema, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) { GenerateInheritanceDiscriminator(type, schema); Type baseType = type.GetTypeInfo().get_BaseType(); if ((object)baseType != null && (object)baseType != typeof(object)) { if (Settings.FlattenInheritanceHierarchy) GeneratePropertiesAndInheritance(baseType, schema, schemaResolver, schemaDefinitionAppender); else { JsonSchema4 item = Generate(baseType, schemaResolver, schemaDefinitionAppender); schema.AllOf.Add(item); } } } private void GenerateInheritanceDiscriminator(Type type, JsonSchema4 schema) { if (!Settings.FlattenInheritanceHierarchy) { string text = TryGetInheritanceDiscriminator(CustomAttributeExtensions.GetCustomAttributes(type.GetTypeInfo(), false).OfType<Attribute>()); if (!string.IsNullOrEmpty(text)) { if (schema.Properties.ContainsKey(text)) throw new InvalidOperationException("The JSON property '" + text + "' is defined multiple times on type '" + type.FullName + "'."); schema.Discriminator = text; schema.Properties[text] = new JsonProperty { Type = JsonObjectType.String, IsRequired = true }; } } } private string TryGetInheritanceDiscriminator(IEnumerable<Attribute> typeAttributes) { dynamic val = (typeAttributes != null) ? typeAttributes.FirstOrDefault((Attribute a) => a.GetType().get_Name() == "JsonConverterAttribute") : null; if ((val != null) && ((Type)val.ConverterType).get_Name() == "JsonInheritanceConverter") { dynamic val2 = val.ConverterParameters != null; if ((val2 ? false : true) ? val2 : (val2 & (val.ConverterParameters.Length > 0))) return (string)val.ConverterParameters[0]; return JsonInheritanceConverter.DefaultDiscriminatorName; } return null; } protected virtual string[] GetTypeProperties(Type type) { if ((object)type == typeof(Exception)) return new string[4] { "InnerException", "Message", "Source", "StackTrace" }; return null; } private void LoadEnumerations(Type type, JsonSchema4 schema, JsonObjectTypeDescription typeDescription) { schema.Type = typeDescription.Type; schema.Enumeration.Clear(); schema.EnumerationNames.Clear(); string[] names = Enum.GetNames(type); foreach (string text in names) { if (typeDescription.Type == JsonObjectType.Integer) { object item = Convert.ChangeType(Enum.Parse(type, text), Enum.GetUnderlyingType(type)); schema.Enumeration.Add(item); } else { dynamic val = type.GetTypeInfo().GetDeclaredField(text).GetCustomAttributes() .TryGetIfAssignableTo("System.Runtime.Serialization.EnumMemberAttribute", TypeNameStyle.FullName); dynamic val2 = val != null; if ((val2 ? false : true) ? val2 : (val2 & !string.IsNullOrEmpty(val.Value))) schema.Enumeration.Add((string)val.Value); else schema.Enumeration.Add(text); } schema.EnumerationNames.Add(text); } } private void LoadPropertyOrField(MemberInfo property, Type propertyType, Type parentType, JsonSchema4 parentSchema, ISchemaResolver schemaResolver, ISchemaDefinitionAppender schemaDefinitionAppender) { Attribute[] array = CustomAttributeExtensions.GetCustomAttributes(property, true).OfType<Attribute>().ToArray(); JsonObjectTypeDescription jsonObjectTypeDescription = JsonObjectTypeDescription.FromType(propertyType, array, Settings.DefaultEnumHandling); if (!IsPropertyIgnored(parentType, array)) { if (propertyType.get_Name() == "Nullable`1") propertyType = propertyType.GenericTypeArguments[0]; bool flag = (Settings.TypeMappers.FirstOrDefault((ITypeMapper m) => (object)m.MappedType == propertyType)?.UseReference ?? true) && !Settings.TypeMappers.Any((ITypeMapper m) => m is PrimitiveTypeMapper) && !jsonObjectTypeDescription.IsDictionary && (jsonObjectTypeDescription.Type.HasFlag(JsonObjectType.Object) || jsonObjectTypeDescription.IsEnum); JsonProperty jsonProperty; if (flag) { JsonSchema4 jsonSchema = Generate<JsonSchema4>(propertyType, array, schemaResolver, schemaDefinitionAppender); if (Settings.NullHandling == NullHandling.JsonSchema) { jsonProperty = new JsonProperty(); jsonProperty.OneOf.Add(new JsonSchema4 { SchemaReference = jsonSchema.ActualSchema }); } else jsonProperty = new JsonProperty { SchemaReference = jsonSchema.ActualSchema }; } else jsonProperty = Generate<JsonProperty>(propertyType, array, schemaResolver, schemaDefinitionAppender); string 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); Attribute attribute = array.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RequiredAttribute", TypeNameStyle.FullName); JsonPropertyAttribute jsonPropertyAttribute = array.OfType<JsonPropertyAttribute>().SingleOrDefault(); bool flag2 = jsonPropertyAttribute != null && (jsonPropertyAttribute.Required == Required.Always || jsonPropertyAttribute.Required == Required.AllowNull); bool num = attribute != null; if (num | flag2) parentSchema.RequiredProperties.Add(propertyName); bool flag3 = jsonPropertyAttribute != null && jsonPropertyAttribute.Required == Required.AllowNull; if (!num && (jsonObjectTypeDescription.IsNullable | flag3)) { if (Settings.NullHandling == NullHandling.JsonSchema) { if (flag) 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 |= JsonObjectType.Null; } } } else if (Settings.NullHandling == NullHandling.Swagger && !parentSchema.RequiredProperties.Contains(propertyName)) { parentSchema.RequiredProperties.Add(propertyName); } dynamic val = array.TryGetIfAssignableTo("System.ComponentModel.ReadOnlyAttribute", TypeNameStyle.FullName); if (val != null) jsonProperty.IsReadOnly = ((byte)val.IsReadOnly != 0); jsonProperty.Description = GetDescription(property, array); ApplyPropertyAnnotations(jsonProperty, parentType, array, jsonObjectTypeDescription); } } private static bool IsPropertyIgnored(Type parentType, Attribute[] propertyAttributes) { if (propertyAttributes.Any((Attribute a) => a is JsonIgnoreAttribute)) return true; if (parentType.GetTypeInfo().GetCustomAttributes().Any((Attribute a) => a.GetType().get_Name() == "DataContractAttribute") && propertyAttributes.All((Attribute a) => a.GetType().get_Name() != "DataMemberAttribute") && !propertyAttributes.Any((Attribute a) => a is JsonPropertyAttribute)) return true; return false; } public void ApplyPropertyAnnotations(JsonSchema4 jsonProperty, Type parentType, IList<Attribute> attributes, JsonObjectTypeDescription propertyTypeDescription) { dynamic val = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.DisplayAttribute", TypeNameStyle.FullName); dynamic val2 = val != null; if ((val2 ? false : true) ? val2 : (val2 & (val.Name != null))) jsonProperty.Title = (string)val.Name; dynamic val3 = attributes.TryGetIfAssignableTo("System.ComponentModel.DefaultValueAttribute", TypeNameStyle.FullName); val2 = (val3 != null); if ((val2 ? false : true) ? val2 : (val2 & (val3.Value != null))) jsonProperty.Default = (object)this.ConvertDefaultValue(parentType, attributes, val3); dynamic val4 = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RegularExpressionAttribute", TypeNameStyle.FullName); if (val4 != null) jsonProperty.Pattern = (string)val4.Pattern; if (propertyTypeDescription.Type == JsonObjectType.Number || propertyTypeDescription.Type == JsonObjectType.Integer) { dynamic val5 = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.RangeAttribute", TypeNameStyle.FullName); if (val5 != null) { if (val5.Minimum != null) jsonProperty.Minimum = (double?)val5.Minimum; if (val5.Maximum != null) jsonProperty.Maximum = (double?)val5.Maximum; } MultipleOfAttribute multipleOfAttribute = attributes.OfType<MultipleOfAttribute>().SingleOrDefault(); if (multipleOfAttribute != null) jsonProperty.MultipleOf = multipleOfAttribute.MultipleOf; } dynamic val6 = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.MinLengthAttribute", TypeNameStyle.FullName); val2 = (val6 != null); if ((val2 ? false : true) ? val2 : (val2 & (val6.Length != null))) { if (propertyTypeDescription.Type == JsonObjectType.String) jsonProperty.MinLength = (int?)val6.Length; else if (propertyTypeDescription.Type == JsonObjectType.Array) { jsonProperty.MinItems = (int)val6.Length; } } dynamic val7 = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.MaxLengthAttribute", TypeNameStyle.FullName); val2 = (val7 != null); if ((val2 ? false : true) ? val2 : (val2 & (val7.Length != null))) { if (propertyTypeDescription.Type == JsonObjectType.String) jsonProperty.MaxLength = (int?)val7.Length; else if (propertyTypeDescription.Type == JsonObjectType.Array) { jsonProperty.MaxItems = (int)val7.Length; } } dynamic val8 = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.StringLengthAttribute", TypeNameStyle.FullName); if ((val8 != null) && propertyTypeDescription.Type == JsonObjectType.String) { jsonProperty.MinLength = (int?)val8.MinimumLength; jsonProperty.MaxLength = (int?)val8.MaximumLength; } dynamic val9 = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.DataTypeAttribute", TypeNameStyle.FullName); if (val9 != null) { dynamic val10 = val9.DataType.ToString(); if (DataTypeFormats.ContainsKey(val10)) jsonProperty.Format = (string)DataTypeFormats[val10]; } } private object ConvertDefaultValue(Type parentType, IEnumerable<Attribute> propertyAttributes, dynamic defaultValueAttribute) { if (((Type)defaultValueAttribute.Value.GetType()).GetTypeInfo().get_IsEnum()) { if (JsonObjectTypeDescription.IsStringEnum(parentType, propertyAttributes, Settings.DefaultEnumHandling)) return defaultValueAttribute.Value.ToString(); return (int)defaultValueAttribute.Value; } return defaultValueAttribute.Value; } private string GetDescription(MemberInfo memberInfo, IEnumerable<Attribute> attributes) { dynamic val = attributes.TryGetIfAssignableTo("System.ComponentModel.DescriptionAttribute", TypeNameStyle.FullName); dynamic val2 = val != null; if ((val2 ? false : true) ? val2 : (val2 & (val.Description != null))) return (string)val.Description; dynamic val3 = attributes.TryGetIfAssignableTo("System.ComponentModel.DataAnnotations.DisplayAttribute", TypeNameStyle.FullName); val2 = (val3 != null); if ((val2 ? false : true) ? val2 : (val2 & (val3.Description != null))) return (string)val3.Description; string xmlDocumentation = memberInfo.GetXmlDocumentation(); if (xmlDocumentation != string.Empty) return xmlDocumentation; return null; } } }