68

I would like to know if I can define a JSON schema (draft 4) that requires at least one of many properties possible for an object. I already know of allOf, anyOf and oneOf but just can't figure out how to use them in the way I want.

Here are some example JSON to illustrate :

// Test Data 1 - Should pass
{

    "email": "hello@example.com",
    "name": "John Doe"
}
// Test Data 2 - Should pass
{
    "id": 1,
    "name": "Jane Doe"
}
// Test Data 3 - Should pass
{
    "id": 1,
    "email": "hello@example.com",
    "name": "John Smith"
}
// Test Data 4 - Should fail, invalid email
{
    "id": 1,
    "email": "thisIsNotAnEmail",
    "name": "John Smith"
}

// Test Data 5 - Should fail, missing one of required properties
{
    "name": "John Doe"
}

I would like to require at least id or email (also accepting both of them) and still pass validation according to format. Using oneOf fails validation if I provide both (test 3), anyOf passes validation even if one of them is not valid (test 4)

Here is my schema :

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "id": "https://example.com",
    "properties": {
        "name": {
            "type": "string"
        }
    },
    "anyOf": [
        {
            "properties": {
                "email": {
                    "type": "string",
                    "format": "email"
                }
            }
        },
        {
            "properties": {
                "id": {
                    "type": "integer"
                }
            }
        }
    ]
}

Can you help me how to achieve correct validation for my use case ?

MaxiWheat
  • 6,133
  • 6
  • 47
  • 76

2 Answers2

135

To require at least one of a set of properties, use required inside a series of anyOf options:

{
    "type": "object",
    "anyOf": [
        {"required": ["id"]},
        {"required": ["email"]}
        // any other properties, in a similar way
    ],
    "properties": {
        // Your actual property definitions here
    }
}

If any of the properties you want is present ("id", "email"), then it will pass the corresponding option in allOf.

Fusion
  • 5,046
  • 5
  • 42
  • 51
cloudfeet
  • 12,156
  • 1
  • 56
  • 57
38

You may use minProperties: number (and maxProperties: number if needed). That would shorten the schema definition:

{
     type: "object",
     minProperties: 1,
     properties: [/* your actual properties definitions */],
     additionalProperties: false
}

Link to documentation: https://json-schema.org/understanding-json-schema/reference/object.html#size

SILENT
  • 3,916
  • 3
  • 38
  • 57
NBR
  • 460
  • 4
  • 3
  • 5
    This is actually a bad solution, because an object, that contains none of the properties defined in the schema while having properties that are not present in the schema, will still validate. Example: `{ "foo": "bar" }` will validate against `{ "type": "object", "minProperties": 1, "properties": { "test": { "type": "string" }, "bar": { "type": "string" } } }`. – Benni Jan 14 '19 at 15:45
  • 12
    @Benni what about `"additionalProperties": false` then? – webjunkie Feb 20 '19 at 12:43
  • 1
    That would work. Read more [here](https://json-schema.org/understanding-json-schema/reference/object.html#properties). – Benni Feb 26 '19 at 21:01