Just a Pseudo-Random Guy Blog

The 'i' in JSON Schema stands for intuitive

Here is a small exercise. Craft a payload that is valid for the following JSON Schema

{
    "oneOf": [
        {
            "type": "object", 
            "properties": {
                "fullName": {"type": "string" }
            }
        },
        {
            "type": "object",
            "properties": {
                "firstName": {"type": "string"},
                "lastName": {"type": "string"}
            }
        }
    ]
}

Chances are that if you never worked with JSON Schema directly, you think values like {"fullName": "John Doe"} or {"firstName": "John", "lastName": "Doe"} are valid. They aren’t. In fact, no value that can match the above schema, except for the empty value (not to be confused with the empty string "")

How is it possible? Well… here is how this beautiful standard works:

So, {"fullName": "John Doe"} matches {"properties": {"name": {"type": "string}}} because nothing rejects "fullName" and nothing requires "name" to be present. Actually, the following values also match {"properties": {"name": {"type": "string}}}: 1, "1", [1], [{"hi": true}], false, null. Notice there is no "type" keyword specified, therefore keywords that don’t apply to a given type are ignored. For anything that is not of type object, the schema becomes {}, which always validates.

This is not a far-fetched and contrived example, schemas like this are commonly generated by tools that generate JSON Schema from CRUD endpoints. For some reason oneOf is present everywhere in real-world schemas. In the best case scenario, the schemas in oneOf are mutually exclusive and the validation library has to waste some CPU cycles checking every subschema, instead of doing an early return as soon as the first subschema matches. In the worst case scenario, your data fails and you will spend some time figuring out why.