From 7b36b97230e1c714bf77b21ef133228267971f5e Mon Sep 17 00:00:00 2001 From: Jochen Christ Date: Wed, 3 Jun 2026 09:59:52 +0200 Subject: [PATCH 1/9] Add custom_properties for carrying data not covered by the spec Importing an ontology from an external format such as OWL/RDF loses any data that the core OSI spec does not model (IRIs, rdfs:label, owl:equivalentClass, annotations, provenance, ...), because every ontology object is locked down with additionalProperties: false. Add an open `custom_properties` object to the ontology root, concepts, relationships, and roles. Keys are free-form and values may be any JSON, so tools can preserve foreign data and round-trip it. CURIEs like `owl:equivalentClass` are recommended for semantic-web sources. The rest of each object keeps additionalProperties: false, so custom_properties is the single, explicit escape hatch. Includes a FOAF/OWL import example and documents the field in the spec. Co-Authored-By: Claude Opus 4.8 (1M context) --- examples/foaf_owl_import.yaml | 37 ++++++++++++++++++++++++++++++++++ ontology/ontology.json | 17 ++++++++++++++++ ontology/ontology.md | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 examples/foaf_owl_import.yaml diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml new file mode 100644 index 000000000..831469e31 --- /dev/null +++ b/examples/foaf_owl_import.yaml @@ -0,0 +1,37 @@ +version: 0.2.0.dev0 +name: foaf +description: > + Small ontology imported from the FOAF (Friend of a Friend) OWL vocabulary, + demonstrating how custom_properties preserves OWL/RDF data (IRIs, labels, and + axioms) that the core OSI spec does not model, so the source can be round-tripped. +custom_properties: + owl:Ontology: "http://xmlns.com/foaf/0.1/" + dc:title: "Friend of a Friend (FOAF) vocabulary" +ontology: + - concept: + name: Person + type: EntityType + custom_properties: + iri: "http://xmlns.com/foaf/0.1/Person" + rdfs:label: "Person" + rdfs:comment: "A person." + owl:equivalentClass: "https://schema.org/Person" + relationships: + - name: name + roles: + - concept: String + verbalizes: [ "{Person} has name {String}" ] + multiplicity: ManyToOne + custom_properties: + iri: "http://xmlns.com/foaf/0.1/name" + rdfs:label: "name" + - name: knows + roles: + - concept: Person + name: acquaintance + custom_properties: + rdfs:range: "http://xmlns.com/foaf/0.1/Person" + verbalizes: [ "{Person} knows {Person:acquaintance}" ] + custom_properties: + iri: "http://xmlns.com/foaf/0.1/knows" + owl:inverseOf: "http://xmlns.com/foaf/0.1/knows" diff --git a/ontology/ontology.json b/ontology/ontology.json index 473f69b33..c56c0f70b 100644 --- a/ontology/ontology.json +++ b/ontology/ontology.json @@ -35,11 +35,19 @@ "items": { "$ref": "#/$defs/OntologyMap" } + }, + "custom_properties": { + "$ref": "#/$defs/CustomProperties" } }, "required": ["version", "name", "ontology"], "additionalProperties": false, "$defs": { + "CustomProperties": { + "type": "object", + "description": "Open set of custom properties for carrying data that is not covered by the core ontology spec, for example when importing an ontology from an external format such as OWL/RDF. Keys are free-form; CURIEs such as 'owl:equivalentClass' or 'rdfs:label' are recommended when round-tripping semantic-web sources. Values may be any JSON.", + "additionalProperties": true + }, "OntologyComponent": { "type": "object", "description": "Ontology component that defines a single concept and any relationships that are keyed primarily by that concept", @@ -101,6 +109,9 @@ "type": "string" }, "description": "Natural language expressions that verbalize this relationship" + }, + "custom_properties": { + "$ref": "#/$defs/CustomProperties" } }, "required": ["name", "verbalizes"], @@ -148,6 +159,9 @@ "$ref": "#/$defs/Expression" }, "description": "Expressions that constrain the population of this concept" + }, + "custom_properties": { + "$ref": "#/$defs/CustomProperties" } }, "required": ["name", "type"], @@ -216,6 +230,9 @@ "name": { "type": "string", "description": "Optional name of this role, used when the same concept plays multiple roles in the same relationship" + }, + "custom_properties": { + "$ref": "#/$defs/CustomProperties" } }, "required": ["concept"], diff --git a/ontology/ontology.md b/ontology/ontology.md index 921e061db..ff663a3cd 100644 --- a/ontology/ontology.md +++ b/ontology/ontology.md @@ -69,6 +69,7 @@ hierarchically, grouping each relationship under the concept that plays its firs | `description` | string | No | Human-readable description | | `ai_context` | string/object | No | Additional context for AI tools | | `ontology` | list | Yes | Concepts and relationships they group that form this ontology | +| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | Each component of an ontology defines a concept and a list of relationships where that concept plays the first role: @@ -95,6 +96,7 @@ Concepts have the following schema: | `derived_by` | list | No | Expressions that derive this concept's population | | `identify_by` | list | No | Names of relationships that uniquely reference objects of this concept | | `requires` | list | No | Expressions that constrain this concept's population | +| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | Each concept is either an entity type or a value type. @@ -141,6 +143,7 @@ Each relationship that is declared under a concept conforms to the following sch | `derived_by` | list | No | Expressions that derive links of this relationship | | `requires` | list | No | Expressions that constrain this relationship's population | | `verbalizes` | list | Yes | Patterns describing how to verbalize links | +| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | Each relationship is uniquely identified by a prepending its declared name with that of the containing concept. For instance, in: @@ -185,6 +188,7 @@ using this schema: |-------|------|----------|-------------| | `concept` | string | Yes | Name of the concept that plays this role | | `name` | string | No | Optional role name | +| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | For instance, in: @@ -372,6 +376,38 @@ ontology: the first expression requires any value that plays the `Amount` role to be positive while the second requires any item that has sales in some store to be offered in that store. +### Custom properties + +The ontology root, concepts, relationships, and roles may each carry a `custom_properties` object: an +open set of key-value pairs for data that the core spec does not model. This gives tools a place to +preserve information that would otherwise be lost, which is especially useful when importing an ontology +from an external format such as OWL/RDF and round-tripping it back out. + +Keys are free-form, and values may be any JSON. When carrying data from a semantic-web source, CURIEs +such as `owl:equivalentClass` or `rdfs:label` are recommended so the origin of each property stays clear. +Tools that do not understand a given property should preserve it as-is. For example, an OWL importer might +map an `owl:Class` to a concept while retaining its IRI and annotations: + +```yaml +ontology: + - concept: + name: Person + type: EntityType + custom_properties: + iri: "http://xmlns.com/foaf/0.1/Person" + rdfs:label: "Person" + owl:equivalentClass: "https://schema.org/Person" + relationships: + - name: knows + roles: + - concept: Person + name: acquaintance + verbalizes: [ "{Person} knows {Person:acquaintance}" ] + custom_properties: + iri: "http://xmlns.com/foaf/0.1/knows" + owl:inverseOf: "http://xmlns.com/foaf/0.1/knows" +``` + ## Ontology mappings Ontology mappings declare how to map the values of fields at the logical level to objects and links @@ -585,6 +621,8 @@ though `Store` plays a role in three of the relationships. - **0.2.0.dev0** (2026-05-29): Basic support for ontologies and logical schema mappings - Core ontology structure: Concepts, relationships, and business rules (requires and derived_by) - Schema mappings from one or more logical models into an ontology + - `custom_properties` on the ontology root, concepts, relationships, and roles for carrying + data not covered by the core spec (e.g. when importing from OWL/RDF) --- From 9388fec10a054458d8ea19ad09dba312971c89a5 Mon Sep 17 00:00:00 2001 From: Jochen Christ Date: Wed, 3 Jun 2026 10:10:18 +0200 Subject: [PATCH 2/9] Document language-tagged literals in custom_properties RDF labels and comments are usually language-tagged and the previous example used a plain string, which is lossy. Recommend the readable `prefix:local@lang` key convention for language-tagged literals (the `@` cannot appear in a CURIE local name, so it parses unambiguously), and point at the JSON-LD value-object form for the multi-value and typed literal cases that the flat form cannot express. Co-Authored-By: Claude Opus 4.8 (1M context) --- examples/foaf_owl_import.yaml | 8 +++++--- ontology/ontology.md | 30 +++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index 831469e31..beb0ef9de 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -13,8 +13,9 @@ ontology: type: EntityType custom_properties: iri: "http://xmlns.com/foaf/0.1/Person" - rdfs:label: "Person" - rdfs:comment: "A person." + rdfs:label@en: "Person" + rdfs:label@fr: "Personne" + rdfs:comment@en: "A person." owl:equivalentClass: "https://schema.org/Person" relationships: - name: name @@ -24,7 +25,7 @@ ontology: multiplicity: ManyToOne custom_properties: iri: "http://xmlns.com/foaf/0.1/name" - rdfs:label: "name" + rdfs:label@en: "name" - name: knows roles: - concept: Person @@ -34,4 +35,5 @@ ontology: verbalizes: [ "{Person} knows {Person:acquaintance}" ] custom_properties: iri: "http://xmlns.com/foaf/0.1/knows" + rdfs:label@en: "knows" owl:inverseOf: "http://xmlns.com/foaf/0.1/knows" diff --git a/ontology/ontology.md b/ontology/ontology.md index ff663a3cd..0ab092d7f 100644 --- a/ontology/ontology.md +++ b/ontology/ontology.md @@ -395,7 +395,8 @@ ontology: type: EntityType custom_properties: iri: "http://xmlns.com/foaf/0.1/Person" - rdfs:label: "Person" + rdfs:label@en: "Person" + rdfs:label@fr: "Personne" owl:equivalentClass: "https://schema.org/Person" relationships: - name: knows @@ -408,6 +409,33 @@ ontology: owl:inverseOf: "http://xmlns.com/foaf/0.1/knows" ``` +#### Language-tagged literals + +RDF literals often carry a language tag (for example `rdfs:label` with values `"Person"@en` and +`"Personne"@fr`). The recommended convention is to append the [BCP 47](https://www.rfc-editor.org/info/bcp47) +language tag to the key after an `@`, giving keys of the form `prefix:local@lang`: + +```yaml +custom_properties: + rdfs:label@en: "Person" + rdfs:label@fr: "Personne" +``` + +`@` is not a legal character in a CURIE local name, so `prefix:local@lang` parses unambiguously into the +property and its language. This form holds at most one value per language per key. When a property needs +multiple values in the same language, or carries a non-language datatype such as `xsd:date`, use the +[JSON-LD](https://www.w3.org/TR/json-ld11/) value-object form instead, which is lossless: + +```yaml +custom_properties: + rdfs:label: + - { "@value": "Person", "@language": "en" } + - { "@value": "Persona", "@language": "en" } + dcterms:created: + "@value": "2020-01-01" + "@type": "xsd:date" +``` + ## Ontology mappings Ontology mappings declare how to map the values of fields at the logical level to objects and links From 118776ebafeb8c79bae41946a59d5304c4a6bb39 Mon Sep 17 00:00:00 2001 From: Jochen Christ Date: Wed, 3 Jun 2026 10:12:29 +0200 Subject: [PATCH 3/9] Move root custom_properties below the ontology key in example Co-Authored-By: Claude Opus 4.8 (1M context) --- examples/foaf_owl_import.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index beb0ef9de..0e822814e 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -4,9 +4,6 @@ description: > Small ontology imported from the FOAF (Friend of a Friend) OWL vocabulary, demonstrating how custom_properties preserves OWL/RDF data (IRIs, labels, and axioms) that the core OSI spec does not model, so the source can be round-tripped. -custom_properties: - owl:Ontology: "http://xmlns.com/foaf/0.1/" - dc:title: "Friend of a Friend (FOAF) vocabulary" ontology: - concept: name: Person @@ -37,3 +34,6 @@ ontology: iri: "http://xmlns.com/foaf/0.1/knows" rdfs:label@en: "knows" owl:inverseOf: "http://xmlns.com/foaf/0.1/knows" +custom_properties: + owl:Ontology: "http://xmlns.com/foaf/0.1/" + dc:title: "Friend of a Friend (FOAF) vocabulary" From ebe5e896a0ef0be3d6c7e6e5014c2dbc404ce129 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 18 Aug 2026 19:43:22 +0200 Subject: [PATCH 4/9] Flatten concept declaration in foaf example --- examples/foaf_owl_import.yaml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index 0e822814e..b060955df 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -5,15 +5,14 @@ description: > demonstrating how custom_properties preserves OWL/RDF data (IRIs, labels, and axioms) that the core OSI spec does not model, so the source can be round-tripped. ontology: - - concept: - name: Person - type: EntityType - custom_properties: - iri: "http://xmlns.com/foaf/0.1/Person" - rdfs:label@en: "Person" - rdfs:label@fr: "Personne" - rdfs:comment@en: "A person." - owl:equivalentClass: "https://schema.org/Person" + - concept: Person + type: EntityType + custom_properties: + iri: "http://xmlns.com/foaf/0.1/Person" + rdfs:label@en: "Person" + rdfs:label@fr: "Personne" + rdfs:comment@en: "A person." + owl:equivalentClass: "https://schema.org/Person" relationships: - name: name roles: From 7856958f1fd02f60e91753055d361e9e635b94f6 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 19 Sep 2026 09:22:58 +0200 Subject: [PATCH 5/9] ontology: drop language-tag and CURIE conventions from custom_properties custom_properties is an opaque, free-form object. Remove the recommended CURIE key naming and the rdfs:label@lang / JSON-LD value-object guidance from the schema description, docs, and example so the spec makes no assumptions about the structure of its contents. --- examples/foaf_owl_import.yaml | 19 ++++++++-------- ontology/ontology.json | 2 +- ontology/ontology.md | 41 +++++------------------------------ 3 files changed, 16 insertions(+), 46 deletions(-) diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index b060955df..d5bd7dfb4 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -9,10 +9,9 @@ ontology: type: EntityType custom_properties: iri: "http://xmlns.com/foaf/0.1/Person" - rdfs:label@en: "Person" - rdfs:label@fr: "Personne" - rdfs:comment@en: "A person." - owl:equivalentClass: "https://schema.org/Person" + label: "Person" + comment: "A person." + equivalent_class: "https://schema.org/Person" relationships: - name: name roles: @@ -21,18 +20,18 @@ ontology: multiplicity: ManyToOne custom_properties: iri: "http://xmlns.com/foaf/0.1/name" - rdfs:label@en: "name" + label: "name" - name: knows roles: - concept: Person name: acquaintance custom_properties: - rdfs:range: "http://xmlns.com/foaf/0.1/Person" + range: "http://xmlns.com/foaf/0.1/Person" verbalizes: [ "{Person} knows {Person:acquaintance}" ] custom_properties: iri: "http://xmlns.com/foaf/0.1/knows" - rdfs:label@en: "knows" - owl:inverseOf: "http://xmlns.com/foaf/0.1/knows" + label: "knows" + inverse_of: "http://xmlns.com/foaf/0.1/knows" custom_properties: - owl:Ontology: "http://xmlns.com/foaf/0.1/" - dc:title: "Friend of a Friend (FOAF) vocabulary" + ontology_iri: "http://xmlns.com/foaf/0.1/" + title: "Friend of a Friend (FOAF) vocabulary" diff --git a/ontology/ontology.json b/ontology/ontology.json index bd4e7b877..59d8f320e 100644 --- a/ontology/ontology.json +++ b/ontology/ontology.json @@ -60,7 +60,7 @@ "$defs": { "CustomProperties": { "type": "object", - "description": "Open set of custom properties for carrying data that is not covered by the core ontology spec, for example when importing an ontology from an external format such as OWL/RDF. Keys are free-form; CURIEs such as 'owl:equivalentClass' or 'rdfs:label' are recommended when round-tripping semantic-web sources. Values may be any JSON.", + "description": "Open set of custom properties for carrying data that is not covered by the core ontology spec, for example when importing an ontology from an external format such as OWL/RDF. Keys are free-form and values may be any JSON; the spec does not interpret their structure.", "additionalProperties": true }, "OntologyComponent": { diff --git a/ontology/ontology.md b/ontology/ontology.md index 68c8939fd..36c87b7e7 100644 --- a/ontology/ontology.md +++ b/ontology/ontology.md @@ -390,10 +390,9 @@ open set of key-value pairs for data that the core spec does not model. This giv preserve information that would otherwise be lost, which is especially useful when importing an ontology from an external format such as OWL/RDF and round-tripping it back out. -Keys are free-form, and values may be any JSON. When carrying data from a semantic-web source, CURIEs -such as `owl:equivalentClass` or `rdfs:label` are recommended so the origin of each property stays clear. -Tools that do not understand a given property should preserve it as-is. For example, an OWL importer might -map an `owl:Class` to a concept while retaining its IRI and annotations: +Keys are free-form, and values may be any JSON. The spec does not interpret or constrain the structure of +`custom_properties`; tools that do not understand a given property should preserve it as-is. For example, +an OWL importer might map an `owl:Class` to a concept while retaining its IRI and annotations: ```yaml ontology: @@ -401,9 +400,8 @@ ontology: type: EntityType custom_properties: iri: "http://xmlns.com/foaf/0.1/Person" - rdfs:label@en: "Person" - rdfs:label@fr: "Personne" - owl:equivalentClass: "https://schema.org/Person" + label: "Person" + equivalent_class: "https://schema.org/Person" relationships: - name: knows roles: @@ -412,34 +410,7 @@ ontology: verbalizes: [ "{Person} knows {Person:acquaintance}" ] custom_properties: iri: "http://xmlns.com/foaf/0.1/knows" - owl:inverseOf: "http://xmlns.com/foaf/0.1/knows" -``` - -#### Language-tagged literals - -RDF literals often carry a language tag (for example `rdfs:label` with values `"Person"@en` and -`"Personne"@fr`). The recommended convention is to append the [BCP 47](https://www.rfc-editor.org/info/bcp47) -language tag to the key after an `@`, giving keys of the form `prefix:local@lang`: - -```yaml -custom_properties: - rdfs:label@en: "Person" - rdfs:label@fr: "Personne" -``` - -`@` is not a legal character in a CURIE local name, so `prefix:local@lang` parses unambiguously into the -property and its language. This form holds at most one value per language per key. When a property needs -multiple values in the same language, or carries a non-language datatype such as `xsd:date`, use the -[JSON-LD](https://www.w3.org/TR/json-ld11/) value-object form instead, which is lossless: - -```yaml -custom_properties: - rdfs:label: - - { "@value": "Person", "@language": "en" } - - { "@value": "Persona", "@language": "en" } - dcterms:created: - "@value": "2020-01-01" - "@type": "xsd:date" + inverse_of: "http://xmlns.com/foaf/0.1/knows" ``` ## Ontology mappings From 412e557bd844d0e9623fd72542558a676bc28692 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 19 Sep 2026 09:27:42 +0200 Subject: [PATCH 6/9] ontology: use the uri field instead of an iri custom property in examples Concepts and relationships already have a first-class uri field, so the custom_properties examples no longer duplicate it under an iri key. --- examples/foaf_owl_import.yaml | 12 ++++++------ ontology/ontology.md | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index d5bd7dfb4..9fcc7e18e 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -2,26 +2,28 @@ version: 0.2.0.dev0 name: foaf description: > Small ontology imported from the FOAF (Friend of a Friend) OWL vocabulary, - demonstrating how custom_properties preserves OWL/RDF data (IRIs, labels, and - axioms) that the core OSI spec does not model, so the source can be round-tripped. + demonstrating how custom_properties preserves OWL/RDF data (labels, comments, + and axioms) that the core OSI spec does not model, so the source can be + round-tripped. ontology: - concept: Person type: EntityType + uri: "http://xmlns.com/foaf/0.1/Person" custom_properties: - iri: "http://xmlns.com/foaf/0.1/Person" label: "Person" comment: "A person." equivalent_class: "https://schema.org/Person" relationships: - name: name + uri: "http://xmlns.com/foaf/0.1/name" roles: - concept: String verbalizes: [ "{Person} has name {String}" ] multiplicity: ManyToOne custom_properties: - iri: "http://xmlns.com/foaf/0.1/name" label: "name" - name: knows + uri: "http://xmlns.com/foaf/0.1/knows" roles: - concept: Person name: acquaintance @@ -29,9 +31,7 @@ ontology: range: "http://xmlns.com/foaf/0.1/Person" verbalizes: [ "{Person} knows {Person:acquaintance}" ] custom_properties: - iri: "http://xmlns.com/foaf/0.1/knows" label: "knows" inverse_of: "http://xmlns.com/foaf/0.1/knows" custom_properties: - ontology_iri: "http://xmlns.com/foaf/0.1/" title: "Friend of a Friend (FOAF) vocabulary" diff --git a/ontology/ontology.md b/ontology/ontology.md index 36c87b7e7..39129839b 100644 --- a/ontology/ontology.md +++ b/ontology/ontology.md @@ -392,24 +392,24 @@ from an external format such as OWL/RDF and round-tripping it back out. Keys are free-form, and values may be any JSON. The spec does not interpret or constrain the structure of `custom_properties`; tools that do not understand a given property should preserve it as-is. For example, -an OWL importer might map an `owl:Class` to a concept while retaining its IRI and annotations: +an OWL importer might map an `owl:Class` to a concept while retaining its annotations and axioms: ```yaml ontology: - concept: Person type: EntityType + uri: "http://xmlns.com/foaf/0.1/Person" custom_properties: - iri: "http://xmlns.com/foaf/0.1/Person" label: "Person" equivalent_class: "https://schema.org/Person" relationships: - name: knows + uri: "http://xmlns.com/foaf/0.1/knows" roles: - concept: Person name: acquaintance verbalizes: [ "{Person} knows {Person:acquaintance}" ] custom_properties: - iri: "http://xmlns.com/foaf/0.1/knows" inverse_of: "http://xmlns.com/foaf/0.1/knows" ``` From 8ec0f51cc7d21c8d2f6673fc489bc07f3691cbc7 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 19 Sep 2026 09:34:51 +0200 Subject: [PATCH 7/9] ontology: metrics-style custom_properties example; QName keys in FOAF import The docs now show custom_properties on a ContributionMargin value type (abbreviation, formula, unit, better_when, owner) so the free-form nature of the keys is clear. The FOAF example declares a prefixes map and keys its properties by the source predicate's QName, noted as the importer's own convention rather than a spec requirement. --- examples/foaf_owl_import.yaml | 30 +++++++++++++++++------------- ontology/ontology.md | 33 ++++++++++++++++----------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index 9fcc7e18e..371cc28ab 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -4,34 +4,38 @@ description: > Small ontology imported from the FOAF (Friend of a Friend) OWL vocabulary, demonstrating how custom_properties preserves OWL/RDF data (labels, comments, and axioms) that the core OSI spec does not model, so the source can be - round-tripped. + round-tripped. This importer keys custom properties by the source predicate's + qualified name; that is the importer's convention, not a requirement of the spec. +prefixes: + foaf: "http://xmlns.com/foaf/0.1/" + rdfs: "http://www.w3.org/2000/01/rdf-schema#" + owl: "http://www.w3.org/2002/07/owl#" + dc: "http://purl.org/dc/elements/1.1/" ontology: - concept: Person type: EntityType - uri: "http://xmlns.com/foaf/0.1/Person" + uri: foaf:Person custom_properties: - label: "Person" - comment: "A person." - equivalent_class: "https://schema.org/Person" + rdfs:label: "Person" + rdfs:comment: "A person." + owl:equivalentClass: "https://schema.org/Person" relationships: - name: name - uri: "http://xmlns.com/foaf/0.1/name" + uri: foaf:name roles: - concept: String verbalizes: [ "{Person} has name {String}" ] multiplicity: ManyToOne custom_properties: - label: "name" + rdfs:label: "name" - name: knows - uri: "http://xmlns.com/foaf/0.1/knows" + uri: foaf:knows roles: - concept: Person name: acquaintance - custom_properties: - range: "http://xmlns.com/foaf/0.1/Person" verbalizes: [ "{Person} knows {Person:acquaintance}" ] custom_properties: - label: "knows" - inverse_of: "http://xmlns.com/foaf/0.1/knows" + rdfs:label: "knows" + owl:inverseOf: foaf:knows custom_properties: - title: "Friend of a Friend (FOAF) vocabulary" + dc:title: "Friend of a Friend (FOAF) vocabulary" diff --git a/ontology/ontology.md b/ontology/ontology.md index 39129839b..4013ed16e 100644 --- a/ontology/ontology.md +++ b/ontology/ontology.md @@ -387,32 +387,31 @@ requires any item that has sales in some store to be offered in that store. The ontology root, concepts, relationships, and roles may each carry a `custom_properties` object: an open set of key-value pairs for data that the core spec does not model. This gives tools a place to -preserve information that would otherwise be lost, which is especially useful when importing an ontology -from an external format such as OWL/RDF and round-tripping it back out. +attach their own metadata, or to preserve information when importing an ontology from an external +format so that it can be round-tripped. Keys are free-form, and values may be any JSON. The spec does not interpret or constrain the structure of `custom_properties`; tools that do not understand a given property should preserve it as-is. For example, -an OWL importer might map an `owl:Class` to a concept while retaining its annotations and axioms: +a metrics tool might annotate a value type with how it is computed and displayed: ```yaml ontology: - - concept: Person - type: EntityType - uri: "http://xmlns.com/foaf/0.1/Person" + - concept: ContributionMargin + type: ValueType + extends: [Decimal] + description: Revenue remaining after variable costs custom_properties: - label: "Person" - equivalent_class: "https://schema.org/Person" - relationships: - - name: knows - uri: "http://xmlns.com/foaf/0.1/knows" - roles: - - concept: Person - name: acquaintance - verbalizes: [ "{Person} knows {Person:acquaintance}" ] - custom_properties: - inverse_of: "http://xmlns.com/foaf/0.1/knows" + abbreviation: CM + formula: revenue - variable_costs + unit: EUR + better_when: higher + owner: finance-analytics ``` +An importer from a semantic-web format might instead use the source vocabulary's qualified names as keys, +as in the [FOAF example](../examples/foaf_owl_import.yaml). Both are valid; the choice of keys belongs to +the tool that writes them. + ## Ontology mappings Ontology mappings declare how to map the values of fields at the logical level to objects and links From 5bf47ec53c999c957fe2f6a93e100e3038a41c50 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 5 Oct 2026 09:22:05 +0200 Subject: [PATCH 8/9] Use the FOAF import as the custom_properties example - Replace the ContributionMargin example in ontology.md, whose `formula` reads like `derived_by`, with the FOAF Person concept - Add a precedence rule: custom_properties only holds data the spec cannot express - FOAF example: map rdfs:comment to description, drop the incorrect owl:inverseOf axiom on foaf:knows, add vs:term_status and rdfs:subPropertyOf from the FOAF vocabulary - Accept custom_properties in the Python ontology models (extra=forbid rejected it) - Validate the FOAF example in CI --- .github/workflows/validation-ci.yml | 3 ++ .../ontology/src/ossie_ontology/spec.py | 8 ++++ converters/ontology/tests/test_spec.py | 40 ++++++++++++++++++- examples/foaf_owl_import.yaml | 27 ++++++++----- ontology/ontology.md | 38 +++++++++++------- 5 files changed, 91 insertions(+), 25 deletions(-) diff --git a/.github/workflows/validation-ci.yml b/.github/workflows/validation-ci.yml index ffe6fb829..9e1c4fd81 100644 --- a/.github/workflows/validation-ci.yml +++ b/.github/workflows/validation-ci.yml @@ -89,3 +89,6 @@ jobs: uv run validation/validate.py examples/flights.ontology.yaml --schema ontology/ontology.json uv run validation/validate.py examples/flights.semantic_model.yaml uv run validation/validate.py examples/flights.mapping.yaml --schema ontology/mapping.json + + - name: Validate FOAF example + run: uv run validation/validate.py examples/foaf_owl_import.yaml --schema ontology/ontology.json diff --git a/converters/ontology/src/ossie_ontology/spec.py b/converters/ontology/src/ossie_ontology/spec.py index 3c4d34679..a696837af 100644 --- a/converters/ontology/src/ossie_ontology/spec.py +++ b/converters/ontology/src/ossie_ontology/spec.py @@ -40,6 +40,10 @@ class OssieObject(BaseModel): # like `instructions`, `synonyms`, `examples` (per core.md). AiContext = str | dict[str, Any] +# Open set of properties the spec does not model (e.g. OWL/RDF annotations kept +# for round-tripping). Keys are free-form and values may be any JSON. +CustomProperties = dict[str, Any] + class CustomExtension(OssieObject): """Vendor-specific metadata attached to any logical-model element (core.md). @@ -59,6 +63,7 @@ class Role(OssieObject): concept plays multiple roles in the same relationship.""" concept: str name: str | None = None + custom_properties: CustomProperties = Field(default_factory=dict) class Relationship(OssieObject): @@ -78,6 +83,7 @@ class Relationship(OssieObject): multiplicity: Literal["OneToOne", "ManyToOne"] | None = None derived_by: list[str] = Field(default_factory=list) requires: list[str] = Field(default_factory=list) + custom_properties: CustomProperties = Field(default_factory=dict) class ConceptComponent(OssieObject): @@ -106,6 +112,7 @@ class ConceptComponent(OssieObject): identify_by: list[str] = Field(default_factory=list) derived_by: list[str] = Field(default_factory=list) requires: list[str] = Field(default_factory=list) + custom_properties: CustomProperties = Field(default_factory=dict) relationships: list[Relationship] = Field(default_factory=list) @@ -248,6 +255,7 @@ class OssieSpec(OssieObject): name: str description: str | None = None prefixes: dict[str, str] = Field(default_factory=dict) + custom_properties: CustomProperties = Field(default_factory=dict) requires: list[str] = Field(default_factory=list) ai_context: AiContext | None = None ontology: list[ConceptComponent] = Field(default_factory=list) diff --git a/converters/ontology/tests/test_spec.py b/converters/ontology/tests/test_spec.py index 63938b7b2..64eedff90 100644 --- a/converters/ontology/tests/test_spec.py +++ b/converters/ontology/tests/test_spec.py @@ -24,7 +24,7 @@ import pytest import yaml -from ossie_ontology.spec import DatasetField, Metric, SemanticModel +from ossie_ontology.spec import DatasetField, Metric, OssieSpec, SemanticModel # tests/ -> ontology -> converters -> _EXAMPLES_DIR = Path(__file__).resolve().parents[3] / "examples" @@ -63,3 +63,41 @@ def test_tpcds_example_parses(): assert model.name == "tpcds_retail_model" assert any(f.datatype for d in model.datasets for f in d.fields) assert any(m.datatype for m in model.metrics) + + +def test_foaf_example_keeps_custom_properties(): + """`custom_properties` carries imported data the spec does not model, so it + must parse at every level that declares it and survive a dump unchanged.""" + example_path = _EXAMPLES_DIR / "foaf_owl_import.yaml" + if not example_path.is_file(): + pytest.skip(f"canonical example not present at {example_path}") + + doc = yaml.safe_load(example_path.read_text(encoding="utf-8")) + spec = OssieSpec.model_validate(doc) + + assert spec.custom_properties == {"dc:title": "Friend of a Friend (FOAF) vocabulary"} + person = spec.ontology[0] + assert person.custom_properties["owl:equivalentClass"] == "http://schema.org/Person" + assert person.relationships[0].custom_properties["rdfs:subPropertyOf"] == "rdfs:label" + + dumped = OssieSpec.load_yaml(spec.dump_yaml()).dump_dict() + assert dumped["custom_properties"] == doc["custom_properties"] + assert dumped["ontology"][0]["custom_properties"] == doc["ontology"][0]["custom_properties"] + assert dumped["ontology"][0]["relationships"][1]["custom_properties"] == ( + doc["ontology"][0]["relationships"][1]["custom_properties"] + ) + + +def test_role_accepts_custom_properties(): + relationship = OssieSpec.model_validate({ + "name": "test", + "ontology": [{ + "concept": "Person", + "type": "EntityType", + "relationships": [{ + "name": "knows", + "roles": [{"concept": "Person", "name": "acquaintance", "custom_properties": {"source": "foaf"}}], + }], + }], + }).ontology[0].relationships[0] + assert relationship.roles[0].custom_properties == {"source": "foaf"} diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index f71e91a44..6c4a3b1e9 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -18,26 +18,32 @@ version: 0.2.0.dev0 name: foaf description: > - Small ontology imported from the FOAF (Friend of a Friend) OWL vocabulary, - demonstrating how custom_properties preserves OWL/RDF data (labels, comments, - and axioms) that the core Ossie spec does not model, so the source can be - round-tripped. This importer keys custom properties by the source predicate's - qualified name; that is the importer's convention, not a requirement of the spec. + Small ontology imported from the FOAF (Friend of a Friend) OWL vocabulary. + The importer writes RDF data that has a home in the spec to that field + (rdfs:comment becomes description) and preserves the rest (labels, axioms, + term status) in custom_properties, so the source can be round-tripped. This + importer keys custom properties by the source predicate's qualified name; + that is the importer's convention, not a requirement of the spec. prefixes: foaf: "http://xmlns.com/foaf/0.1/" rdfs: "http://www.w3.org/2000/01/rdf-schema#" owl: "http://www.w3.org/2002/07/owl#" dc: "http://purl.org/dc/elements/1.1/" + vs: "http://www.w3.org/2003/06/sw-vocab-status/ns#" +custom_properties: + dc:title: "Friend of a Friend (FOAF) vocabulary" ontology: - concept: Person type: EntityType + description: "A person." iri: foaf:Person custom_properties: rdfs:label: "Person" - rdfs:comment: "A person." - owl:equivalentClass: "https://schema.org/Person" + owl:equivalentClass: "http://schema.org/Person" + vs:term_status: "stable" relationships: - name: name + description: "A name for some thing." iri: foaf:name roles: - concept: String @@ -45,7 +51,10 @@ ontology: multiplicity: ManyToOne custom_properties: rdfs:label: "name" + rdfs:subPropertyOf: "rdfs:label" + vs:term_status: "testing" - name: knows + description: "A person known by this person (indicating some level of reciprocated interaction between the parties)." iri: foaf:knows roles: - concept: Person @@ -53,6 +62,4 @@ ontology: verbalizes: [ "{Person} knows {Person:acquaintance}" ] custom_properties: rdfs:label: "knows" - owl:inverseOf: foaf:knows -custom_properties: - dc:title: "Friend of a Friend (FOAF) vocabulary" + vs:term_status: "stable" diff --git a/ontology/ontology.md b/ontology/ontology.md index 93b4f90b2..d77403de4 100644 --- a/ontology/ontology.md +++ b/ontology/ontology.md @@ -449,26 +449,36 @@ attach their own metadata, or to preserve information when importing an ontology format so that it can be round-tripped. Keys are free-form, and values may be any JSON. The spec does not interpret or constrain the structure of -`custom_properties`; tools that do not understand a given property should preserve it as-is. For example, -a metrics tool might annotate a value type with how it is computed and displayed: +`custom_properties`; tools that do not understand a given property should preserve it as-is. + +Use `custom_properties` only for data that the spec cannot express. Data that has a home in the spec, +such as a description, a supertype (`extends`), a derivation (`derived_by`), a constraint (`requires`), +or a verbalization (`verbalizes`), belongs in that field. + +For example, an importer from the OWL version of the [FOAF](http://xmlns.com/foaf/0.1/) vocabulary +writes `rdfs:comment` to `description` and keeps the RDF data that has no home in the spec, keyed by +the source predicate's qualified name: ```yaml +prefixes: + foaf: "http://xmlns.com/foaf/0.1/" + rdfs: "http://www.w3.org/2000/01/rdf-schema#" + owl: "http://www.w3.org/2002/07/owl#" + vs: "http://www.w3.org/2003/06/sw-vocab-status/ns#" ontology: - - concept: ContributionMargin - type: ValueType - extends: [Decimal] - description: Revenue remaining after variable costs + - concept: Person + type: EntityType + description: A person. + iri: foaf:Person custom_properties: - abbreviation: CM - formula: revenue - variable_costs - unit: EUR - better_when: higher - owner: finance-analytics + rdfs:label: Person + owl:equivalentClass: http://schema.org/Person + vs:term_status: stable ``` -An importer from a semantic-web format might instead use the source vocabulary's qualified names as keys, -as in the [FOAF example](../examples/foaf_owl_import.yaml). Both are valid; the choice of keys belongs to -the tool that writes them. +The full document is in [`examples/foaf_owl_import.yaml`](../examples/foaf_owl_import.yaml). Keying +properties by qualified name is this importer's convention. The choice of keys belongs to the tool that +writes them. ## Ontology mappings From 83abbd88cad8c93d90cacc7168b4f47c1d08ec1b Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 13:46:52 +0200 Subject: [PATCH 9/9] Rename custom_properties to annotations Avoids confusion with the core spec's custom_extensions. --- .../ontology/src/ossie_ontology/spec.py | 10 ++++---- converters/ontology/tests/test_spec.py | 24 +++++++++---------- examples/foaf_owl_import.yaml | 12 +++++----- ontology/ontology.json | 20 ++++++++-------- ontology/ontology.md | 22 ++++++++--------- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/converters/ontology/src/ossie_ontology/spec.py b/converters/ontology/src/ossie_ontology/spec.py index a696837af..3b73fbb55 100644 --- a/converters/ontology/src/ossie_ontology/spec.py +++ b/converters/ontology/src/ossie_ontology/spec.py @@ -42,7 +42,7 @@ class OssieObject(BaseModel): # Open set of properties the spec does not model (e.g. OWL/RDF annotations kept # for round-tripping). Keys are free-form and values may be any JSON. -CustomProperties = dict[str, Any] +Annotations = dict[str, Any] class CustomExtension(OssieObject): @@ -63,7 +63,7 @@ class Role(OssieObject): concept plays multiple roles in the same relationship.""" concept: str name: str | None = None - custom_properties: CustomProperties = Field(default_factory=dict) + annotations: Annotations = Field(default_factory=dict) class Relationship(OssieObject): @@ -83,7 +83,7 @@ class Relationship(OssieObject): multiplicity: Literal["OneToOne", "ManyToOne"] | None = None derived_by: list[str] = Field(default_factory=list) requires: list[str] = Field(default_factory=list) - custom_properties: CustomProperties = Field(default_factory=dict) + annotations: Annotations = Field(default_factory=dict) class ConceptComponent(OssieObject): @@ -112,7 +112,7 @@ class ConceptComponent(OssieObject): identify_by: list[str] = Field(default_factory=list) derived_by: list[str] = Field(default_factory=list) requires: list[str] = Field(default_factory=list) - custom_properties: CustomProperties = Field(default_factory=dict) + annotations: Annotations = Field(default_factory=dict) relationships: list[Relationship] = Field(default_factory=list) @@ -255,7 +255,7 @@ class OssieSpec(OssieObject): name: str description: str | None = None prefixes: dict[str, str] = Field(default_factory=dict) - custom_properties: CustomProperties = Field(default_factory=dict) + annotations: Annotations = Field(default_factory=dict) requires: list[str] = Field(default_factory=list) ai_context: AiContext | None = None ontology: list[ConceptComponent] = Field(default_factory=list) diff --git a/converters/ontology/tests/test_spec.py b/converters/ontology/tests/test_spec.py index 64eedff90..360868c87 100644 --- a/converters/ontology/tests/test_spec.py +++ b/converters/ontology/tests/test_spec.py @@ -65,8 +65,8 @@ def test_tpcds_example_parses(): assert any(m.datatype for m in model.metrics) -def test_foaf_example_keeps_custom_properties(): - """`custom_properties` carries imported data the spec does not model, so it +def test_foaf_example_keeps_annotations(): + """`annotations` carries imported data the spec does not model, so it must parse at every level that declares it and survive a dump unchanged.""" example_path = _EXAMPLES_DIR / "foaf_owl_import.yaml" if not example_path.is_file(): @@ -75,20 +75,20 @@ def test_foaf_example_keeps_custom_properties(): doc = yaml.safe_load(example_path.read_text(encoding="utf-8")) spec = OssieSpec.model_validate(doc) - assert spec.custom_properties == {"dc:title": "Friend of a Friend (FOAF) vocabulary"} + assert spec.annotations == {"dc:title": "Friend of a Friend (FOAF) vocabulary"} person = spec.ontology[0] - assert person.custom_properties["owl:equivalentClass"] == "http://schema.org/Person" - assert person.relationships[0].custom_properties["rdfs:subPropertyOf"] == "rdfs:label" + assert person.annotations["owl:equivalentClass"] == "http://schema.org/Person" + assert person.relationships[0].annotations["rdfs:subPropertyOf"] == "rdfs:label" dumped = OssieSpec.load_yaml(spec.dump_yaml()).dump_dict() - assert dumped["custom_properties"] == doc["custom_properties"] - assert dumped["ontology"][0]["custom_properties"] == doc["ontology"][0]["custom_properties"] - assert dumped["ontology"][0]["relationships"][1]["custom_properties"] == ( - doc["ontology"][0]["relationships"][1]["custom_properties"] + assert dumped["annotations"] == doc["annotations"] + assert dumped["ontology"][0]["annotations"] == doc["ontology"][0]["annotations"] + assert dumped["ontology"][0]["relationships"][1]["annotations"] == ( + doc["ontology"][0]["relationships"][1]["annotations"] ) -def test_role_accepts_custom_properties(): +def test_role_accepts_annotations(): relationship = OssieSpec.model_validate({ "name": "test", "ontology": [{ @@ -96,8 +96,8 @@ def test_role_accepts_custom_properties(): "type": "EntityType", "relationships": [{ "name": "knows", - "roles": [{"concept": "Person", "name": "acquaintance", "custom_properties": {"source": "foaf"}}], + "roles": [{"concept": "Person", "name": "acquaintance", "annotations": {"source": "foaf"}}], }], }], }).ontology[0].relationships[0] - assert relationship.roles[0].custom_properties == {"source": "foaf"} + assert relationship.roles[0].annotations == {"source": "foaf"} diff --git a/examples/foaf_owl_import.yaml b/examples/foaf_owl_import.yaml index 6c4a3b1e9..2c03c6a78 100644 --- a/examples/foaf_owl_import.yaml +++ b/examples/foaf_owl_import.yaml @@ -21,8 +21,8 @@ description: > Small ontology imported from the FOAF (Friend of a Friend) OWL vocabulary. The importer writes RDF data that has a home in the spec to that field (rdfs:comment becomes description) and preserves the rest (labels, axioms, - term status) in custom_properties, so the source can be round-tripped. This - importer keys custom properties by the source predicate's qualified name; + term status) in annotations, so the source can be round-tripped. This + importer keys annotations by the source predicate's qualified name; that is the importer's convention, not a requirement of the spec. prefixes: foaf: "http://xmlns.com/foaf/0.1/" @@ -30,14 +30,14 @@ prefixes: owl: "http://www.w3.org/2002/07/owl#" dc: "http://purl.org/dc/elements/1.1/" vs: "http://www.w3.org/2003/06/sw-vocab-status/ns#" -custom_properties: +annotations: dc:title: "Friend of a Friend (FOAF) vocabulary" ontology: - concept: Person type: EntityType description: "A person." iri: foaf:Person - custom_properties: + annotations: rdfs:label: "Person" owl:equivalentClass: "http://schema.org/Person" vs:term_status: "stable" @@ -49,7 +49,7 @@ ontology: - concept: String verbalizes: [ "{Person} has name {String}" ] multiplicity: ManyToOne - custom_properties: + annotations: rdfs:label: "name" rdfs:subPropertyOf: "rdfs:label" vs:term_status: "testing" @@ -60,6 +60,6 @@ ontology: - concept: Person name: acquaintance verbalizes: [ "{Person} knows {Person:acquaintance}" ] - custom_properties: + annotations: rdfs:label: "knows" vs:term_status: "stable" diff --git a/ontology/ontology.json b/ontology/ontology.json index b9c7821b7..6af518f67 100644 --- a/ontology/ontology.json +++ b/ontology/ontology.json @@ -44,8 +44,8 @@ "$ref": "#/$defs/OntologyMap" } }, - "custom_properties": { - "$ref": "#/$defs/CustomProperties" + "annotations": { + "$ref": "#/$defs/Annotations" }, "prefixes": { "type": "object", @@ -59,9 +59,9 @@ "required": ["version", "name", "ontology"], "additionalProperties": false, "$defs": { - "CustomProperties": { + "Annotations": { "type": "object", - "description": "Open set of custom properties for carrying data that is not covered by the core ontology spec, for example when importing an ontology from an external format such as OWL/RDF. Keys are free-form and values may be any JSON; the spec does not interpret their structure.", + "description": "Open set of annotations for carrying data that is not covered by the core ontology spec, for example when importing an ontology from an external format such as OWL/RDF. Keys are free-form and values may be any JSON; the spec does not interpret their structure.", "additionalProperties": true }, "OntologyComponent": { @@ -114,8 +114,8 @@ }, "description": "Defines relationships that pertain primarily to the concept defined in this component" }, - "custom_properties": { - "$ref": "#/$defs/CustomProperties" + "annotations": { + "$ref": "#/$defs/Annotations" }, "iri": { "type": "string", @@ -172,8 +172,8 @@ }, "description": "Natural language expressions that verbalize this relationship" }, - "custom_properties": { - "$ref": "#/$defs/CustomProperties" + "annotations": { + "$ref": "#/$defs/Annotations" }, "iri": { "type": "string", @@ -247,8 +247,8 @@ "type": "string", "description": "Optional name of this role, used when the same concept plays multiple roles in the same relationship" }, - "custom_properties": { - "$ref": "#/$defs/CustomProperties" + "annotations": { + "$ref": "#/$defs/Annotations" } }, "required": ["concept"], diff --git a/ontology/ontology.md b/ontology/ontology.md index d77403de4..f450b9560 100644 --- a/ontology/ontology.md +++ b/ontology/ontology.md @@ -90,7 +90,7 @@ hierarchically, grouping each relationship under the concept that plays its firs | `ontology` | list | Yes | Concepts and relationships they group that form this ontology | | `prefixes` | object | No | Namespace prefixes used to abbreviate [IRIs](#global-identifiers) | | `ontology_mappings` | list | No | Deprecated; accepted only so existing documents continue to validate. Write mappings as [mapping documents](#mapping-documents) instead | -| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | +| `annotations` | object | No | Open set of annotations for data not covered by this spec (see [Annotations](#annotations)) | Each component of an ontology declares a concept and lists the relationships where that concept plays the first role. The concept's name is the value of the `concept` field, and @@ -115,7 +115,7 @@ Concepts have the following schema: | `requires` | list | No | Expressions that constrain this concept's population | | `relationships` | list | No | Relationships where this concept plays the first role | | `iri` | string | No | Optional [global identifier](#global-identifiers) of this concept | -| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | +| `annotations` | object | No | Open set of annotations for data not covered by this spec (see [Annotations](#annotations)) | Each concept is either an entity type or a value type. @@ -161,7 +161,7 @@ Each relationship that is declared under a concept conforms to the following sch | `requires` | list | No | Expressions that constrain this relationship's population | | `verbalizes` | list | Yes | Patterns describing how to verbalize links | | `iri` | string | No | Optional [global identifier](#global-identifiers) of this relationship | -| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | +| `annotations` | object | No | Open set of annotations for data not covered by this spec (see [Annotations](#annotations)) | Each relationship is uniquely identified by prepending its declared name with that of the containing concept. For instance, in: @@ -205,7 +205,7 @@ using this schema: |-------|------|----------|-------------| | `concept` | string | Yes | Name of the concept that plays this role | | `name` | string | No | Optional role name | -| `custom_properties` | object | No | Open set of custom properties not covered by this spec (see [Custom properties](#custom-properties)) | +| `annotations` | object | No | Open set of annotations for data not covered by this spec (see [Annotations](#annotations)) | For instance, in: @@ -441,17 +441,17 @@ Expressions, roles, and mappings continue to use local names. The IRI serves too between this specification and IRI-based languages and lets multiple ontologies state that they refer to the same externally defined concept or relationship. -### Custom properties +### Annotations -The ontology root, concepts, relationships, and roles may each carry a `custom_properties` object: an +The ontology root, concepts, relationships, and roles may each carry an `annotations` object: an open set of key-value pairs for data that the core spec does not model. This gives tools a place to attach their own metadata, or to preserve information when importing an ontology from an external format so that it can be round-tripped. Keys are free-form, and values may be any JSON. The spec does not interpret or constrain the structure of -`custom_properties`; tools that do not understand a given property should preserve it as-is. +`annotations`; tools that do not understand a given annotation should preserve it as-is. -Use `custom_properties` only for data that the spec cannot express. Data that has a home in the spec, +Use `annotations` only for data that the spec cannot express. Data that has a home in the spec, such as a description, a supertype (`extends`), a derivation (`derived_by`), a constraint (`requires`), or a verbalization (`verbalizes`), belongs in that field. @@ -470,14 +470,14 @@ ontology: type: EntityType description: A person. iri: foaf:Person - custom_properties: + annotations: rdfs:label: Person owl:equivalentClass: http://schema.org/Person vs:term_status: stable ``` The full document is in [`examples/foaf_owl_import.yaml`](../examples/foaf_owl_import.yaml). Keying -properties by qualified name is this importer's convention. The choice of keys belongs to the tool that +annotations by qualified name is this importer's convention. The choice of keys belongs to the tool that writes them. ## Ontology mappings @@ -738,7 +738,7 @@ though `Store` plays a role in three of the relationships. - Optional IRIs on concepts and relationships, with namespace prefixes declared at the top level - Mapping documents (`ontology/mapping.json`) that reference one ontology and one semantic model; embedded `ontology_mappings` is deprecated - - `custom_properties` on the ontology root, concepts, relationships, and roles for carrying + - `annotations` on the ontology root, concepts, relationships, and roles for carrying data not covered by the core spec (e.g. when importing from OWL/RDF) ---