DefaultReflectionService
The default reflection service implementation.
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NJsonSchema.Annotations;
using NJsonSchema.Infrastructure;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace NJsonSchema.Generation
{
public class DefaultReflectionService : IReflectionService
{
public virtual JsonTypeDescription GetDescription(Type type, IEnumerable<Attribute> parentAttributes, JsonSchemaGeneratorSettings settings)
{
bool isNullable = IsNullable(type, parentAttributes, settings);
JsonSchemaTypeAttribute jsonSchemaTypeAttribute = type.GetTypeInfo().GetCustomAttribute<JsonSchemaTypeAttribute>() ?? ((parentAttributes != null) ? parentAttributes.OfType<JsonSchemaTypeAttribute>().SingleOrDefault() : null);
if (jsonSchemaTypeAttribute != null) {
type = jsonSchemaTypeAttribute.Type;
if (jsonSchemaTypeAttribute.IsNullableRaw.HasValue)
isNullable = jsonSchemaTypeAttribute.IsNullableRaw.Value;
}
JsonSchemaAttribute jsonSchemaAttribute = type.GetTypeInfo().GetCustomAttribute<JsonSchemaAttribute>() ?? ((parentAttributes != null) ? parentAttributes.OfType<JsonSchemaAttribute>().SingleOrDefault() : null);
if (jsonSchemaAttribute != null) {
JsonObjectType jsonType = (jsonSchemaAttribute.Type != 0) ? jsonSchemaAttribute.Type : JsonObjectType.Object;
string format = (!string.IsNullOrEmpty(jsonSchemaAttribute.Format)) ? jsonSchemaAttribute.Format : null;
return JsonTypeDescription.Create(type, jsonType, isNullable, format);
}
if (type.GetTypeInfo().IsEnum) {
bool flag = IsStringEnum(type, parentAttributes, settings);
return JsonTypeDescription.CreateForEnumeration(type, flag ? JsonObjectType.String : JsonObjectType.Integer, false);
}
if (type == typeof(short) || type == typeof(uint) || type == typeof(ushort))
return JsonTypeDescription.Create(type, JsonObjectType.Integer, false, null);
if (type == typeof(int))
return JsonTypeDescription.Create(type, JsonObjectType.Integer, false, "int32");
if (type == typeof(long) || type == typeof(ulong))
return JsonTypeDescription.Create(type, JsonObjectType.Integer, false, "int64");
if (type == typeof(double) || type == typeof(float))
return JsonTypeDescription.Create(type, JsonObjectType.Number, false, "double");
if (type == typeof(decimal))
return JsonTypeDescription.Create(type, JsonObjectType.Number, false, "decimal");
if (type == typeof(bool))
return JsonTypeDescription.Create(type, JsonObjectType.Boolean, false, null);
if (type == typeof(string) || type == typeof(Type))
return JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, null);
if (type == typeof(char))
return JsonTypeDescription.Create(type, JsonObjectType.String, false, null);
if (type == typeof(Guid))
return JsonTypeDescription.Create(type, JsonObjectType.String, false, "guid");
if (type == typeof(DateTime) || type == typeof(DateTimeOffset) || type.FullName == "NodaTime.OffsetDateTime" || type.FullName == "NodaTime.LocalDateTime" || type.FullName == "NodaTime.ZonedDateTime")
return JsonTypeDescription.Create(type, JsonObjectType.String, false, "date-time");
if (type == typeof(TimeSpan) || type.FullName == "NodaTime.Duration")
return JsonTypeDescription.Create(type, JsonObjectType.String, false, "time-span");
if (type.FullName == "NodaTime.LocalDate")
return JsonTypeDescription.Create(type, JsonObjectType.String, false, "date");
if (type.FullName == "NodaTime.LocalTime")
return JsonTypeDescription.Create(type, JsonObjectType.String, false, "time");
if (type == typeof(Uri))
return JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, "uri");
if (type == typeof(byte))
return JsonTypeDescription.Create(type, JsonObjectType.Integer, false, "byte");
if (type == typeof(byte[]))
return JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, "byte");
if (type.IsAssignableTo("JArray", TypeNameStyle.Name))
return JsonTypeDescription.Create(type, JsonObjectType.Array, isNullable, null);
if (type.IsAssignableTo("JToken", TypeNameStyle.Name) || type.FullName == "System.Dynamic.ExpandoObject" || type == typeof(object))
return JsonTypeDescription.Create(type, JsonObjectType.None, isNullable, null);
if (IsFileType(type, parentAttributes))
return JsonTypeDescription.Create(type, JsonObjectType.File, isNullable, null);
JsonContract jsonContract = settings.ResolveContract(type);
if (IsDictionaryType(type, parentAttributes) && jsonContract is JsonDictionaryContract)
return JsonTypeDescription.CreateForDictionary(type, JsonObjectType.Object, isNullable);
if (IsArrayType(type, parentAttributes) && jsonContract is JsonArrayContract)
return JsonTypeDescription.Create(type, JsonObjectType.Array, isNullable, null);
if (type.Name == "Nullable`1") {
JsonTypeDescription description = GetDescription(type.GetGenericArguments()[0], (parentAttributes != null) ? (from a in parentAttributes
where !(a is JsonSchemaTypeAttribute)
select a) : null, settings);
description.IsNullable = true;
return description;
}
if (jsonContract is JsonStringContract)
return JsonTypeDescription.Create(type, JsonObjectType.String, isNullable, null);
return JsonTypeDescription.Create(type, JsonObjectType.Object, isNullable, null);
}
public virtual bool IsNullable(Type type, IEnumerable<Attribute> parentAttributes, JsonSchemaGeneratorSettings settings)
{
JsonPropertyAttribute jsonPropertyAttribute = (parentAttributes != null) ? parentAttributes.OfType<JsonPropertyAttribute>().SingleOrDefault() : null;
if (jsonPropertyAttribute != null && jsonPropertyAttribute.Required == Required.DisallowNull)
return false;
if (parentAttributes.TryGetIfAssignableTo("NotNullAttribute", TypeNameStyle.Name) != null)
return false;
if (parentAttributes.TryGetIfAssignableTo("CanBeNullAttribute", TypeNameStyle.Name) != null)
return true;
if (type.Name == "Nullable`1")
return true;
if (!(type != typeof(string)) || !type.GetTypeInfo().IsValueType)
return settings.DefaultReferenceTypeNullHandling == ReferenceTypeNullHandling.Null;
return false;
}
protected virtual bool IsFileType(Type type, IEnumerable<Attribute> parentAttributes)
{
if (!(type.Name == "IFormFile") && !type.IsAssignableTo("HttpPostedFile", TypeNameStyle.Name) && !type.IsAssignableTo("HttpPostedFileBase", TypeNameStyle.Name))
return type.GetTypeInfo().GetInterfaces().Any((Type i) => i.Name == "IFormFile");
return true;
}
protected virtual bool IsArrayType(Type type, IEnumerable<Attribute> parentAttributes)
{
if (IsDictionaryType(type, parentAttributes))
return false;
if (type.Name == "ObservableCollection`1")
return true;
if (!type.IsArray) {
if (Enumerable.Contains(type.GetTypeInfo().GetInterfaces(), typeof(IEnumerable))) {
if (!(type.GetTypeInfo().BaseType == (Type)null))
return !Enumerable.Contains(type.GetTypeInfo().BaseType.GetTypeInfo().GetInterfaces(), typeof(IEnumerable));
return true;
}
return false;
}
return true;
}
protected virtual bool IsDictionaryType(Type type, IEnumerable<Attribute> parentAttributes)
{
if (type.Name == "IDictionary`2" || type.Name == "IReadOnlyDictionary`2")
return true;
if (Enumerable.Contains(type.GetTypeInfo().GetInterfaces(), typeof(IDictionary))) {
if (!(type.GetTypeInfo().BaseType == (Type)null))
return !Enumerable.Contains(type.GetTypeInfo().BaseType.GetTypeInfo().GetInterfaces(), typeof(IDictionary));
return true;
}
return false;
}
private bool IsStringEnum(Type type, IEnumerable<Attribute> parentAttributes, JsonSchemaGeneratorSettings settings)
{
bool num = settings.ActualSerializerSettings.Converters.OfType<StringEnumConverter>().Any();
bool flag = HasStringEnumConverter(ReflectionExtensions.GetCustomAttributes(type.GetTypeInfo(), true));
bool flag2 = parentAttributes != null && HasStringEnumConverter(parentAttributes);
return num | flag | flag2;
}
private bool HasStringEnumConverter(IEnumerable<Attribute> attributes)
{
if (attributes == null)
return false;
dynamic val = (attributes != null) ? attributes.FirstOrDefault((Attribute a) => a.GetType().Name == "JsonConverterAttribute") : null;
if (ReflectionExtensions.HasProperty(val, "ConverterType"))
return ((Type)val.ConverterType).IsAssignableTo("StringEnumConverter", TypeNameStyle.Name);
return false;
}
}
}