in src/Elastic.Apm/Libraries/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs [203:330]
private JsonSchema GenerateInternal(Type type, Required valueRequired, bool required)
{
ValidationUtils.ArgumentNotNull(type, nameof(type));
var resolvedId = GetTypeId(type, false);
var explicitId = GetTypeId(type, true);
if (!StringUtils.IsNullOrEmpty(resolvedId))
{
var resolvedSchema = _resolver.GetSchema(resolvedId);
if (resolvedSchema != null)
{
// resolved schema is not null but referencing member allows nulls
// change resolved schema to allow nulls. hacky but what are ya gonna do?
if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null))
resolvedSchema.Type |= JsonSchemaType.Null;
if (required && resolvedSchema.Required != true) resolvedSchema.Required = true;
return resolvedSchema;
}
}
// test for unresolved circular reference
if (_stack.Any(tc => tc.Type == type))
throw new JsonException(
"Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property."
.FormatWith(CultureInfo.InvariantCulture, type));
var contract = ContractResolver.ResolveContract(type);
var converter = contract.Converter ?? contract.InternalConverter;
Push(new TypeSchema(type, new JsonSchema()));
if (explicitId != null) CurrentSchema.Id = explicitId;
if (required) CurrentSchema.Required = true;
CurrentSchema.Title = GetTitle(type);
CurrentSchema.Description = GetDescription(type);
if (converter != null)
{
// todo: Add GetSchema to JsonConverter and use here?
CurrentSchema.Type = JsonSchemaType.Any;
}
else
{
switch (contract.ContractType)
{
case JsonContractType.Object:
CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
CurrentSchema.Id = GetTypeId(type, false);
GenerateObjectSchema(type, (JsonObjectContract)contract);
break;
case JsonContractType.Array:
CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired);
CurrentSchema.Id = GetTypeId(type, false);
var arrayAttribute = JsonTypeReflector.GetCachedAttribute<JsonArrayAttribute>(type);
var allowNullItem = arrayAttribute == null || arrayAttribute.AllowNullItems;
var collectionItemType = ReflectionUtils.GetCollectionItemType(type);
if (collectionItemType != null)
{
CurrentSchema.Items = new List<JsonSchema>();
CurrentSchema.Items.Add(GenerateInternal(collectionItemType, !allowNullItem ? Required.Always : Required.Default, false));
}
break;
case JsonContractType.Primitive:
CurrentSchema.Type = GetJsonSchemaType(type, valueRequired);
if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum() && !type.IsDefined(typeof(FlagsAttribute), true))
{
CurrentSchema.Enum = new List<JToken>();
var enumValues = EnumUtils.GetEnumValuesAndNames(type);
for (var i = 0; i < enumValues.Names.Length; i++)
{
var v = enumValues.Values[i];
var value = JToken.FromObject(Enum.ToObject(type, v));
CurrentSchema.Enum.Add(value);
}
}
break;
case JsonContractType.String:
var schemaType = !ReflectionUtils.IsNullable(contract.UnderlyingType)
? JsonSchemaType.String
: AddNullType(JsonSchemaType.String, valueRequired);
CurrentSchema.Type = schemaType;
break;
case JsonContractType.Dictionary:
CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
Type keyType;
Type valueType;
ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType);
if (keyType != null)
{
var keyContract = ContractResolver.ResolveContract(keyType);
// can be converted to a string
if (keyContract.ContractType == JsonContractType.Primitive)
CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false);
}
break;
#if HAVE_BINARY_SERIALIZATION
case JsonContractType.Serializable:
CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
CurrentSchema.Id = GetTypeId(type, false);
GenerateISerializableContract(type, (JsonISerializableContract)contract);
break;
#endif
#if HAVE_DYNAMIC
case JsonContractType.Dynamic:
#endif
case JsonContractType.Linq:
CurrentSchema.Type = JsonSchemaType.Any;
break;
default:
throw new JsonException("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract));
}
}
return Pop().Schema;
}