I have been using rfc/4/orientation.schema.json to validate axes metadata in
a reader, and noticed it never reports an error, whatever the document:
Draft202012Validator(schema).iter_errors(
{"axes": ["not an object", {"name": 42, "type": "nonsense"}]}
) # no errors
The schema has no entry point: gen-json-schema emits root properties only
for the class marked tree_root: true, and no class in orientation.yml
carries it. The root comes out as
{"type": "object", "additionalProperties": true, "$defs": {...}}, so the
eleven definitions are never referenced.
Marking Axes as the root is not quite enough
I tried that first, and it flips the schema to rejecting everything, including
{"axes": [{"name": "x", "type": "space", "unit": "micrometer"}]}, with
is not of type 'string'.
Axes.axes has no range, so default_range: string applies and is emitted
beside the union:
"items": {"anyOf": [{"$ref": "#/$defs/SpaceAxis"}, ...], "type": "string"}
JSON Schema keywords combine with AND, so an axis would have to be an object
and a string at once. SpaceAxis.orientation has the same pair.
Declaring a range clears it: range: AnatomicalOrientation for orientation,
and for the union the linkml:Any idiom, which emits
{"$ref": "#/$defs/Any", "anyOf": [...]} and no type keyword:
Any:
class_uri: linkml:Any
Axes:
tree_root: true
attributes:
axes:
multivalued: true
range: Any
any_of: [{range: SpaceAxis}, {range: TimeAxis}, {range: ChannelAxis}]
One gotcha I ran into: an abstract class cannot serve as the range, since
abstract classes are not emitted into $defs and the $ref is then
unresolvable.
Three constraints that would want relaxing at the same time
They are inert today, but they reject valid OME-Zarr once the root is bound:
unit is required on SpaceAxis and TimeAxis. In the 0.4 and 0.6 axes
schemas an axis requires only name, and unit is optional.
AxesNames allows only t, c, z, y, x and SpaceAxesNames only z, y, x,
while the core spec takes any non-empty string, and RFC 3 widens it further.
The file already anticipates this:
# AxesNames are bound to change after RFC3 is implemented.
ChannelAxis requires the name to be c.
Trying it out
With tree_root, the declared ranges, optional unit, free-string names and
the vocabulary restored (#585), sixteen test documents come out as
RFC 4 describes: partial orientation and "orientation": null accepted, a
non-anatomical type, a value outside the vocabulary and an orientation on a
non-space axis rejected.
Axis-name uniqueness stays with implementations either way: JSON Schema has no
way to compare one property across items. Mutual exclusion of antonym pairs, on
the other hand, is expressible, at one not / contains / minContains clause
per anatomical pair. I had it wrong when I opened this.
For context, the normative RFC 4 schema for 0.9.dev1 is being written by hand in
ome/ngff-spec#190, as schemas/axis_orientation.schema. This issue is about the
model here and the four artifacts generated from it, which implementations
bundle and read as normative.
Happy to open a PR with these changes if that would be useful.
I have been using
rfc/4/orientation.schema.jsonto validate axes metadata ina reader, and noticed it never reports an error, whatever the document:
The schema has no entry point:
gen-json-schemaemits rootpropertiesonlyfor the class marked
tree_root: true, and no class inorientation.ymlcarries it. The root comes out as
{"type": "object", "additionalProperties": true, "$defs": {...}}, so theeleven definitions are never referenced.
Marking
Axesas the root is not quite enoughI tried that first, and it flips the schema to rejecting everything, including
{"axes": [{"name": "x", "type": "space", "unit": "micrometer"}]}, withis not of type 'string'.Axes.axeshas norange, sodefault_range: stringapplies and is emittedbeside the union:
JSON Schema keywords combine with AND, so an axis would have to be an object
and a string at once.
SpaceAxis.orientationhas the same pair.Declaring a range clears it:
range: AnatomicalOrientationfororientation,and for the union the
linkml:Anyidiom, which emits{"$ref": "#/$defs/Any", "anyOf": [...]}and notypekeyword:One gotcha I ran into: an abstract class cannot serve as the range, since
abstract classes are not emitted into
$defsand the$refis thenunresolvable.
Three constraints that would want relaxing at the same time
They are inert today, but they reject valid OME-Zarr once the root is bound:
unitis required onSpaceAxisandTimeAxis. In the 0.4 and 0.6 axesschemas an axis requires only
name, andunitis optional.AxesNamesallows onlyt, c, z, y, xandSpaceAxesNamesonlyz, y, x,while the core spec takes any non-empty string, and RFC 3 widens it further.
The file already anticipates this:
# AxesNames are bound to change after RFC3 is implemented.ChannelAxisrequires the name to bec.Trying it out
With
tree_root, the declared ranges, optionalunit, free-string names andthe vocabulary restored (#585), sixteen test documents come out as
RFC 4 describes: partial orientation and
"orientation": nullaccepted, anon-anatomical
type, a value outside the vocabulary and an orientation on anon-space axis rejected.
Axis-name uniqueness stays with implementations either way: JSON Schema has no
way to compare one property across items. Mutual exclusion of antonym pairs, on
the other hand, is expressible, at one
not/contains/minContainsclauseper anatomical pair. I had it wrong when I opened this.
For context, the normative RFC 4 schema for 0.9.dev1 is being written by hand in
ome/ngff-spec#190, as
schemas/axis_orientation.schema. This issue is about themodel here and the four artifacts generated from it, which implementations
bundle and read as normative.
Happy to open a PR with these changes if that would be useful.