Я пытаюсь шаг за шагом научиться проверке схемы JSON (на C#) для проекта компании, и я достиг точки, требующей изучения поля, но результаты, которые я вижу, похоже, не соответствуют моим Спецификация обязательного поля. Мне не помешало бы выяснить, почему.
Вот схема, которую я сохранил в файле с именем «TestJsonSchema.txt»…
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/product.schema.json",
"title": "Person",
"description": "Simple person for testing schema validation",
"type": "object",
"properties": {
"FirstName": {
"description": "The person's first name.",
"type": "string"
},
"LastName": {
"description": "The person's last name.",
"type": "string"
},
"Age": {
"description": "Age of the person in years.",
"type": "integer",
"exclusiveMinimum": 18
}
},
"required": ["LastName", "Age"]
}
... а вот тестовая программа...
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
string schemaFilePath = @"C:\testtest\TestJsonSchema.txt";
JSchema schema = JSchema.Parse(File.ReadAllText(schemaFilePath));
// Should be valid.
Person fullPerson = new Person()
{
FirstName = "John",
LastName = "Doe",
Age = 25
};
// I expect this to be valid as there's a LName and age.
Person noFNamePerson = new Person()
{
LastName = "Teller",
Age = 30
};
// I expect this to be invalid as there's no LName
Person noLNamePerson = new Person()
{
FirstName = "Madonna",
Age = 56
};
// I expect this to be valid as both teh LastName and Age are populated.
Person BlankFNamePerson = new Person()
{
FirstName = "",
LastName = "Mason",
Age = 49
};
// I honestly don't know whether this should or shouldn't be valid since
// the required LastName is blank.
// (My first time with these validity checks. Hence all these test cases)
Person BlankLNamePerson = new Person()
{
FirstName = "Cher",
LastName = "",
Age = 120
};
JObject fullObj = JObject.Parse(JsonConvert.SerializeObject(fullPerson));
JObject noFNameObj = JObject.Parse(JsonConvert.SerializeObject(noFNamePerson));
JObject noLNameObj = JObject.Parse(JsonConvert.SerializeObject(noLNamePerson));
JObject blankFNameObj = JObject.Parse(JsonConvert.SerializeObject(BlankFNamePerson));
JObject blankLNameObj = JObject.Parse(JsonConvert.SerializeObject(BlankLNamePerson));
bool fullValid = fullObj.IsValid(schema); // Coming up true. EXPECTED
bool noFNameValid = noFNameObj.IsValid(schema); // Coming up false. !! WHY?? !!
bool noLNameValid = noLNameObj.IsValid(schema); // Coming up false. EXPECTED
bool blankFNameValid = blankFNameObj.IsValid(schema); // Coming up true. EXPECTED
bool blankLNameValid = blankLNameObj.IsValid(schema); // Coming up true. NOT SURE IF CORRECT.
// THESE ARE MY QUICK AND DIRTY WAYS OF FINDING OUT WHAT IS AND ISN'T VALID.
Console.WriteLine("Full person is valid: " + fullValid);
Console.WriteLine("No First Name person is valid: " + noFNameValid);
Console.WriteLine("No Last Name person is valid: " + noLNameValid);
Console.WriteLine("Blank First Name person is valid: " + blankFNameValid);
Console.WriteLine("Blank Last Name person is valid: " + blankLNameValid);
}
}
Для более тщательного изучения вот преобразованный JSON как для человека без имени, так и для человека с пустым именем...
Без имени (подходит неверно)
{"FirstName":null,"LastName":"Teller","Age":30}
Пустое имя (будет действительным)
{"FirstName":"","LastName":"Mason","Age":49}
Итак, вкратце:
Почему человек без имени недействителен, хотя у него есть обязательные поля? Кроме того, достаточно ли наличия пустой строки, как в JSON с пустым именем, для заполнения обязательного поля?
ПРИМЕЧАНИЕ. Эти действительные/недействительные результаты точно такие же, как и при удалении «обязательного» элемента из схемы. Вот почему я вообще создал этот вопрос; Я не уверен, что использую его неправильно.
Спасибо.
Что касается вашей линии
bool noFNameValid = noFNameObj.IsValid(schema); // Coming up false. !! WHY?? !!
Вы пытаетесь подтвердить noFNamePerson
JObject noFNameObj = JObject.Parse(JsonConvert.SerializeObject(noFNamePerson));
Что определяется, как показано ниже
// I expect this to be valid as there's a LName and age.
Person noFNamePerson = new Person()
{
LastName = "Teller",
Age = 30
};
Это правило не выполняется с ошибкой:
Неверный тип. Ожидалась строка, но получено значение Null.
для FirstName
Это можно было бы легко исследовать, если бы вы использовали эту перегрузку метода:
bool noFNameValid = noFNameObj.IsValid(schema, out IList<ValidationError> errors);
который показывает, что не так:
bool blankLNameValid = blankLNameObj.IsValid(schema); // Coming up true. NOT SURE IF CORRECT.
Это для объекта, определенного, как показано ниже:
// I honestly don't know whether this should or shouldn't be valid since
// the required LastName is blank.
// (My first time with these validity checks. Hence all these test cases)
Person BlankLNamePerson = new Person()
{
FirstName = "Cher",
LastName = "",
Age = 120
};
Пустое значение — это все равно значение, а не его отсутствие, поэтому оно и проходит. Если бы вы определили проверку JSON, как показано ниже:
"LastName": {
"minLength": 1,
"description": "The person's last name.",
"type": "string"
}
это не пройдет проверку.
Спасибо большое, Михал. Это охватывает почти все, что мне нужно.
Это неправильное поведение схемы JSON.
Вы пытаетесь проверить noFNamePerson
JObject noFNameObj = JObject.Parse(JsonConvert.SerializeObject(noFNamePerson)); Что определяется, как показано ниже
// Я ожидаю, что это будет правильно, поскольку есть LName и возраст. Человек noFNamePerson = новый Человек() { Фамилия = "Кассир", Возраст = 30 }; Это правило не выполняется с ошибкой:
Неверный тип. Ожидалась строка, но получено значение Null.
для имени
В вашей схеме написано required: [LastName, Age]
Если вы не пройдете FirstName
, это все еще действующий экземпляр. JSON Schema — это язык, основанный на ограничениях, что означает, что проверяются только ПРИМЕНЕННЫЕ ограничения. Все остальное игнорируется
Единственное ограничение, которое вы применили к FirstName
, — это type: string
, IF и ONLY IF, если это свойство определено в экземпляре.
properties:
FirstName:
type: string
Похоже, ваш json неправильно сериализует все свойства и создает экземпляр FirstName
как нулевой.
Вы можете попробовать использовать аннотации, чтобы сообщить сериализатору желаемое поведение.
public class Person
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string FirstName? { get; set; }
[JsonProperty(Required = Required.Always)] = string.Empty;
public string LastName { get; set; }
[JsonProperty(Required = Required.Always)]
public int Age { get; set; }
}
Для справки, «требуется» в вашей схеме JSON просто означает «свойство присутствует». Никакого отношения к стоимости недвижимости это не имеет.