123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- using Newtonsoft.Json;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
- using System.Linq;
- using System.Reflection;
- using VCommon.Reflection;
- using VCommon.VApplication.DataAnnotations;
- namespace VCommon.VOpenApi.Docgen
- {
- internal class SchemaCrawler
- {
- public static IDictionary<string, Schema> Crawl(IEnumerable<Type> modelsToCrawl, string refPath = "#/definitions/")
- {
- var dic = new Dictionary<string, Schema>();
- foreach (var item in modelsToCrawl) CrawlInternal(dic, item, refPath);
- return dic;
- }
- private static void CrawlInternal(IDictionary<string, Schema> dic, Type toCrawl, string refPath)
- {
- if (toCrawl.IsPrimitiveType())
- {
- var primitiveTypeName = toCrawl.GetPrimitiveTypeName();
- if (dic.ContainsKey(primitiveTypeName)) return;
- dic[primitiveTypeName] = CreatePrimitiveSchema(toCrawl);
- }
- else
- {
- var fullName = toCrawl.GetFriendlyTypeName();
- if (dic.ContainsKey(fullName)) return;
- if (toCrawl.IsArray())
- {
- var arrayElementType = toCrawl.GetArrayElementType();
- dic[fullName] = CreateArrayTypeSchema(refPath, arrayElementType.GetFriendlyTypeName());
- CrawlInternal(dic, arrayElementType, refPath);
- }
- else
- {
- var typeSchema = dic[fullName] = new Schema
- {
- type = "object",
- properties = new Dictionary<string, Schema>(),
- required = new List<string>()
- };
- foreach (var propertyInfo in toCrawl.GetPublicInstanceProperties())
- {
- if (propertyInfo.IsDefined(typeof(JsonIgnoreAttribute))) continue;
- if (propertyInfo.IsDefined(typeof(OutputIgnoreAttribute))) continue;
- if (propertyInfo.IsDefined(typeof(IgnoreSchema))) continue;
- var propertySchema = new Schema();
- var propertyName = propertyInfo.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? propertyInfo.Name;
- if (propertyInfo.IsDefined(typeof(RequiredAttribute))) typeSchema.required.Add(propertyInfo.Name);
- if (propertyInfo.IsDefinedIncludeInterface(typeof(ExampleValueAttribute))) propertySchema.example = propertyInfo.GetCustomAttributeIncludeInterface<ExampleValueAttribute>().First().Value;
- var ptype = propertyInfo.PropertyType;
- if (ptype.IsPrimitiveType())
- {
- propertySchema.@ref = refPath + ptype.GetPrimitiveTypeName();
- CrawlInternal(dic, ptype, refPath);
- }
- else if (ptype.IsArray())
- {
- var arrayElementType = ptype.GetArrayElementType();
- propertySchema = CreateArrayTypeSchema(refPath, arrayElementType.GetFriendlyTypeName());
- CrawlInternal(dic, arrayElementType, refPath);
- }
- else
- {
- propertySchema.@ref = refPath + ptype.GetFriendlyTypeName();
- CrawlInternal(dic, ptype, refPath);
- }
- typeSchema.properties[propertyName] = propertySchema;
- }
- }
- }
- }
- private static Schema CreateArrayTypeSchema(string refPath, string fullName)
- {
- return new Schema
- {
- type = "array",
- items = new Schema
- {
- @ref = refPath + fullName
- }
- };
- }
- private static Schema CreatePrimitiveSchema(Type type)
- {
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- type = type.GetGenericArguments()[0];
- }
- if (type.IsEnum)
- return CreateEnumSchema(type);
- switch (type.FullName)
- {
- case "System.Boolean":
- return new Schema { type = "boolean" };
- case "System.Byte":
- case "System.SByte":
- case "System.Int16":
- case "System.UInt16":
- case "System.Int32":
- case "System.UInt32":
- return new Schema { type = "integer", format = "int32" };
- case "System.Int64":
- case "System.UInt64":
- return new Schema { type = "integer", format = "int64" };
- case "System.Single":
- return new Schema { type = "number", format = "float" };
- case "System.Double":
- case "System.Decimal":
- return new Schema { type = "number", format = "double" };
- case "System.Byte[]":
- return new Schema { type = "string", format = "byte" };
- case "System.DateTime":
- case "System.DateTimeOffset":
- return new Schema { type = "string", format = "date-time" };
- case "System.Guid": return new Schema { type = "string", format = "uuid", example = Guid.Empty };
- case "System.Object": return new Schema { type = "object" };
- default: return new Schema { type = "string" };
- }
- }
- private static Schema CreateEnumSchema(IReflect enumType)
- {
- return new Schema
- {
- type = "string",
- @enum = enumType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
- .Select(fieldInfo => (object)fieldInfo.Name)
- .ToArray()
- };
- }
- }
- }
|