From 7fda548d441de44558f563adc899ba6d06823aa9 Mon Sep 17 00:00:00 2001 From: SimonTaurus Date: Mon, 21 Sep 2026 06:03:13 +0200 Subject: [PATCH] spec: a term mapped with @reverse is reference-valued OOLD-EXT-6d10 listed three signals; a reverse term matched only when the author also wrote "@type": "@id", which JSON-LD makes redundant. Both implementations therefore embedded the targets of a bare @reverse term, the defect #160 closed. Also states plain @reverse itself: the specification used it in three worked examples but only ever defined x-oold-reverse-properties, so the distinction between a read projection and the editor affordance sat in a parenthetical. --- docs/rules.md | 10 +++++----- docs/spec/index.html | 7 +++++-- meta/oold-rules.json | 20 ++++++++++---------- meta/rules-baseline.json | 2 +- spec/sections/09-extensions.md | 8 +++++++- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/docs/rules.md b/docs/rules.md index fb38cd4..c329fd8 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -946,7 +946,7 @@ A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD ??? quote "In context" - A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. + A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), a `@context` term mapped `"@type": "@id"`, or a `@context` term mapped with `@reverse`. The last stands on its own: the values of a reverse term are node references by definition (JSON-LD11 §4.1.10), so `"@type": "@id"` alongside it is redundant and an author who omits it has still written a reference. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. See [this rule in the specification](spec/#OOLD-EXT-05d3) (section: framing). @@ -1235,13 +1235,13 @@ See [this rule in the specification](spec/#OOLD-EXT-68fa) (section: framing). - Machine-checkable: no - Since: 1.0.0-rc.3 -A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, or a term mapped @type @id. +A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, a term mapped @type @id, or a term mapped @reverse. -A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. +A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), a `@context` term mapped `"@type": "@id"`, or a `@context` term mapped with `@reverse`. ??? quote "In context" - A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. + A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), a `@context` term mapped `"@type": "@id"`, or a `@context` term mapped with `@reverse`. The last stands on its own: the values of a reverse term are node references by definition (JSON-LD11 §4.1.10), so `"@type": "@id"` alongside it is redundant and an author who omits it has still written a reference. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. See [this rule in the specification](spec/#OOLD-EXT-6d10) (section: framing). @@ -1532,6 +1532,6 @@ Where a property matches both this and the embedded-object shape, the embedded-o ??? quote "In context" - A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. + A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), a `@context` term mapped `"@type": "@id"`, or a `@context` term mapped with `@reverse`. The last stands on its own: the values of a reverse term are node references by definition (JSON-LD11 §4.1.10), so `"@type": "@id"` alongside it is redundant and an author who omits it has still written a reference. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. See [this rule in the specification](spec/#OOLD-EXT-ff64) (section: framing). diff --git a/docs/spec/index.html b/docs/spec/index.html index f4c99aa..c359095 100644 --- a/docs/spec/index.html +++ b/docs/spec/index.html @@ -921,7 +921,7 @@

Terminology

Framing

JSON-LD 1.1 Framing reshapes a flat or arbitrarily-structured RDF graph into a specific tree layout described by a frame. An OO-LD schema already describes exactly such a tree - its properties give the nesting, its @context gives the term IRIs, a type constant (x-oold-instance-rdf-type, or a const on the type property) gives the node type, and x-oold-range gives the type of embedded or referenced objects - so an OO-LD-aware tool MAY auto-construct a frame from the schema. Framing an instance graph with that frame produces a JSON document shaped like the schema, which then validates against the same schema.

This makes an OO-LD schema bidirectional: its @context drives expansion (JSON to RDF), and the frame derived from its structure drives framing (RDF back to the schema's JSON tree). A tool that derives a frame MUST derive it mechanically: the schema's class type becomes the frame @type; an inlined object property becomes a nested subframe that embeds the referenced node; a reference-valued property (including one whose term is mapped with JSON-LD @reverse) becomes a subframe with @embed: @never so its targets stay IRIs, in line with the inline-versus-reference choice x-oold-range already records; and @explicit / @requireAll / @default follow from additionalProperties and required. The frame's @context is the composition of the referenced schemas' contexts.

-

A derivation MUST recognize a property as reference-valued from any of three signals: an x-oold-range on a string-typed value, an IRI-family format (the family recommends), or a @context term mapped "@type": "@id". Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally id for @id and type for @type (see ). Such a key names the node rather than pointing at another one, and a subframe under it produces {"@id": {...}}, which a processor rejects.

+

A derivation MUST recognize a property as reference-valued from any of four signals: an x-oold-range on a string-typed value, an IRI-family format (the family recommends), a @context term mapped "@type": "@id", or a @context term mapped with @reverse. The last stands on its own: the values of a reverse term are node references by definition ([[JSON-LD11]] §4.1.10), so "@type": "@id" alongside it is redundant and an author who omits it has still written a reference. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally id for @id and type for @type (see ). Such a key names the node rather than pointing at another one, and a subframe under it produces {"@id": {...}}, which a processor rejects.

Omitting @embed: @never is not a cosmetic difference. A reference whose target happens to carry triples in the same graph is then embedded as an object, and the framed document no longer validates against the schema the frame was derived from, which declares a string in that position.

Value-term aliases (@vocab)

The synonym machinery keys x-oold-context by term, so it reaches property and class terms but not IRIs that appear as instance values - for example the unit IRIs a quantity property points at, where two widely used unit vocabularies name the same unit differently: QUDT http://qudt.org/vocab/unit/SEC and the Ontology of units of Measure http://www.ontology-of-units-of-measure.org/resource/om-2/second. Coercing the property with "@type": "@vocab" (rather than "@type": "@id") closes the gap: a string value is then resolved against the active context's terms before the base IRI, so declaring the unit as a value term ("second": "http://qudt.org/vocab/unit/SEC") lets an instance write the readable "second" while the same x-oold-context synonyms and profile selection alias it ("x-oold-context": { "second": { "om:second": { … } } }); full IRIs remain valid values. Individual mappings round-trip through SSSOM like any other.

Because @vocab expands an unmatched string against the vocabulary - concatenating it onto the default vocabulary base when one is set (minting a new IRI), or leaving it a relative IRI when none is - a typo silently becomes a stray IRI rather than an error. A property coerced "@type": "@vocab" therefore SHOULD constrain its values with an enum of the value terms (optionally named with x-enum-varnames) or with x-oold-range, so only intended individuals are accepted. The value terms SHOULD also be kept from colliding with JSON-LD keyword aliases (id, type) or other context terms, since a value term shares the context's global term namespace - a term added for a value would otherwise also rewrite a property or keyword of the same name. Confining the value terms to the property's own scoped @context keeps them out of that shared namespace, since they then resolve only for that property's values; naming them with opaque identifiers such as UUIDs avoids the clash where readability is not required.

Why x-oold-ref and not $ref

x-oold-range is a custom keyword, so a $ref placed inside it is undefined behavior for generic JSON Schema tooling ([[JSONSCHEMA]] §9.4.2). In practice the behavior is not merely undefined but inconsistent: generic reference resolvers eagerly inline such a $ref, and because x-oold-range targets can form a cyclic graph of schemas this can pull in an unbounded graph, while schema-aware bundlers instead drop it.

-

x-oold-ref avoids this. Generic tools only follow the standard $ref keyword, so they leave x-oold-ref untouched. An OO-LD-aware tool SHOULD resolve x-oold-ref lazily, and MUST handle a cyclic reference graph - terminating and returning the references it has already resolved, rather than recursing indefinitely - since the graph it opts into may be unbounded or self-referential. The standard $ref continues to be used for ordinary schema composition (allOf, properties, $defs), which bundlers are expected to resolve. Because the only difference is the keyword name, the mapping is reversible: an OO-LD-aware tool can mechanically replace x-oold-ref with $ref to obtain a plain, fully-resolvable JSON Schema - the explicit opt-in to resolving the (possibly cyclic) graph.

Generation targets

x-oold-range (and the reverse properties below) exist so that the logical and conceptual modelling layers can be generated from the same OO-LD source instead of being maintained separately: a range constrains the type of a referenced object, which an OO-LD-aware tool emits as a SHACL property shape (sh:class / sh:node) and as an OWL property restriction. SHACL 1.2 (in progress, including Node Expressions for derived values) and OWL are the intended targets. These are generation targets, not additional validation performed by generic JSON Schema tools.

Reverse properties

Many relations are symmetric (e.g. Organization employs Person ⇔ Person works for Organization) and users want to edit them from both sides, without storing the information twice. The keywords x-oold-reverse-properties and x-oold-reverse-required declare such a [=reverse property=], mapped with JSON-LD @reverse in the @context. A reverse property shown by default in a generated user interface is marked with x-oold-ui-default-property on the property itself (see ). To make employees the reverse of works_for:

+

x-oold-ref avoids this. Generic tools only follow the standard $ref keyword, so they leave x-oold-ref untouched. An OO-LD-aware tool SHOULD resolve x-oold-ref lazily, and MUST handle a cyclic reference graph - terminating and returning the references it has already resolved, rather than recursing indefinitely - since the graph it opts into may be unbounded or self-referential. The standard $ref continues to be used for ordinary schema composition (allOf, properties, $defs), which bundlers are expected to resolve. Because the only difference is the keyword name, the mapping is reversible: an OO-LD-aware tool can mechanically replace x-oold-ref with $ref to obtain a plain, fully-resolvable JSON Schema - the explicit opt-in to resolving the (possibly cyclic) graph.

Generation targets

x-oold-range (and the reverse properties below) exist so that the logical and conceptual modelling layers can be generated from the same OO-LD source instead of being maintained separately: a range constrains the type of a referenced object, which an OO-LD-aware tool emits as a SHACL property shape (sh:class / sh:node) and as an OWL property restriction. SHACL 1.2 (in progress, including Node Expressions for derived values) and OWL are the intended targets. These are generation targets, not additional validation performed by generic JSON Schema tools.

Reverse properties

A relation is stored on one side and often wanted on the other. OO-LD answers that at two levels, and they are easy to confuse because both spell the relation with @reverse.

+

The plain [[JSON-LD11]] mechanism needs nothing from OO-LD. A schema may map an ordinary property with @reverse, and it then reads the relation from the referenced objects rather than from this one. Nothing is stored on the subject, so the property is a read projection: it appears on expansion and framing, and writing to it is not defined. The values of such a term are node references, never literals, which is why a frame derivation treats it as reference-valued whether or not "@type": "@id" is also written (see ).

+

x-oold-reverse-properties is the other level: an editor affordance that makes the relation writable from the side that does not store it. The rest of this section covers that keyword.

+

Many relations are symmetric (e.g. Organization employs Person ⇔ Person works for Organization) and users want to edit them from both sides, without storing the information twice. The keywords x-oold-reverse-properties and x-oold-reverse-required declare such a [=reverse property=], mapped with JSON-LD @reverse in the @context. A reverse property shown by default in a generated user interface is marked with x-oold-ui-default-property on the property itself (see ). To make employees the reverse of works_for:

  • define works_for in the properties of Person, mapped to a semantic property (schema:worksFor) in the @context of Person;
  • define employees in x-oold-reverse-properties of Organization, mapped with @reverse to the same property in the @context of Organization ([[JSON-LD11]] reverse properties).
  • diff --git a/meta/oold-rules.json b/meta/oold-rules.json index e07b940..77b275e 100644 --- a/meta/oold-rules.json +++ b/meta/oold-rules.json @@ -221,7 +221,7 @@ "summary": "A frame derivation must not emit a subframe for a property whose key aliases a JSON-LD keyword.", "text": "A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)).", "text_sha256": "07e05da8b2fcd62c4560330cca1372fbfe129432a17fa660040f417566c43dc9", - "context": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.", + "context": "A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), a `@context` term mapped `\"@type\": \"@id\"`, or a `@context` term mapped with `@reverse`. The last stands on its own: the values of a reverse term are node references by definition (JSON-LD11 §4.1.10), so `\"@type\": \"@id\"` alongside it is redundant and an author who omits it has still written a reference. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.", "machine_checkable": false, "since": "1.0.0-rc.3", "deprecated": false, @@ -255,7 +255,7 @@ "machine_checkable": true, "since": "1.0.0-rc.2", "deprecated": false, - "source": "09-extensions.md:437" + "source": "09-extensions.md:443" }, { "id": "OOLD-EXT-1f92", @@ -345,7 +345,7 @@ "machine_checkable": false, "since": "1.0.0-rc.1", "deprecated": false, - "source": "09-extensions.md:460" + "source": "09-extensions.md:466" }, { "id": "OOLD-EXT-44bd", @@ -435,7 +435,7 @@ "machine_checkable": false, "since": "1.0.0-rc.1", "deprecated": false, - "source": "09-extensions.md:459" + "source": "09-extensions.md:465" }, { "id": "OOLD-EXT-6312", @@ -473,10 +473,10 @@ "level": "MUST", "applies_to": "implementation", "section": "framing", - "summary": "A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, or a term mapped @type @id.", - "text": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`.", - "text_sha256": "f7aef6a0c109e10d28813bf45fe0808edbb5efdfab22edac7a171cd13a743f4d", - "context": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.", + "summary": "A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, a term mapped @type @id, or a term mapped @reverse.", + "text": "A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), a `@context` term mapped `\"@type\": \"@id\"`, or a `@context` term mapped with `@reverse`.", + "text_sha256": "4b9b79313f357dd298d30c2542183bc41e3011c4585fff20e42cc9a9fecd8936", + "context": "A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), a `@context` term mapped `\"@type\": \"@id\"`, or a `@context` term mapped with `@reverse`. The last stands on its own: the values of a reverse term are node references by definition (JSON-LD11 §4.1.10), so `\"@type\": \"@id\"` alongside it is redundant and an author who omits it has still written a reference. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.", "machine_checkable": false, "since": "1.0.0-rc.3", "deprecated": false, @@ -690,7 +690,7 @@ "machine_checkable": false, "since": "1.0.0-rc.3", "deprecated": false, - "source": "09-extensions.md:395" + "source": "09-extensions.md:401" }, { "id": "OOLD-EXT-ef09", @@ -731,7 +731,7 @@ "summary": "Where a property matches both the embedded-object and the reference-valued signals, the embedded-object reading wins.", "text": "Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares.", "text_sha256": "52ad7df4c37e1dc4253a1db04f928fc84099a704077d2b5c35d74807b72abf2c", - "context": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.", + "context": "A derivation MUST recognize a property as reference-valued from any of four signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), a `@context` term mapped `\"@type\": \"@id\"`, or a `@context` term mapped with `@reverse`. The last stands on its own: the values of a reverse term are node references by definition (JSON-LD11 §4.1.10), so `\"@type\": \"@id\"` alongside it is redundant and an author who omits it has still written a reference. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.", "machine_checkable": false, "since": "1.0.0-rc.3", "deprecated": false, diff --git a/meta/rules-baseline.json b/meta/rules-baseline.json index 3f4909d..c48f59c 100644 --- a/meta/rules-baseline.json +++ b/meta/rules-baseline.json @@ -30,7 +30,7 @@ "OOLD-EXT-61aa": "aa1601aa9ebbaf66046ee3c18acd7690ee8da151843196a42f3d4c1958ed42cd", "OOLD-EXT-6312": "24f4abaced11e4203298124cfbc2642efc2b6b0fc4c9a3369e0ff4c8d16ce1b8", "OOLD-EXT-68fa": "a71753a84fe8aa58779eb057aa8655918b06b17fea6e6e4ac0624bd780402012", - "OOLD-EXT-6d10": "f7aef6a0c109e10d28813bf45fe0808edbb5efdfab22edac7a171cd13a743f4d", + "OOLD-EXT-6d10": "4b9b79313f357dd298d30c2542183bc41e3011c4585fff20e42cc9a9fecd8936", "OOLD-EXT-6ea3": "a1fd73169ee2f84b0a897e74e5495e9019dd4a8ac1c763a108fc8cb02a0afe9e", "OOLD-EXT-7256": "1d8752aa41f9ee074a8b56e0e957c837218642db3fe32699d62cd0e5a669456b", "OOLD-EXT-725f": "ceb79118ae8684c9a82b93e11eb96ec7ebd9d2515dbc087ca1c5afecd1f6fb57", diff --git a/spec/sections/09-extensions.md b/spec/sections/09-extensions.md index db7f9dd..c353afc 100644 --- a/spec/sections/09-extensions.md +++ b/spec/sections/09-extensions.md @@ -95,7 +95,7 @@ Normalized - a single, consistent representation: This makes an OO-LD schema bidirectional: its `@context` drives expansion (JSON to RDF), and the frame derived from its structure drives framing (RDF back to the schema's JSON tree). :rule[OOLD-EXT-68fa]{applies=implementation level=MUST summary="A frame derived from a schema takes its @type from the class type, embeds inlined object properties as subframes and keeps reference-valued properties as IRIs with @embed @never."}A tool that derives a frame MUST derive it mechanically: the schema's class type becomes the frame `@type`; an inlined object property becomes a nested subframe that embeds the referenced node; a reference-valued property (including one whose term is mapped with JSON-LD `@reverse`) becomes a subframe with `@embed: @never` so its targets stay IRIs, in line with the inline-versus-reference choice `x-oold-range` already records; and `@explicit` / `@requireAll` / `@default` follow from `additionalProperties` and `required`. The frame's `@context` is the composition of the referenced schemas' contexts. -:rule[OOLD-EXT-6d10]{applies=implementation level=MUST summary="A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, or a term mapped @type @id."}A derivation MUST recognize a property as **reference-valued** from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. :rule[OOLD-EXT-ff64]{applies=implementation level=MUST summary="Where a property matches both the embedded-object and the reference-valued signals, the embedded-object reading wins."}Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. :rule[OOLD-EXT-05d3]{applies=implementation level="MUST NOT" summary="A frame derivation must not emit a subframe for a property whose key aliases a JSON-LD keyword."}A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. +:rule[OOLD-EXT-6d10]{applies=implementation level=MUST summary="A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, a term mapped @type @id, or a term mapped @reverse."}A derivation MUST recognize a property as **reference-valued** from any of four signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), a `@context` term mapped `"@type": "@id"`, or a `@context` term mapped with `@reverse`. The last stands on its own: the values of a reverse term are node references by definition ([[JSON-LD11]] §4.1.10), so `"@type": "@id"` alongside it is redundant and an author who omits it has still written a reference. :rule[OOLD-EXT-ff64]{applies=implementation level=MUST summary="Where a property matches both the embedded-object and the reference-valued signals, the embedded-object reading wins."}Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. :rule[OOLD-EXT-05d3]{applies=implementation level="MUST NOT" summary="A frame derivation must not emit a subframe for a property whose key aliases a JSON-LD keyword."}A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects. Omitting `@embed: @never` is not a cosmetic difference. A reference whose target happens to carry triples in the same graph is then embedded as an object, and the framed document no longer validates against the schema the frame was derived from, which declares a string in that position. @@ -377,6 +377,12 @@ Because `@vocab` expands an unmatched string against the vocabulary - concatenat #### Reverse properties {#reverse-properties} +A relation is stored on one side and often wanted on the other. OO-LD answers that at two levels, and they are easy to confuse because both spell the relation with `@reverse`. + +The plain [[JSON-LD11]] mechanism needs nothing from OO-LD. A schema may map an ordinary property with `@reverse`, and it then reads the relation from the referenced objects rather than from this one. Nothing is stored on the subject, so the property is a **read projection**: it appears on expansion and framing, and writing to it is not defined. The values of such a term are node references, never literals, which is why a frame derivation treats it as reference-valued whether or not `"@type": "@id"` is also written (see [](#framing)). + +`x-oold-reverse-properties` is the other level: an editor affordance that makes the relation writable from the side that does not store it. The rest of this section covers that keyword. + Many relations are symmetric (e.g. Organization employs Person ⇔ Person works for Organization) and users want to edit them from both sides, without storing the information twice. The keywords `x-oold-reverse-properties` and `x-oold-reverse-required` declare such a [=reverse property=], mapped with JSON-LD `@reverse` in the `@context`. A reverse property shown by default in a generated user interface is marked with `x-oold-ui-default-property` on the property itself (see [](#ui-generation)). To make `employees` the reverse of `works_for`: - define `works_for` in the `properties` of `Person`, mapped to a semantic property (`schema:worksFor`) in the `@context` of `Person`;