DefaultReflectionService
The default reflection service implementation.
using Namotion.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NJsonSchema.Annotations;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace NJsonSchema.Generation
{
public class DefaultReflectionService : IReflectionService
{
public JsonTypeDescription GetDescription(ContextualType contextualType, JsonSchemaGeneratorSettings settings)
{
return GetDescription(contextualType, settings.DefaultReferenceTypeNullHandling, settings);
}
public virtual JsonTypeDescription GetDescription(ContextualType contextualType, ReferenceTypeNullHandling defaultReferenceTypeNullHandling, JsonSchemaGeneratorSettings settings)
{
Type type = contextualType.get_OriginalType();
bool isNullable = IsNullable(contextualType, defaultReferenceTypeNullHandling);
JsonSchemaTypeAttribute attribute = contextualType.GetAttribute<JsonSchemaTypeAttribute>();
if (attribute != null) {
type = attribute.Type;
contextualType = ContextualTypeExtensions.ToContextualType(type);
if (attribute.IsNullableRaw.HasValue)
isNullable = attribute.IsNullableRaw.Value;
}
JsonSchemaAttribute attribute2 = contextualType.GetAttribute<JsonSchemaAttribute>();
if (attribute2 != null) {
JsonObjectType jsonType = (attribute2.Type != 0) ? attribute2.Type : JsonObjectType.Object;
string format = (!string.IsNullOrEmpty(attribute2.Format)) ? attribute2.Format : null;
return JsonTypeDescription.Create(contextualType, jsonType, isNullable, format);
}
if (LegacyTypeExtensions.GetTypeInfo(type).IsEnum) {
bool flag = IsStringEnum(contextualType, settings.ActualSerializerSettings);
return JsonTypeDescription.CreateForEnumeration(contextualType, flag ? JsonObjectType.String : JsonObjectType.Integer, false);
}
if (type == typeof(short) || type == typeof(uint) || type == typeof(ushort))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, null);
if (type == typeof(int))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, "int32");
if (type == typeof(long) || type == typeof(ulong))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, "int64");
if (type == typeof(double))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Number, false, "double");
if (type == typeof(float))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Number, false, "float");
if (type == typeof(decimal))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Number, false, "decimal");
if (type == typeof(bool))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Boolean, false, null);
if (type == typeof(string) || type == typeof(Type))
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, null);
if (type == typeof(char))
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, null);
if (type == typeof(Guid))
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, "guid");
if (type == typeof(DateTime) || type == typeof(DateTimeOffset) || type.FullName == "NodaTime.OffsetDateTime" || type.FullName == "NodaTime.LocalDateTime" || type.FullName == "NodaTime.ZonedDateTime" || type.FullName == "NodaTime.Instant")
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, "date-time");
if (type == typeof(TimeSpan) || type.FullName == "NodaTime.Duration")
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, "time-span");
if (type.FullName == "NodaTime.LocalDate")
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, "date");
if (type.FullName == "NodaTime.LocalTime")
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, false, "time");
if (type == typeof(Uri))
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, "uri");
if (type == typeof(byte))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Integer, false, "byte");
if (type == typeof(byte[]))
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, "byte");
if (TypeExtensions.IsAssignableToTypeName(type, "JArray", 0))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Array, isNullable, null);
if (TypeExtensions.IsAssignableToTypeName(type, "JToken", 0) || type.FullName == "System.Dynamic.ExpandoObject" || type == typeof(object))
return JsonTypeDescription.Create(contextualType, JsonObjectType.None, isNullable, null);
if (IsBinary(contextualType)) {
if (settings.SchemaType == SchemaType.Swagger2)
return JsonTypeDescription.Create(contextualType, JsonObjectType.File, isNullable, null);
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, "binary");
}
if (contextualType.get_IsNullableType()) {
JsonTypeDescription description = GetDescription(contextualType.get_OriginalGenericArguments()[0], defaultReferenceTypeNullHandling, settings);
description.IsNullable = true;
return description;
}
JsonContract jsonContract = settings.ResolveContract(type);
if (IsDictionaryType(contextualType) && jsonContract is JsonDictionaryContract)
return JsonTypeDescription.CreateForDictionary(contextualType, JsonObjectType.Object, isNullable);
if (IsIAsyncEnumerableType(contextualType) || (IsArrayType(contextualType) && jsonContract is JsonArrayContract))
return JsonTypeDescription.Create(contextualType, JsonObjectType.Array, isNullable, null);
if (jsonContract is JsonStringContract)
return JsonTypeDescription.Create(contextualType, JsonObjectType.String, isNullable, null);
return JsonTypeDescription.Create(contextualType, JsonObjectType.Object, isNullable, null);
}
public virtual bool IsNullable(ContextualType contextualType, ReferenceTypeNullHandling defaultReferenceTypeNullHandling)
{
JsonPropertyAttribute contextAttribute = contextualType.GetContextAttribute<JsonPropertyAttribute>();
if (contextAttribute != null && contextAttribute.Required == Required.DisallowNull)
return false;
if (EnumerableExtensions.FirstAssignableToTypeNameOrDefault<Attribute>((IEnumerable<Attribute>)contextualType.get_ContextAttributes(), "NotNullAttribute", 0) != null)
return false;
if (EnumerableExtensions.FirstAssignableToTypeNameOrDefault<Attribute>((IEnumerable<Attribute>)contextualType.get_ContextAttributes(), "CanBeNullAttribute", 0) != null)
return true;
if ((int)contextualType.get_Nullability() != 0)
return (int)contextualType.get_Nullability() == 2;
if (!(contextualType.get_Type() != typeof(string)) || !contextualType.get_TypeInfo().IsValueType)
return defaultReferenceTypeNullHandling != ReferenceTypeNullHandling.NotNull;
return false;
}
public bool IsStringEnum(ContextualType contextualType, JsonSerializerSettings serializerSettings)
{
if (!contextualType.get_TypeInfo().IsEnum)
return false;
if (!serializerSettings.Converters.OfType<StringEnumConverter>().Any())
return HasStringEnumConverter(contextualType);
return true;
}
protected virtual bool IsBinary(ContextualType contextualType)
{
if (!(contextualType.get_TypeName() == "IFormFile") && !TypeExtensions.IsAssignableToTypeName(contextualType, "HttpPostedFile", 0) && !TypeExtensions.IsAssignableToTypeName(contextualType, "HttpPostedFileBase", 0))
return contextualType.get_TypeInfo().GetInterfaces().Any((Type i) => i.Name == "IFormFile");
return true;
}
private bool IsIAsyncEnumerableType(ContextualType contextualType)
{
return contextualType.get_TypeName() == "IAsyncEnumerable`1";
}
protected virtual bool IsArrayType(ContextualType contextualType)
{
if (IsDictionaryType(contextualType))
return false;
if (contextualType.get_TypeName() == "ObservableCollection`1")
return true;
if (!contextualType.get_Type().IsArray) {
if (Enumerable.Contains(contextualType.get_Type().GetInterfaces(), typeof(IEnumerable))) {
if (!(contextualType.get_TypeInfo().BaseType == (Type)null))
return !Enumerable.Contains(LegacyTypeExtensions.GetTypeInfo(contextualType.get_TypeInfo().BaseType).GetInterfaces(), typeof(IEnumerable));
return true;
}
return false;
}
return true;
}
protected virtual bool IsDictionaryType(ContextualType contextualType)
{
if (contextualType.get_TypeName() == "IDictionary`2" || contextualType.get_TypeName() == "IReadOnlyDictionary`2")
return true;
if (Enumerable.Contains(contextualType.get_Type().GetInterfaces(), typeof(IDictionary))) {
if (!(contextualType.get_TypeInfo().BaseType == (Type)null))
return !Enumerable.Contains(LegacyTypeExtensions.GetTypeInfo(contextualType.get_TypeInfo().BaseType).GetInterfaces(), typeof(IDictionary));
return true;
}
return false;
}
private bool HasStringEnumConverter(ContextualType contextualType)
{
IEnumerable<Attribute> attributes = contextualType.get_Attributes();
dynamic val = (attributes != null) ? attributes.FirstOrDefault((Attribute a) => a.GetType().Name == "JsonConverterAttribute") : null;
dynamic val2 = val != null;
if ((val2 ? false : true) ? val2 : (val2 & ObjectExtensions.HasProperty(val, "ConverterType")))
return TypeExtensions.IsAssignableToTypeName((Type)val.ConverterType, "StringEnumConverter", 0);
return false;
}
}
}