Skip to content

Ontology: Add custom_properties (e.g. for OWL import) - #146

Open
jochenchrist wants to merge 10 commits into
apache:mainfrom
jochenchrist:feat/ontology-custom-properties
Open

jochenchrist wants to merge 10 commits into
apache:mainfrom
jochenchrist:feat/ontology-custom-properties

Conversation

@jochenchrist

@jochenchrist jochenchrist commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Importing an ontology from an external format such as OWL/RDF currently loses any data that the core OSI spec does not model (labels, equivalence axioms, annotations, provenance, and so on) because every ontology object is locked down with additionalProperties: false. There is no place to put it, so a round-trip (OWL → OSI → OWL) is lossy.

This PR adds an open custom_properties object to the ontology root, concepts, relationships, and roles. Keys are free-form and values may be any JSON, so importers can preserve foreign data and round-trip it. The spec does not interpret or constrain the structure of custom_properties. The rest of each object keeps additionalProperties: false, so custom_properties is the single, explicit escape hatch rather than letting arbitrary keys leak in anywhere.

ontology:
  - concept: ContributionMargin
    type: ValueType
    extends: [Decimal]
    custom_properties:
      abbreviation: CM
      formula: revenue - variable_costs
      unit: EUR
      better_when: higher

An OWL importer would instead key properties by the source predicate (rdfs:label, owl:equivalentClass); see examples/foaf_owl_import.yaml. The choice of keys belongs to the tool that writes them, not the spec.

Design notes

The core spec (logical model) already has a custom_extensions: [{vendor_name, data}] mechanism where data is a stringified JSON blob. I deliberately did not reuse that here:

  • OWL/RDF is a standard interchange format, not a single "vendor", so a vendor_name blob is an awkward fit.
  • A structured map keeps imported annotations as real, queryable/validatable JSON instead of an opaque string.

If maintainers would prefer to unify on one extensibility mechanism spec-wide, I'm happy to switch this to the custom_extensions shape instead. Flagging the choice explicitly.

Changes

  • ontology/ontology.json: add a reusable CustomProperties $def and reference it from the root, Concept, Relationship, and Role.
  • ontology/ontology.md: add the field to the four schema tables, a new "Custom properties" section with a metrics example, and a version-history note.
  • examples/foaf_owl_import.yaml: a small FOAF/OWL import example exercising custom_properties at the root, concept, and relationship levels, with a prefixes map and QName keys.

Validation

  • ontology/ontology.json is well-formed JSON.
  • The new example and the existing examples/flights.yaml both validate against the updated schema (Draft 2020-12).
  • Confirmed unknown keys are still rejected everywhere except inside custom_properties.

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) <noreply@anthropic.com>
@jochenchrist jochenchrist changed the title Add custom_properties to ontology elements (e.g. for OWL import) Ontology: Add custom_properties (e.g. for OWL import) Jun 3, 2026
jochenchrist and others added 2 commits June 3, 2026 10:10
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) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@khush-bhatia
khush-bhatia requested a review from jbonofre June 3, 2026 17:43
kurtStirewalt
kurtStirewalt previously approved these changes Jun 7, 2026

@kurtStirewalt kurtStirewalt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice addition!

jbonofre
jbonofre previously approved these changes Jun 14, 2026

@jbonofre jbonofre left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall good, I just wonder if we should use custom_properties or custom_extensions.

@jochenchrist @kurtStirewalt thoughts?

Comment thread examples/foaf_owl_import.yaml Outdated
Resolve conflicts with the flattened concept declarations: custom_properties
now sits on the flattened OntologyComponent instead of the removed Concept
def, and the docs example uses the flattened concept syntax.
@jochenchrist

Copy link
Copy Markdown
Contributor Author

@jbonofre @kurtStirewalt
To push this PR, we would need a decision.

Option A

Structured custom_properties:

Example:

  - 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"

Consequences:

  • Different to core-spec v0.0.1 custom_extensions
  • I would introduce a PR to deprecate custom_extensions and introduce custom_properties in core spec as well.

Option B

Same with custom_extensions

  - concept: Person
    type: EntityType
    custom_extensions:
      - vendor_name: OWL
        data: |
          {
            "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"
          }

Consequences:

  • Following core-spec v0.0.1 version
  • This data string (JSON in String...) is hard to read, serialize, and validate. But maybe good-enough?

What do you think?

@scotthenninger

scotthenninger commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Option A would be my preference.

On language tags why not use the RDF convention of delimiting the string, e.g.
rdfs:comment “A person”@en

Comment thread ontology/ontology.json Outdated
Comment thread ontology/ontology.md Outdated
```yaml
custom_properties:
rdfs:label@en: "Person"
rdfs:label@fr: "Personne"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be very messy to parse, in my opinion. Why not:

rdfs:label:
  en: "Person"
  fr: "Personne"

This would not require mixing in the language tag into the property IRI, which is some new invented syntax. The syntax I wrote above is already used in LinkML-Scala.

Comment thread ontology/ontology.md Outdated
Comment thread ontology/ontology.md Outdated
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.
…ples

Concepts and relationships already have a first-class uri field, so the
custom_properties examples no longer duplicate it under an iri key.
… 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants