oneOf — Exclusive Schema Matching
Use oneOf in JSON Schema for exclusive matching — the value must match exactly one sub-schema. Perfect for discriminated unions and payment method selection.
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"payment": {
"oneOf": [
{
"type": "object",
"properties": {
"method": { "const": "credit_card" },
"cardNumber": { "type": "string", "pattern": "^\\d{13,19}$" },
"expiry": { "type": "string", "pattern": "^(0[1-9]|1[0-2])/\\d{2}$" }
},
"required": ["method", "cardNumber", "expiry"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"method": { "const": "bank_transfer" },
"iban": { "type": "string", "pattern": "^[A-Z]{2}\\d{2}[A-Z0-9]{11,30}$" }
},
"required": ["method", "iban"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"method": { "const": "paypal" },
"email": { "type": "string", "format": "email" }
},
"required": ["method", "email"],
"additionalProperties": false
}
]
}
},
"required": ["payment"]
}Test Data
{
"payment": {
"method": "credit_card",
"cardNumber": "4111111111111111",
"expiry": "12/26"
}
}Detailed Explanation
oneOf — Exactly One Schema Must Match
The oneOf keyword validates that a value matches exactly one of the provided sub-schemas. If the value matches zero schemas or more than one schema, validation fails. This creates a mutually exclusive choice — a discriminated union.
Basic Syntax
{
"oneOf": [
{ "type": "string" },
{ "type": "integer" }
]
}
This accepts strings or integers, but not values that could be both. Since strings and integers are inherently different types, this behaves like anyOf in practice. The power of oneOf shows when sub-schemas can overlap.
How the Example Schema Works
The schema models a payment method using a discriminated union pattern:
- Credit card — requires
method: "credit_card", acardNumber(13-19 digits), and anexpirydate (MM/YY format). - Bank transfer — requires
method: "bank_transfer"and aniban(International Bank Account Number). - PayPal — requires
method: "paypal"and anemailaddress.
Each sub-schema uses const on the method field as a discriminator — a fixed value that identifies which variant applies. The additionalProperties: false in each sub-schema ensures no cross-contamination between variants.
The test data provides credit card details with method: "credit_card", which matches only the first sub-schema. The other two schemas fail (wrong method value), so exactly one match — validation passes.
The Discriminator Pattern
The most robust way to use oneOf is with a discriminator field:
{
"oneOf": [
{ "properties": { "type": { "const": "A" } }, ... },
{ "properties": { "type": { "const": "B" } }, ... }
]
}
The const on the discriminator ensures exactly one sub-schema can match for any valid input. Without a discriminator, you risk overlapping schemas where a value accidentally matches multiple alternatives.
Failure Modes
oneOf fails in two distinct ways:
- Zero matches: The value does not satisfy any sub-schema. Error: "value did not match any of the alternatives."
- Multiple matches: The value satisfies two or more sub-schemas. Error: "value matched more than one alternative."
The multiple-match failure is unique to oneOf and does not occur with anyOf. It catches schema design errors where your alternatives are not truly mutually exclusive.
Performance Note
Validators must evaluate all sub-schemas (they cannot short-circuit after finding one match) because they need to confirm that exactly one matches. For schemas with many alternatives, consider using if/then/else chains for better performance.
Use Case
Use oneOf for payment method selection in e-commerce APIs, message type routing in event-driven architectures, polymorphic resource creation (different types of notifications, different types of addresses), and any API where the client must choose exactly one variant from a set of options.