Skip to content

RFC-4: making orientation.schema.json usable as a validator #586

Description

@vboussot

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions