Использование JsonSchema.Net для проверки JSON, когда схема JSON ссылается на другие схемы.C#

Место общения программистов C#
Ответить
Anonymous
 Использование JsonSchema.Net для проверки JSON, когда схема JSON ссылается на другие схемы.

Сообщение Anonymous »

Я пытаюсь использовать библиотеку JsonSchema.Net для проверки объектов JSON.
Например, у меня есть следующие схемы: основная схема AddressChangedEvent.schema.json, которая ссылается как на EventHeader.schema.json, так и на AddressChangedEventBody.schema.json.
AddressChangedEvent.schema.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.example.com/1.0/address ... event.json",
"title": "Address Changed Event",
"type": "object",
"additionalProperties": false,
"properties": {
"header": {
"$ref": "https://schemas.example.com/1.0/event-h ... chema.json"
},
"body": {
"$ref": "https://schemas.example.com/1.0/address ... chema.json"
}
},
"required": [ "header", "body" ]
}

EventHeader.schema.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.example.com/1.0/event-h ... chema.json",
"title": "Event Header",
"type": "object",
"additionalProperties": false,
"properties": {
"messageId": {
"type": "string",
"minLength": 1
},
"messageType": {
"type": "string",
"minLength": 1
},
"version": {
"type": "string",
"minLength": 1
},
"utcSentDateTime": {
"type": "string",
"format": "date-time"
}
},
"required": [
"messageId",
"messageType",
"version",
"utcSentDateTime"
]
}

AddressChangedEventBody.schema.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.example.com/1.0/address ... chema.json",
"title": "Address Changed Event Body",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"changedBy": {
"type": "string",
"minLength": 1
}
},
"required": [ "name", "changedBy" ]
}

Приложение записывает в базу данных событие AddressChangedEvent как JSON, поэтому я хочу проверить JSON перед записью в базу данных...
Проблема в том, что схемы, на которые ссылаются, не разрешаются во время проверки на соответствие схемам, несмотря на то, что они зарегистрированы в глобальном реестре.

У меня есть JsonSchemaProvider, который считывает все схемы JSON один раз и сохраняет их в реестре схем... но при проверке JSON Я получаю эту ошибку

Json.Schema.RefResolutionException
Не удалось разрешить «https://schemas.example.com/1.0/address ... chema.json»

Вот JsonSchemaProvider (зарегистрированный как синглтон):
public class JsonSchemaProvider : IJsonSchemaProvider
{
private static readonly ConcurrentDictionary RegisteredSchemaIds = new(StringComparer.Ordinal);
private readonly string _basePath;
private readonly ConcurrentDictionary _cache = new(StringComparer.OrdinalIgnoreCase);

public JsonSchemaProvider()
{
var coreAssembly = typeof(IJsonSchemaProvider).Assembly;
var location = Path.GetDirectoryName(coreAssembly.Location)!;
_basePath = Path.Combine(location, "Areas", "JsonSchemas");

if (!Directory.Exists(_basePath))
{
return;
}

foreach (var file in Directory.GetFiles(_basePath, "*.schema.json", SearchOption.AllDirectories))
{
var relativeKey = Path.GetRelativePath(_basePath, file).Replace('\\', '/');
var schema = LoadAndRegisterSchema(file);
_cache.TryAdd(relativeKey, schema);
}
}

public JsonSchema GetSchema(string fileName, string version)
{
var key = $"{version}/{fileName}";
if (_cache.TryGetValue(key, out var schema))
{
return schema;
}

var fullPath = Path.Combine(_basePath, version, fileName);
schema = LoadAndRegisterSchema(fullPath);
_cache[key] = schema;
return schema;
}

private static JsonSchema LoadAndRegisterSchema(string schemaFilePath)
{
var text = File.ReadAllText(schemaFilePath);
var schema = JsonSchema.FromText(File.ReadAllText(schemaFilePath));

using var doc = JsonDocument.Parse(text);
var id = doc.RootElement.TryGetProperty("$id", out var idProperty) &&
Uri.TryCreate(idProperty.GetString(), UriKind.Absolute, out var absoluteId)
? absoluteId
: new Uri(Path.GetFullPath(schemaFilePath));

if (RegisteredSchemaIds.TryAdd(id.ToString(), 0))
{
SchemaRegistry.Global.Register(id, schema);
}

return schema;
}
}

JsonSchemaValidator:

public class JsonSchemaValidator(IJsonSchemaProvider jsonSchemaProvider) : IJsonSchemaValidator
{
public bool TryValidate(string? json, string jsonSchemaFileName, string jsonSchemaVersion, out string errors)
{
ArgumentNullException.ThrowIfNull(json);
ArgumentException.ThrowIfNullOrWhiteSpace(jsonSchemaFileName);
ArgumentException.ThrowIfNullOrWhiteSpace(jsonSchemaVersion);

var schema = jsonSchemaProvider.GetSchema(jsonSchemaFileName, jsonSchemaVersion);
var jsonNode = JsonNode.Parse(json);
var targetNode = jsonNode;

return EvaluateNode(schema, targetNode!, out errors);
}

private static bool EvaluateNode(JsonSchema schema, JsonNode node, out string errors)
{
var evaluationOptions = new EvaluationOptions
{
OutputFormat = OutputFormat.Hierarchical
};

var result = schema.Evaluate(node, evaluationOptions);
if (result.IsValid)
{
errors = string.Empty;
return true;
}

var messages = ExtractErrors(result).ToArray();
errors = string.Join(',', messages);
return false;
}

private static IEnumerable ExtractErrors(EvaluationResults result)
{
if (result.Errors is not null)
{
foreach (var error in result.Errors)
{
yield return $"{result.InstanceLocation}: {error.Value}";
}
}

foreach (var detail in result.Details)
{
foreach (var message in ExtractErrors(detail))
{
yield return message;
}
}
}
}

ОБНОВЛЕНИЕ
Я заметил, что порядок загрузки схем имеет значение, порядок должен быть от наименее зависимой схемы к наиболее зависимой схеме...
if (buildParentSchemaLast) // The order of schema registration matters (doesn't throw RefResolutionException)
{
headerSchema = JsonSchema.FromText(File.ReadAllText("JsonSchemas/EventHeader.schema.json"));
bodySchema = JsonSchema.FromText(File.ReadAllText("JsonSchemas/AddressChangedEventBody.schema.json"));
parentSchema = JsonSchema.FromText(File.ReadAllText("JsonSchemas/AddressChangedEvent.schema.json"));
}
else // throws RefResolutionException
{
headerSchema = JsonSchema.FromText(File.ReadAllText("JsonSchemas/EventHeader.schema.json"));
parentSchema = JsonSchema.FromText(File.ReadAllText("JsonSchemas/AddressChangedEvent.schema.json"));
bodySchema = JsonSchema.FromText(File.ReadAllText("JsonSchemas/AddressChangedEventBody.schema.json"));
}


Подробнее здесь: https://stackoverflow.com/questions/798 ... other-sche
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»