I'm trying to teach myself JSON schema validation (in C#) step-by-step for a company project and I've reached the point of learning field requirement, but the results I'm seeing don't seem to respect my 'required field' specification. I could use a hand figuring out why.
Here's the schema I'm saved into a file called "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"]
}
... and here is the test program...
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);
}
}
For the sake of strict scrutiny, here are the converted JSON for both the No-First-Name Person and the Blank-First-Name Person...
No-First-Name (Coming up Invalid)
{"FirstName":null,"LastName":"Teller","Age":30}
Blank-First-Name (Coming up Valid)
{"FirstName":"","LastName":"Mason","Age":49}
So, long story short:
Why is the no-first-name person invalid even though it has the required fields? Also, is the presence of a blank string, as in the Blank-First-Name JSON, enough to satisfy a required-field?
NOTE These valid/invalid results are exactly the same as when I remove the "required" item from the schema. That's why I created this question in the first place; I'm not sure if I'm using it improperly.
Thanks.
Regarding your line
bool noFNameValid = noFNameObj.IsValid(schema); // Coming up false. !! WHY?? !!
You try to validate noFNamePerson
JObject noFNameObj = JObject.Parse(JsonConvert.SerializeObject(noFNamePerson));
Which is defined as below
// I expect this to be valid as there's a LName and age.
Person noFNamePerson = new Person()
{
LastName = "Teller",
Age = 30
};
It fails this rule with error:
Invalid type. Expected String but got Null.
for FirstName
This could be easily investigated if you would use this method overload:
bool noFNameValid = noFNameObj.IsValid(schema, out IList<ValidationError> errors);
bool blankLNameValid = blankLNameObj.IsValid(schema); // Coming up true. NOT SURE IF CORRECT.
This is for object defined as below:
// 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
};
Empty value is still a value, not its absence, that's wy it passes. If you would define you JSON validation as below:
"LastName": {
"minLength": 1,
"description": "The person's last name.",
"type": "string"
}
it would fail the validation.