Во-первых, "format"
для использования для относительного URI составляет "uri-reference"
, а не "uri-relative"
, в соответствии с Проверка схемы JSON: Словарь для структурной проверки JSON:draft-handrews-json-schema-validation-02 .
При этом, похоже, в настоящее время нет способа заставить Json.NET Schema сгенерировать формат "uri-reference"
для свойства Uri
с использованием только атрибутов. Если я ищу в 3.0.11 дереве исходных текстов или в последнем (3.0.12) дереве исходных текстов для Formats.UriReference
, я получаю два результата , ни один из которых не связан с генерацией схемы.
Таким образом, будет необходимо использовать custom JSchemaGenerationProvider
для генерации схемы, необходимой для Link
. Тем не менее, генерация схемы требует участия, и JSchema
сама по себе довольно сложна, поэтому мы хотели бы создать для нее схему по умолчанию, а затем настроить свойства схемы по мере необходимости.
Для этого сначала создайте реферат JSchemaGenerationProvider
с именем SchemaCustomizationGenerationProvider
. Он генерирует схемы для выбранных типов, временно отключая себя потокобезопасным способом, а затем рекурсивно вызывая context.Generator.Generate
для создания схемы по умолчанию. Затем эту схему по умолчанию можно настроить перед возвращением:
public abstract class SchemaCustomizationGenerationProvider : JSchemaGenerationProvider
{
[ThreadStatic]
static HashSet<Tuple<Type, Type>> _types;
HashSet<Tuple<Type, Type>> TypesBeingGenerated { get { return _types ?? (_types = new HashSet<Tuple<Type, Type>>()); } }
void PushType(Type type)
{
if (!TypesBeingGenerated.Add(Tuple.Create(GetType(), type)))
{
throw new JSchemaException(string.Format("Unexpected recursion for type {0}", type));
}
}
void PopType(Type type) { TypesBeingGenerated.Remove(Tuple.Create(GetType(), type)); }
bool CurrentlyGeneratingForType(Type type) { return TypesBeingGenerated.Contains(Tuple.Create(GetType(), type)); }
static JsonContract GetContract(JSchemaTypeGenerationContext context) { return context.Generator.ContractResolver.ResolveContract(context.ObjectType); }
static Type GetType(JsonContract contract) { return Nullable.GetUnderlyingType(contract.UnderlyingType) ?? contract.UnderlyingType; }
public sealed override bool CanGenerateSchema(JSchemaTypeGenerationContext context)
{
var contract = GetContract(context);
var type = GetType(contract);
if (CurrentlyGeneratingForType(type))
return false;
if (!CanCustomize(context, contract, type))
return false;
return true;
}
public override JSchema GetSchema(JSchemaTypeGenerationContext context)
{
var contract = GetContract(context);
var type = GetType(contract);
PushType(type);
try
{
return Customize(context, contract, type, context.Generator.Generate(context.ObjectType));
}
finally
{
PopType(type);
}
}
protected virtual bool CanCustomize(JSchemaTypeGenerationContext context, JsonContract contract, Type type)
{
return true;
}
protected abstract JSchema Customize(JSchemaTypeGenerationContext context, JsonContract contract, Type type, JSchema schema);
}
Затем создайте UriReferenceAttribute
, чтобы отметить Uri
свойства, которые следует интерпретировать как относительные Uri, и UriReferenceGenerationProvider
, чтобы исправить схемы для свойств, чтобыПомечено:
[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class UriReferenceAttribute : System.Attribute
{
}
public sealed class UriReferenceGenerationProvider : SchemaCustomizationGenerationProvider
{
public const string UriReference = "uri-reference";
public const string Uri = "uri";
protected override bool CanCustomize(JSchemaTypeGenerationContext context, JsonContract contract, Type type) { return contract is JsonObjectContract; }
protected override JSchema Customize(JSchemaTypeGenerationContext context, JsonContract contract, Type type, JSchema schema)
{
foreach (var property in ((JsonObjectContract)contract).Properties.Where(p => p.PropertyType == typeof(Uri) && p.AttributeProvider.GetAttributes(typeof(UriReferenceAttribute), true).Any()))
{
JSchema propertySchema;
if (!schema.Properties.TryGetValue(property.PropertyName, out propertySchema))
continue;
propertySchema.Format = UriReference;
}
return schema;
}
}
Все это делается, измените Link
следующим образом:
public class Link
{
[JsonProperty("href")] [Required] [MinLength(14)] [MaxLength(255)]
[UriReferenceAttribute] // Add this
public Uri Href { get; set; }
}
И вы можете получить необходимую схему, используя следующий код:
var generator = new JSchemaGenerator()
{
GenerationProviders = { new UriReferenceGenerationProvider() },
// Your pre-existing settings (not shown in your question) here
SchemaIdGenerationHandling = SchemaIdGenerationHandling.TypeName,
};
var generatedSchema = generator.Generate(typeof(Entity));
Примечания:
- Я считаю, что причина того, что
"format": "uri-relative"
сработала для вас, заключается в том, что схема Json.NET, по-видимому, игнорирует нераспознанные форматы во время проверки. "format": "something-completely-unknown"
также успешно проверяет ваш JSON.
Демонстрационная скрипка здесь .