Skip to content

An add-in keyword cannot be attached to a member whose type is a union #30

Description

@clemensv

An add-in contributes its keywords by $extends-ing a Core definition — #/definitions/Property, #/definitions/ObjectType, and so on — and implementations enforce that the annotated node is of the extended kind. A property whose type is a union is of no single kind, so no add-in keyword can attach to it.

Repro

{
  "$schema": "https://json-structure.org/meta/extended/v0/#",
  "$id": "https://example.com/uniontest",
  "$uses": ["JSONStructureUnits"],
  "name": "T",
  "type": "object",
  "properties": {
    "a": { "type": ["double", "null"], "unit": "m" },
    "b": { "type": "double", "unit": "m" }
  }
}
$ json-structure-check --extended uniontest.json
Schema is invalid:
 - #/properties/a/unit (8:48) [SCHEMA_CONSTRAINT_TYPE_MISMATCH] 'unit' can only appear in numeric schemas.

Property b is fine. Property a differs only in being nullable.

Why it matters

["T", "null"] is the idiomatic way to say "a T that may be absent-but-present" in a schema derived from a feed that emits null for an unreported channel, and unreported channels are the normal case in observational data. The choice an author is left with is:

  • keep the union and lose the annotation, so the number carries no unit; or
  • drop null from the union, leave the member out of required, and state in prose that an unreported channel is omitted rather than nulled — which changes the wire format the schema describes.

I took the second option across roughly thirty members of a sample corpus. It is a real change to the contract, made for a reason that has nothing to do with the data.

The same applies to any add-in keyword, not just unit: symbol from Units, and every annotation contributed by an extension whose add-in extends a scalar type definition.

Proposal

Define how add-in applicability is computed for a union type. The narrow rule that covers the motivating case:

An add-in whose $extends names a type definition T applies to a schema whose type is an array if every member of that array other than "null" is T.

That makes ["double", "null"] accept everything double accepts, and leaves genuinely heterogeneous unions such as ["double", "string"] rejected, which is the right answer — there is no single numeric interpretation to attach a unit to.

An alternative, if the union rule is felt to be too loose: give Core a way to write "nullable T" that preserves the scalar type identity, so that the annotation has an unambiguous subject. That is a larger change but it also removes the ambiguity from the many other places where ["T", "null"] is treated as a union rather than as an optionality marker.

Whichever is chosen, the current situation — where adding "null" to a type silently removes the author's ability to say what the number means — should not be the answer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions