I am using Cerberus to validate some YAML files that might look like this:
fleet.yml
fleet_city: "New York"
vehicles:
vehicle_1:
car:
# License plate required for car
license_plate: "ABC123"
cargo: 10
vehicle_2:
# All vehicle types can have optional remarks block
remarks:
active: true
car:
license_plate: "XYZ789"
# Cargo is optional
# Vehicle names (dict keys) are arbitrary
cool_ride:
remarks:
active: false
boat:
# Required
seaworthy: true
# not required
sails: 3
The general structure is that there are some file-wide values like fleet_city
, and then a block called vehicles
. The latter is a dictionary with vehicle names as keys. Every vehicle type has its own schema, although they're all dictionaries. I came up with this schema to formalize this:
schema.yml
fleet_city:
type: string
vehicles:
type: dict
keysrules:
# Vehicle name constraints
type: string
regex: '[A-Za-z\d_]+'
valuesrules:
type: dict
schema:
# Optional remarks block
remarks:
type: dict
# Car schema
car:
schema:
license_plate:
type: string
required: true
cargo:
type: integer
required: false
# Boat schema
boat:
schema:
seaworthy:
type: boolean
required: true
propulsion:
type: dict
schema:
sails:
type: integer
required: false
Which I can test with:
import yaml
from cerberus import Validator
with open('schema.yml') as f:
schema = yaml.safe_load(f)
v = Validator(schema)
with open('fleet.yml') as f:
fleet = yaml.safe_load(f)
print(v.validate(fleet))
print(v.errors)
Which correctly returns True
. However, in the input every vehicle is supposed to have one type (either one car
block or one boat
block, not both). This rule is not reflected in the schema above. I tried to add it like so:
schema2.yml
fleet_city:
type: string
vehicles:
type: dict
keysrules:
# Vehicle name constraints
type: string
regex: '[A-Za-z\d_]+'
valuesrules:
type: dict
schema:
# Optional remarks block
remarks:
type: dict
oneof_schema:
# Car schema
- car:
schema:
license_plate:
type: string
required: true
cargo:
type: integer
required: false
# Boat schema
- boat:
schema:
seaworthy:
type: boolean
required: true
propulsion:
type: dict
schema:
sails:
type: integer
required: false
From what I can tell, I have followed the syntax given in the docs but schema2.yml
causes:
cerberus.schema.SchemaError: {'vehicles': [{'valuesrules': [{'schema': ['no definitions validate', {'anyof definition 0': [{'oneof': ['must be of dict type']}], 'anyof definition 1': [{'oneof': [{'schema': ['no definitions validate', 'no definitions validate', {'anyof definition 0': [{'car': ['null value not allowed'], 'schema': [{'cargo': ['unknown rule'], 'license_plate': ['unknown rule'], 'propulsion': ['unknown rule'], 'seaworthy': ['unknown rule']}], 'boat': ['null value not allowed']}], 'anyof definition 1': [{'car': ['unknown rule'], 'boat': ['unknown rule']}]}]}], 'remarks': ['unknown rule']}]}]}]}]}
Which doesn't really help.
One workaround is to use excludes
for car
in boat
and vice versa, but that approach will fall apart if we have not just 2 vehicle types but many.
What is the issue in my schema2.yml
and how can I fix it?