Относительная строка URI не соответствует формату 'uri' - PullRequest
1 голос
/ 12 октября 2019

У меня есть простой класс, который выглядит следующим образом.

public class Entity
{
    [Required] [JsonProperty("_links")]
    public Dictionary<string, Link> Links { get; set; }
}

public class Link
{
    [JsonProperty("href")] [Required] [MinLength(14)] [MaxLength(255)]
    public Uri Href { get; set; }
}

Когда я генерирую схему для этих 2 классов, как показано, свойство Href получает добавленный атрибут "format": "uri":

{
  "$id": "Entity",
  "definitions": {
    "Link": {
      "$id": "Link",
      "type": [
        "object",
        "null"
      ],
      "properties": {
        "href": {
          "type": "string",
          "minLength": 14,
          "maxLength": 255,
          "format": "uri"
        }
      },
      "required": [
        "href"
      ]
    }

Все мои ссылки относительныссылки в этом хранилище данных. Когда я пытаюсь проверить что-то вроде этого фрагмента JSON, я получаю String '/api/sys/map/foo01' does not validate against format 'uri'. Есть ли способ украсить мое свойство Href, чтобы вместо схемы JSon.Net использовать format : uri-relative?

Например, с учетом следующего JSON:

{
  "_links": {
    "foo": {
      "href": "/api/sys/map/foo01"
    }
  }
}

Если я вручную изменю схему с "uri" на "uri-relative", она будет анализироваться нормально. Хотите знать, как я могу получить это в сгенерированной схеме. Я использую JSon.Net Schema 3.0.11.

1 Ответ

1 голос
/ 12 октября 2019

Во-первых, "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.

Демонстрационная скрипка здесь .

...