using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Dynamic; using System.Linq; using System.Reflection; namespace CSharpObjectJsonSchema.Controllers { public static class ControllerExtensions { public static object GetJsonSchema(this ControllerBase controller) { if (controller is null) { throw new ArgumentNullException(nameof(controller)); } var metadata = controller.HttpContext.RequestServices.GetRequiredService().GetMetadataForType(typeof(T)); return CreateJson(controller, metadata as DefaultModelMetadata); } public static object GetJsonSchema(this ControllerBase controller, Type type) { if (controller is null) { throw new ArgumentNullException(nameof(controller)); } var metadata = controller.HttpContext.RequestServices.GetRequiredService().GetMetadataForType(type); return CreateJson(controller, metadata as DefaultModelMetadata); } public static object CreateJson(this ControllerBase controller, DefaultModelMetadata metadata) { if (metadata is null) { return null; } try { dynamic json = new ExpandoObject(); var dictionary = (IDictionary)json; //type if (metadata.IsComplexType && metadata.IsCollectionType) { json.type = "array"; json.items = controller.GetJsonSchema(metadata.ModelType.GenericTypeArguments[0]); } else if (metadata.IsEnum) { json.type = "string"; dictionary["enum"] = metadata.IsNullableValueType ? Enum.GetNames(metadata.ModelType.GenericTypeArguments[0]) : Enum.GetNames(metadata.ModelType); } else { var modelType = metadata.IsNullableValueType ? metadata.ModelType.GenericTypeArguments[0] : metadata.ModelType; if (modelType == typeof(string)) { json.type = "string"; } else if (modelType == typeof(int) || modelType == typeof(long)) { json.type = "integer"; } else if (modelType == typeof(float) || modelType == typeof(double) || modelType == typeof(decimal)) { json.type = "number"; } else if (modelType == typeof(bool)) { json.type = "boolean"; } else if (modelType == typeof(DateTime)) { json.type = "string"; } else { json.type = "object"; } } //title if (metadata.DisplayName != null) { json.title = metadata.DisplayName; } foreach (var attribute in metadata.Attributes.Attributes) { if (attribute is DescriptionAttribute descriptionAttribute) {//description json.description = descriptionAttribute.Description; } else if (attribute is RegularExpressionAttribute regularExpressionAttribute) {//pattern json.pattern = regularExpressionAttribute.Pattern; } else if (attribute is DataTypeAttribute dataTypeAttribute) {//format if (dataTypeAttribute.DataType == DataType.DateTime) { json.format = "date-time"; } else if (dataTypeAttribute.DataType == DataType.Time) { json.format = "time"; } else if (dataTypeAttribute.DataType == DataType.EmailAddress) { json.format = "email"; } else if (dataTypeAttribute.DataType == DataType.Url) { json.format = "uri"; } else if (dataTypeAttribute.DataType == DataType.Custom) {//自定义 json.format = dataTypeAttribute.GetDataTypeName().ToLower(); } else { json.format = dataTypeAttribute.DataType.ToString().ToLower(); } } else if (attribute is ReadOnlyAttribute readOnlyAttribute) {//readOnly json.readOnly = readOnlyAttribute.IsReadOnly; } else if (attribute is MinLengthAttribute minLengthAttribute) {//minLength json.minLength = minLengthAttribute.Length; } else if (attribute is MaxLengthAttribute maxLengthAttribute) {//maxLength json.maxLength = maxLengthAttribute.Length; } else if (attribute is StringLengthAttribute stringLengthAttribute) {//minLength,maxLength json.minLength = stringLengthAttribute.MinimumLength; json.maxLength = stringLengthAttribute.MaximumLength; } else if (attribute is RangeAttribute rangeAttribute) {//minimum,maximum json.minimum = rangeAttribute.Minimum; json.maximum = rangeAttribute.Maximum; } else if (attribute is ScaffoldColumnAttribute scaffoldColumnAttribute) {//scaffold 自定义 json.scaffold = scaffoldColumnAttribute.Scaffold; } else if (attribute is HiddenInputAttribute hiddenInputAttribute) {//hidden 自定义 json.hidden = hiddenInputAttribute.DisplayValue; } else if (attribute is UIHintAttribute uIHintAttribute) {//ui 自定义 json.ui = uIHintAttribute.UIHint; } else if (attribute is CompareAttribute compareAttribute) {//compare 自定义 json.compare = compareAttribute.OtherProperty; } else if (attribute is RemoteAttribute remoteAttribute) {//remote 自定义 var routeData = attribute.GetType().GetProperty("RouteData", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(remoteAttribute) as RouteValueDictionary; json.remote = new { url = controller.Url.Action(routeData["action"].ToString(), routeData["controller"].ToString(), new { Area = routeData["area"].ToString() }), remoteAttribute.AdditionalFields, //remoteAttribute. }; } } dynamic properties = new ExpandoObject(); var propertiesDictionary = (IDictionary)properties; var requiredList = new List(); if (metadata.IsComplexType && !metadata.IsCollectionType && metadata.Properties.Any()) { foreach (var item in metadata.Properties) { var modelMetadataItem = item as DefaultModelMetadata; propertiesDictionary[item.PropertyName] = CreateJson(controller, modelMetadataItem); if (modelMetadataItem.IsRequired) { requiredList.Add(item.PropertyName); } } json.properties = properties; if (requiredList.Any()) { json.required = requiredList; } } return json; } catch (Exception ex) { return null; } } } }