Migrating legacy OpenSemanticWorld (OSW/OSL) schemas to OO-LD¶
This guide maps the schema dialect used by OpenSemanticWorld page packages and
OpenSemanticLab (OSL) instances onto OO-LD. It is backed by a complete scan of every
world.opensemantic.* page package (see
legacy-keyword-inventory.md, generated by
scripts/extract-legacy-keywords.mjs),
which lists every legacy keyword, its usage patterns, and its future equivalent.
The big picture¶
A legacy schema lives in one of two places: the jsonschema slot of a Category-namespace
page (*.slot_jsonschema.json), or the main slot of a JsonSchema-namespace page
(.../JsonSchema/*.slot_main.json). Both use the same dialect and migrate identically.
Legacy OSW schemas mix four concerns in one document. OO-LD keeps them but gives each a clearly owned namespace:
| Concern | Legacy form | OO-LD target |
|---|---|---|
| Semantics (identity, types, relations) | uuid, range, subclassof_range, rdf_type, @context |
x-oold-* + @context |
| Portable UI intent | options.hidden, propertyOrder, enum_titles, infoText, widget format values |
x-oold-ui-* |
| Renderer-specific UI | json-editor options.*, watch, headerTemplate, flatpickr |
x-jedison-* (json-editor's successor) |
| Server-side logic | eval_template, dynamic_template, SMW query, data maps |
x-osl-* |
The split lets one schema carry OO-LD semantics and jedison UI hints together, each
clearly owned, while generic JSON Schema 2020-12 validators ignore every x- keyword.
Keyword mapping (summary)¶
The full, per-keyword table with real examples is in
legacy-keyword-inventory.md. The frequent cases:
| Legacy | OO-LD | Notes |
|---|---|---|
uuid |
x-oold-uuid |
schema identity |
range |
x-oold-range |
IRI, array of IRIs, or an OO-LD subschema |
subclassof_range |
x-oold-subclassof-range |
range constraint on subclass targets |
rdf_type |
x-oold-instance-rdf-type |
rdf:type(s) carried by instances |
title* / description* (schema level) |
x-oold-multilang-title / x-oold-multilang-description |
multilang language maps; NOT the @context multi-mapping notation |
id |
$anchor OR x-jedison-id (choose) |
see the note under Pitfalls before reusing $anchor |
enumSource |
$data (value source only) |
$data is a non-standard proposal; per-item title/value templating stays x-jedison-* |
readonly |
readOnly |
standard JSON Schema spelling (camelCase) |
propertyOrder |
x-oold-ui-property-order |
jedison x-categoryOrder |
options.category |
x-oold-ui-property-group |
jedison x-category / x-propGroup |
options.hidden |
x-oold-ui-form-hidden |
jedison x-hidden |
options.enum_titles (+ enum_titles*) |
x-oold-ui-enum-titles |
jedison x-enumTitles; * carries language maps |
options.infoText |
x-oold-ui-hint |
jedison x-info |
defaultProperties (root array) |
x-oold-ui-default-property (per-property boolean) |
moves to each subschema so composition can override it |
non-standard format values (table, tabs, autocomplete, ...) |
x-oold-ui-widget |
registered formats (date, uri, uuid, ...) stay in format |
x-enum-varnames |
x-enum-varnames (kept) |
established code-gen names; pair with x-enum-descriptions |
watch, headerTemplate, labelTemplate, links, flatpickr, json-editor options.* |
x-jedison-* |
renderer-specific, no portable meaning |
eval_template, dynamic_template, template, SMW query, source_path/target_path/field_maps |
x-osl-* |
OSL server-side mustache / SMW |
x-smw-quantity-property |
x-smw-* |
Semantic MediaWiki binding |
role, modes, conditional_visible/conditional_hide |
deferred | query-form constructs, no current OO-LD equivalent |
Worked example¶
A simplified excerpt of the real world.opensemantic.core Entity schema (uuid
ce353767-c628-45bd-9d88-d6eb3009aec0). Most properties are omitted for brevity, the
@context is trimmed to a few entries, and the label render template is abbreviated;
the keyword mapping table above is the complete reference. Full
keyword coverage (range, enum/enum_titles, x-enum-varnames, ...) lives in the
table, not in this one schema.
Before (legacy OSW)¶
{
"@context": {
"schema": "https://schema.org/",
"skos": "https://www.w3.org/TR/skos-reference/",
"Property": { "@id": "wiki:Property-3A", "@prefix": true },
"description": { "@id": "skos:definition" }
},
"type": "object",
"id": "entity",
"uuid": "ce353767-c628-45bd-9d88-d6eb3009aec0",
"title": "Entity",
"required": ["uuid", "label"],
"defaultProperties": ["description"],
"properties": {
"uuid": {
"title": "UUID",
"type": "string",
"format": "uuid",
"options": { "hidden": true }
},
"label": {
"type": "array",
"title": "Label(s)",
"title*": { "de": "Bezeichnung(en)" },
"format": "table",
"eval_template": [{ "type": "mustache-wikitext", "mode": "render", "value": "{{=<% %>=}} ... <%/label%>" }],
"items": { "$ref": "/wiki/JsonSchema:Label?action=raw" },
"minItems": 1
},
"description": {
"type": "array",
"title": "Description",
"title*": { "de": "Beschreibung" },
"format": "table",
"items": { "$ref": "/wiki/JsonSchema:Description?action=raw" }
},
"image": {
"title": "Image",
"title*": { "de": "Bild" },
"propertyOrder": 1020,
"type": "string",
"format": "url"
},
"query_label": {
"title": "Query label",
"type": "string",
"options": { "hidden": true, "conditional_visible": { "modes": ["query"] } }
}
}
}
After (OO-LD)¶
{
"$schema": "https://oo-ld.github.io/oold-schema/latest/meta/oold-meta-schema.json",
"@context": {
"schema": "https://schema.org/",
"skos": "https://www.w3.org/TR/skos-reference/",
"Property": { "@id": "wiki:Property-3A", "@prefix": true },
"description": { "@id": "skos:definition" }
},
"$id": "Entity.schema.json",
"x-jedison-id": "entity",
"x-oold-uuid": "ce353767-c628-45bd-9d88-d6eb3009aec0",
"title": "Entity",
"type": "object",
"required": ["uuid", "label"],
"properties": {
"uuid": {
"title": "UUID",
"type": "string",
"format": "uuid",
"x-oold-ui-form-hidden": true
},
"label": {
"type": "array",
"title": "Label(s)",
"x-oold-multilang-title": { "de": "Bezeichnung(en)" },
"x-oold-ui-widget": "table",
"x-osl-eval-template": [{ "type": "mustache-wikitext", "mode": "render", "value": "{{=<% %>=}} ... <%/label%>" }],
"items": { "$ref": "Label.schema.json" },
"minItems": 1
},
"description": {
"type": "array",
"title": "Description",
"x-oold-multilang-title": { "de": "Beschreibung" },
"x-oold-ui-widget": "table",
"x-oold-ui-default-property": true,
"items": { "$ref": "Description.schema.json" }
},
"image": {
"title": "Image",
"x-oold-multilang-title": { "de": "Bild" },
"x-oold-ui-property-order": 1020,
"type": "string",
"format": "uri"
},
"query_label": {
"title": "Query label",
"type": "string",
"x-oold-ui-form-hidden": true
}
}
}
What changed:
uuid->x-oold-uuid; the per-propertytitle*->x-oold-multilang-title.id(the json-editor watch id) -> vendorx-jedison-id, not$anchor(see Pitfalls).- Raw-slot
$refURLs (/wiki/JsonSchema:Label?action=raw) become resolvable schema URLs (Label.schema.json,$id-addressable). Ordinary composition still uses standard$ref. options: { "hidden": true }->x-oold-ui-form-hidden;propertyOrder->x-oold-ui-property-order.format: "table"is not a registered JSON Schema format, so it moves tox-oold-ui-widget;format: "url"becomes the registered formaturi(a registered format such asuuidstays put).eval_template(OSL server-side mustache) ->x-osl-eval-template.defaultProperties: ["description"]is a root array of the optional properties shown by default. Because composed schemas merge these arrays, it is extend-only: a derived schema can add a default property but cannot switch one off. Moving the flag to a per-property booleanx-oold-ui-default-property: true(here ondescription) makes it overridable - a derived schema resets it tofalseunder most-derived-wins resolution, without restating the whole list. The reverse-property formx-oold-reverse-default-properties(also a root array, same limitation) migrates the same way.options.conditional_visible/modesonquery_labelhave no OO-LD equivalent yet (they drove OSL query forms); record them and drop them, do not invent keywords (see Pitfalls).
Two ways to deliver UI annotations¶
UI keywords may live inline (as above) or in a separate overlay that leaves the schema untouched, following the OpenAPI Overlay 1.1.0 model. Use an overlay when the schema is generated or owned elsewhere, or when one schema needs different presentation in different contexts.
{
"overlay": "1.0.0",
"info": { "title": "Entity form overlay", "version": "1.0.0" },
"extends": "./Entity.schema.json",
"actions": [
{ "target": "$.properties.query_label", "update": { "x-oold-ui-form-hidden": false } },
{ "target": "$.properties.image", "update": { "x-oold-ui-widget": "url" } }
]
}
See the UI annotations section of the spec for the
normative x-oold-ui-* definitions and the overlay format.
Common pitfalls¶
- Typos in the legacy corpus. The inventory flags variants such as
titel*,desciption,defaultPropertis,_format,flatpicker,#range,$coment. Normalize them to the correct keyword before mapping; do not carry them forward. formatmisuse. Values likenumber,string,arrayand the empty string were used where a JSON Schematypebelongs; drop them.urlshould become the registered formaturi.title*is multilang, not multi-mapping. A<base>*sibling of a schema annotation (title*,description*) is a language map and becomesx-oold-multilang-*. The*/**notation inside@contextis a different thing (JSON-LD alternate@idmapping) and is handled by the existing@contextrules.enumSourceis not a drop-in for$data.$data(json-schema-spec#51) is a non-standard, draft-future proposal, realized today only by ajv's option and the data-2023 vocabulary. It is valid only in the whole-array form ("enum": {"$data": ...}, not"enum": [{"$data": ...}]), supplies just the value list, and limits static analysis. Treat it as an optional extension; the per-itemtitle/valuemustache templating has no portable equivalent and stays ax-jedison-*rendering concern.- Reusing
$anchorforidhas implications.$anchoris a real 2020-12 keyword: unique per schema resource, constrained to the anchor name pattern, and referenceable via$ref: "#name". Mapping json-editor's watchidonto it couples UI to validation and breaks on duplicate or non-conforming ids. Prefer vendorx-jedison-idunless you intend watch targets to be resolvable schema anchors. - Deferred keywords.
conditional_visible,conditional_hide,roleandmodeswere designed for OSL query forms and have no OO-LD equivalent yet; record them, do not invent keywords for them. - Server-side templates are not validation.
x-osl-*keywords are executed by OSL, not by JSON Schema validators; they are annotations only.
Checklist¶
- Run / read
legacy-keyword-inventory.mdfor the schema's keyword set and decisions. - Replace raw-slot
@context/$refURLs with resolvable schema URLs. - Rename semantic keywords to
x-oold-*. - Move portable UI keywords to
x-oold-ui-*(inline or overlay); move non-standardformatvalues tox-oold-ui-widget. - Namespace renderer-specific keywords as
x-jedison-*and server-side ones asx-osl-*. - Restructure
defaultPropertiesto per-propertyx-oold-ui-default-propertybooleans. - Drop typos,
formatmisuse, and JS callbacks; record deferred keywords. - Validate the result with
npm run validate. ```