How do I require one field or another or (one of two others) but not all of them? How do I require one field or another or (one of two others) but not all of them? json json

How do I require one field or another or (one of two others) but not all of them?


The problem is the "not" semantics. "not required" does not mean "inclusion forbidden". It just means that you don't have to add it in order to validate that schema.

However, you can use "oneOf" to satisfy your specification in a simpler way. Remember that it means that "just one of these schemas can validate". The following schema achieves the property switching you are attempting to solve:

{    "$schema": "http://json-schema.org/draft-04/schema#",    "type": "object",    "required": [        "unrelatedA"    ],    "properties": {        "unrelatedA": {            "type": "string"        },        "fileNames": {            "type": "array"        },        "copyAll": {            "type": "boolean"        },        "matchesFiles": {            "type": "array"        },        "doesntMatchFiles": {            "type": "array"        }    },    "oneOf": [        {            "required": [                "copyAll"            ]        },        {            "required": [                "fileNames"            ]        },        {            "anyOf": [                {                    "required": [                        "matchesFiles"                    ]                },                {                    "required": [                        "doesntMatchFiles"                    ]                }            ]        }    ]}


If the property having a value of null is as good as it not being there, then something like this might be suitable. commonProp must be provided, and only one of x or y can be provided.

You might get a couple of similar error messages though.

{    $schema: 'http://json-schema.org/draft-07/schema#',    type: 'object',    required: ['commonProp'],    oneOf: [        {            properties: {                x: { type: 'number' },                commonProp: { type: 'number' },                y: {                    type: 'null',                    errorMessage: "should ONLY include either ('x') or ('y') keys. Not a mix.",                },            },            additionalProperties: { not: true, errorMessage: 'remove additional property ${0#}' },        },        {            properties: {                y: { type: 'number' },                commonProp: { type: 'number' },                x: {                    type: 'null',                    errorMessage: "should ONLY include either ('x') or ('y') keys. Not a mix.",                },            },            additionalProperties: { not: true, errorMessage: 'remove additional property ${0#}' },        },    ],}
const model = { x: 0, y: 0, commonProp: 0 };// ⛔️ ⛔️ ⛔️ ⛔️ ⛔️ ⛔️// Model>y should ONLY include either ('x') or ('y') keys. Not a mix.// Model>x should ONLY include either ('x') or ('y') keys. Not a mix.
const model = { x: 0, y: null, commonProp: 0 };//      
const model = { x: 0 };// ⛔️ ⛔️ ⛔️ ⛔️ ⛔️ ⛔️// Model must have required property 'commonProp'


This answer is not related to JSON schema, so it's a bit off the track, though it can bring another perspective on solving this problem, and json validation in general.

The point is to express declaratively exactly what you need as a result: a single field which is the only present. Consider the following json schema:

JsonElement json =    new Gson().toJsonTree(        Map.of(            "first_field", "vasya",            "second_field", false,            "third_field", 777,            "unrelated", "Rinse"        )    );

Let's say you need either one of the first_field, second_field, and third_field. The fourth field doesn't matter. Here is how the corresponding validation object looks like:

Result<SomeTestStructure> result =    new UnnamedBlocOfNameds<SomeTestStructure>(        List.of(            new OneOf(                "global",                new ErrorStub("Only one of the fields must be present"),                new AsString(                    new Required(                        new IndexedValue("first_field", json)                    )                ),                new AsBoolean(                    new Required(                        new IndexedValue("second_field", json)                    )                ),                new AsInteger(                    new Required(                        new IndexedValue("third_field", json)                    )                )            ),            new AsString(                new IndexedValue("unrelated", json)            )        ),        SomeTestStructure.class    )        .result();

First, you declare an unnamed block consisting of named ones; then you say that you need a single successful validatable element out of the three ones. And finally, you declare what success means. In this case, to be successful is to be simply present. If json is valid, an object of SomeTestStructure class is created:

assertTrue(result.isSuccessful());assertEquals(    new SomeTestStructure(777, "Rinse").thirdField(),    result.value().raw().thirdField());

For more info about this approach and a library implementing it, check out a quick start entry.