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

JsonSchemaGenerator

public class JsonSchemaGenerator
Generates a JsonSchema4 object for a given type.
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NJsonSchema.Annotations; using NJsonSchema.Infrastructure; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; namespace NJsonSchema.Generation { public class JsonSchemaGenerator { public JsonSchemaGeneratorSettings Settings { get; } public JsonSchemaGenerator(JsonSchemaGeneratorSettings settings) { Settings = settings; } public JsonSchema4 Generate(Type type, ISchemaResolver schemaResolver) { return Generate<JsonSchema4>(type, null, null, new JsonSchemaDefinitionAppender(Settings.TypeNameGenerator), schemaResolver); } public JsonSchema4 Generate(Type type, JsonSchema4 rootSchema, IEnumerable<Attribute> parentAttributes, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) { return Generate<JsonSchema4>(type, rootSchema, parentAttributes, schemaDefinitionAppender, schemaResolver); } public TSchemaType Generate<TSchemaType>(Type type, JsonSchema4 rootSchema, IEnumerable<Attribute> parentAttributes, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new { TSchemaType val = HandleSpecialTypes<TSchemaType>(type); if (val != null) return val; val = new TSchemaType(); if (rootSchema == null) rootSchema = val; JsonObjectTypeDescription jsonObjectTypeDescription = JsonObjectTypeDescription.FromType(type, parentAttributes, Settings.DefaultEnumHandling); jsonObjectTypeDescription.ApplyType(val); ApplyExtensionDataAttributes(val, type, parentAttributes); if (val.Type.HasFlag(JsonObjectType.Object)) { if (jsonObjectTypeDescription.IsDictionary) GenerateDictionary(type, rootSchema, val, schemaDefinitionAppender, schemaResolver); else { val.TypeNameRaw = GetTypeName(type); if (schemaResolver.HasSchema(type, false)) { val.SchemaReference = schemaResolver.GetSchema(type, false); return val; } if ((object)val.GetType() != typeof(JsonSchema4)) { val.SchemaReference = Generate(type, rootSchema, parentAttributes, schemaDefinitionAppender, schemaResolver); return val; } val.Description = GetDescription(type.GetTypeInfo(), type.GetTypeInfo().GetCustomAttributes()); GenerateObject(type, val, rootSchema, schemaDefinitionAppender, schemaResolver); } } else if (type.GetTypeInfo().get_IsEnum()) { bool isIntegerEnumeration = jsonObjectTypeDescription.Type == JsonObjectType.Integer; if (schemaResolver.HasSchema(type, isIntegerEnumeration)) { val.Type = jsonObjectTypeDescription.Type; val.SchemaReference = schemaResolver.GetSchema(type, isIntegerEnumeration); return val; } if ((object)val.GetType() != typeof(JsonSchema4)) { val.SchemaReference = Generate(type, rootSchema, parentAttributes, schemaDefinitionAppender, schemaResolver); return val; } LoadEnumerations(type, val, jsonObjectTypeDescription); val.TypeNameRaw = GetTypeName(type); val.Description = type.GetXmlDocumentation(); schemaResolver.AddSchema(type, isIntegerEnumeration, val); } else if (val.Type.HasFlag(JsonObjectType.Array)) { val.Type = JsonObjectType.Array; Type[] genericTypeArguments = GetGenericTypeArguments(type); Type type2 = (genericTypeArguments.Length == 0) ? type.GetElementType() : genericTypeArguments[0]; if ((object)type2 == null) throw new InvalidOperationException("Could not find item type of array type '" + type.FullName + "'."); val.Item = Generate(type2, rootSchema, null, schemaDefinitionAppender, schemaResolver); } return val; } private static void ApplyExtensionDataAttributes<TSchemaType>(TSchemaType schema, Type type, IEnumerable<Attribute> parentAttributes) where TSchemaType : JsonSchema4, new { if (parentAttributes == null) { IEnumerable<JsonSchemaExtensionDataAttribute> customAttributes = type.GetTypeInfo().GetCustomAttributes<JsonSchemaExtensionDataAttribute>(); if (customAttributes.Any()) schema.ExtensionData = customAttributes.ToDictionary((JsonSchemaExtensionDataAttribute a) => a.Property, (JsonSchemaExtensionDataAttribute a) => a.Value); } else { IEnumerable<JsonSchemaExtensionDataAttribute> source = parentAttributes.OfType<JsonSchemaExtensionDataAttribute>(); if (source.Any()) schema.ExtensionData = source.ToDictionary((JsonSchemaExtensionDataAttribute a) => a.Property, (JsonSchemaExtensionDataAttribute a) => a.Value); } } private TSchemaType HandleSpecialTypes<TSchemaType>(Type type) where TSchemaType : JsonSchema4, new { if ((object)type == typeof(object) || (object)type == typeof(JObject)) { TSchemaType val = new TSchemaType(); val.Type = JsonObjectType.Object; val.AllowAdditionalProperties = true; return val; } return null; } private string GetTypeName(Type type) { if (type.IsConstructedGenericType) return type.get_Name().Split(new char[1] { '`' }).First() + GetTypeName(type.GenericTypeArguments[0]); return type.get_Name(); } private void GenerateDictionary<TSchemaType>(Type type, JsonSchema4 rootSchema, TSchemaType schema, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new { Type[] genericTypeArguments = GetGenericTypeArguments(type); if (genericTypeArguments.Length != 2) throw new InvalidOperationException("Could not find value type of dictionary type '" + type.FullName + "'."); Type type2 = genericTypeArguments[1]; if ((object)type2 == typeof(object)) schema.AdditionalPropertiesSchema = new JsonSchema4 { Type = (JsonObjectType.Array | JsonObjectType.Boolean | JsonObjectType.Integer | JsonObjectType.Null | JsonObjectType.Number | JsonObjectType.Object | JsonObjectType.String) }; else schema.AdditionalPropertiesSchema = Generate(type2, rootSchema, null, schemaDefinitionAppender, schemaResolver); schema.AllowAdditionalProperties = true; } public Type[] GetGenericTypeArguments(Type type) { Type[] genericTypeArguments = type.GenericTypeArguments; while ((object)type != null && (object)type != typeof(object) && genericTypeArguments.Length == 0) { type = type.GetTypeInfo().get_BaseType(); if ((object)type != null) genericTypeArguments = type.GenericTypeArguments; } return genericTypeArguments; } protected virtual void GenerateObject<TSchemaType>(Type type, TSchemaType schema, JsonSchema4 rootSchema, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) where TSchemaType : JsonSchema4, new { schemaResolver.AddSchema(type, false, schema); schema.AllowAdditionalProperties = false; GeneratePropertiesAndInheritance(type, schema, rootSchema, schemaDefinitionAppender, schemaResolver); if (Settings.GenerateKnownTypes) GenerateKnownTypes(type, rootSchema, schemaDefinitionAppender, schemaResolver); } private void GeneratePropertiesAndInheritance(Type type, JsonSchema4 schema, JsonSchema4 rootSchema, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) { string[] properties = GetTypeProperties(type); foreach (PropertyInfo item in type.GetTypeInfo().DeclaredProperties.Where(delegate(PropertyInfo p) { if (properties != null) return properties.Contains(p.Name); return true; })) { LoadProperty(type, item, schema, rootSchema, schemaDefinitionAppender, schemaResolver); } GenerateInheritance(type, schema, rootSchema, schemaDefinitionAppender, schemaResolver); } private void GenerateKnownTypes(Type type, JsonSchema4 rootSchema, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) { foreach (KnownTypeAttribute customAttribute in type.GetTypeInfo().GetCustomAttributes<KnownTypeAttribute>()) { bool isIntegerEnumeration = JsonObjectTypeDescription.FromType(customAttribute.Type, null, Settings.DefaultEnumHandling).Type == JsonObjectType.Integer; if (!schemaResolver.HasSchema(customAttribute.Type, isIntegerEnumeration)) { JsonSchema4 jsonSchema = Generate(customAttribute.Type, rootSchema, null, schemaDefinitionAppender, schemaResolver); schemaDefinitionAppender.Append(rootSchema, jsonSchema.ActualSchema); } } } private void GenerateInheritance(Type type, JsonSchema4 schema, JsonSchema4 rootSchema, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) { Type baseType = type.GetTypeInfo().get_BaseType(); if ((object)baseType != null && (object)baseType != typeof(object)) { if (Settings.FlattenInheritanceHierarchy) GeneratePropertiesAndInheritance(baseType, schema, rootSchema, schemaDefinitionAppender, schemaResolver); else { JsonSchema4 item = Generate(baseType, rootSchema, null, schemaDefinitionAppender, schemaResolver); schema.AllOf.Add(item); } } } 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[] enumNames = GetEnumNames(type, typeDescription); foreach (string text in enumNames) { if (typeDescription.Type == JsonObjectType.Integer) { object item = Convert.ChangeType(Enum.Parse(type, text), Enum.GetUnderlyingType(type)); schema.Enumeration.Add(item); } else schema.Enumeration.Add(text); schema.EnumerationNames.Add(text); } } private string[] GetEnumNames(Type type, JsonObjectTypeDescription typeDescription) { if (typeDescription.Type == JsonObjectType.String) return Enum.GetNames(type).Select(delegate(string name) { IEnumerable<Attribute> customAttributes = type.GetTypeInfo().GetDeclaredField(name).GetCustomAttributes(); object obj = TryGetAttribute(customAttributes, "System.Runtime.Serialization.EnumMemberAttribute"); object obj2 = (dynamic)obj != null; if ((((dynamic)obj2) ? false : true) ? obj2 : ((dynamic)obj2 & !string.IsNullOrEmpty(((dynamic)obj).Value))) return (string)((dynamic)obj).Value; return name; }).ToArray(); return Enum.GetNames(type); } private void LoadProperty(Type parentType, PropertyInfo property, JsonSchema4 parentSchema, JsonSchema4 rootSchema, ISchemaDefinitionAppender schemaDefinitionAppender, ISchemaResolver schemaResolver) { Type type = property.PropertyType; JsonObjectTypeDescription jsonObjectTypeDescription = JsonObjectTypeDescription.FromType(type, property.GetCustomAttributes(), Settings.DefaultEnumHandling); Attribute[] array = property.GetCustomAttributes().ToArray(); if (!IsPropertyIgnored(parentType, array)) { if (type.get_Name() == "Nullable`1") type = type.GenericTypeArguments[0]; bool flag = !jsonObjectTypeDescription.IsDictionary && (jsonObjectTypeDescription.Type.HasFlag(JsonObjectType.Object) || jsonObjectTypeDescription.IsEnum); JsonProperty jsonProperty; if (flag) { JsonSchema4 jsonSchema = Generate(type, rootSchema, property.GetCustomAttributes(), schemaDefinitionAppender, schemaResolver); if (Settings.PropertyNullHandling == PropertyNullHandling.OneOf) { jsonProperty = new JsonProperty(); jsonProperty.OneOf.Add(new JsonSchema4 { SchemaReference = jsonSchema.ActualSchema }); } else jsonProperty = new JsonProperty { SchemaReference = jsonSchema.ActualSchema }; } else { jsonProperty = Generate<JsonProperty>(type, rootSchema, property.GetCustomAttributes(), schemaDefinitionAppender, schemaResolver); jsonObjectTypeDescription.ApplyType(jsonProperty); } string propertyName = JsonPathUtilities.GetPropertyName(property); parentSchema.Properties.Add(propertyName, jsonProperty); Attribute attribute = TryGetAttribute(array, "System.ComponentModel.DataAnnotations.RequiredAttribute"); JsonPropertyAttribute customAttribute = property.GetCustomAttribute<JsonPropertyAttribute>(); bool flag2 = customAttribute != null && (customAttribute.Required == Required.Always || customAttribute.Required == Required.AllowNull); bool num = attribute != null; if (num | flag2) parentSchema.RequiredProperties.Add(propertyName); bool flag3 = customAttribute != null && customAttribute.Required == Required.AllowNull; if (!num && (!jsonObjectTypeDescription.IsAlwaysRequired | flag3)) { if (Settings.PropertyNullHandling == PropertyNullHandling.OneOf) { if (flag) jsonProperty.OneOf.Add(new JsonSchema4 { Type = JsonObjectType.Null }); else jsonProperty.Type |= JsonObjectType.Null; } } else if (Settings.PropertyNullHandling == PropertyNullHandling.Required && !parentSchema.RequiredProperties.Contains(propertyName)) { parentSchema.RequiredProperties.Add(propertyName); } dynamic val = TryGetAttribute(array, "System.ComponentModel.ReadOnlyAttribute"); if (val != null) jsonProperty.IsReadOnly = ((byte)val.IsReadOnly != 0); jsonProperty.Description = GetDescription(property, array); ApplyPropertyAnnotations(jsonProperty, array, jsonObjectTypeDescription); } } private static bool IsPropertyIgnored(Type parentType, Attribute[] propertyAttributes) { if (propertyAttributes.Any((Attribute a) => a is JsonIgnoreAttribute)) return true; if (parentType.GetTypeInfo().GetCustomAttribute<DataContractAttribute>() != null && !propertyAttributes.Any((Attribute a) => a is DataMemberAttribute) && !propertyAttributes.Any((Attribute a) => a is JsonPropertyAttribute)) return true; return false; } public void ApplyPropertyAnnotations(JsonSchema4 jsonProperty, IEnumerable<Attribute> attributes, JsonObjectTypeDescription propertyTypeDescription) { dynamic val = TryGetAttribute(attributes, "System.ComponentModel.DataAnnotations.DisplayAttribute"); dynamic val2 = val != null; if ((val2 ? false : true) ? val2 : (val2 & (val.Name != null))) jsonProperty.Title = (string)val.Name; dynamic val3 = TryGetAttribute(attributes, "System.ComponentModel.DefaultValueAttribute"); val2 = (val3 != null); if ((val2 ? false : true) ? val2 : (val2 & (val3.Value != null))) jsonProperty.Default = (object)val3.Value; dynamic val4 = TryGetAttribute(attributes, "System.ComponentModel.DataAnnotations.RegularExpressionAttribute"); if (val4 != null) jsonProperty.Pattern = (string)val4.Pattern; if (propertyTypeDescription.Type == JsonObjectType.Number || propertyTypeDescription.Type == JsonObjectType.Integer) { dynamic val5 = TryGetAttribute(attributes, "System.ComponentModel.DataAnnotations.RangeAttribute"); 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 = TryGetAttribute(attributes, "System.ComponentModel.DataAnnotations.MinLengthAttribute"); 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 = TryGetAttribute(attributes, "System.ComponentModel.DataAnnotations.MaxLengthAttribute"); 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; } } } private string GetDescription(MemberInfo memberInfo, IEnumerable<Attribute> attributes) { dynamic val = TryGetAttribute(attributes, "System.ComponentModel.DescriptionAttribute"); dynamic val2 = val != null; if ((val2 ? false : true) ? val2 : (val2 & (val.Description != null))) return (string)val.Description; dynamic val3 = TryGetAttribute(attributes, "System.ComponentModel.DataAnnotations.DisplayAttribute"); 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; } private Attribute TryGetAttribute(IEnumerable<Attribute> attributes, string attributeType) { return attributes.FirstOrDefault((Attribute a) => a.GetType().FullName == attributeType); } } }