OWL / RDFS
The owl importer reads an OWL 2 ontology, or an RDFS vocabulary, in Turtle or N-Triples, into a LinkML schema. The OWL generator does the reverse, and the two should preserve most axioms of an OWL ontology.
OWL can express things LinkML cannot (property chains, defined classes, individuals). Those are lost on the way, and the importer lists them.
ohi get w3c/prov-o --format owl-turtleohi linkml from owl --schema-id https://example.org/prov \ --to prov.yaml prov-o.ttlfrom pathlib import Path
import ohi_linkml
result = ohi_linkml.from_owl( Path("prov-o.ttl").read_text(), schema_id="https://example.org/prov",)import { readFileSync } from "node:fs";import { LinkML } from "@neverblink/linkml";
const result = LinkML.fromOwl( readFileSync("prov-o.ttl", "utf8"), { schema_id: "https://example.org/prov" },);import eu.neverblink.linkml.generator.options.OwlImporterOptionsimport eu.neverblink.linkml.generator.owl.OwlImporter
import java.nio.file.{Files, Path}
val schema = OwlImporter().serializeFromString( Files.readString(Path.of("prov-o.ttl")), OwlImporterOptions(schemaId = Some("https://example.org/prov")),)#include <stddef.h>#include "ohi_linkml.h"
/* document is the text of prov-o.ttl. *//* The result is an ImportResult in JSON. */linkml_init_threads();char *error = NULL;char *result = linkml_from_owl( document, "{\"schema_id\": \"https://example.org/prov\"}", &error);linkml_free(result);linkml_free(error);The result has the schema, and in warnings, one line for each kind of thing that could not be imported. On the command line, add --list-not-imported to list them on stderr:
Not imported: Property chain, which LinkML cannot express (29): has_feature_kind: saref:hasFeatureKind o skos:broader; …The import stops with an error for an IRI that can’t be a URI in the schema: an IRI that isn’t a valid URI, such as <http://example.org/q[2]>, and an IRI whose text would be read back as a CURIE, such as <xsd:string>, as xsd is a prefix of the LinkML metamodel.
The same is true for an ontology IRI or --schema-id that isn’t a valid URI.
The one exception is such an IRI as a reference in an annotation, such as an rdfs:seeAlso value: it doesn’t stop the import, and is kept as an annotation instead of a see_also value.
Prefix names come from sh:declare (which the generator writes), the document’s @prefix lines, the config, a list of well-known ones (rdfs, skos, dcterms, prov, sosa, …), and the last path segment of other namespaces.
Importing RDFS vocabularies
Section titled “Importing RDFS vocabularies”The importer also reads vocabularies written in plain RDFS, such as DC terms or Schema.org:
rdfs:Classis imported as a LinkML class, andrdf:Propertyas a slot.- Schema.org’s
schema:domainIncludesandschema:rangeIncludesare read as well. - RDFS can’t express that a property has one value, so every slot is multivalued.
- A range of
rdfs:Literalorrdfs:Resourcesays nothing, so the slot gets no range. - A vocabulary without an
owl:Ontologygets the namespace of most of its terms as the schemaid. Set it with--schema-id.
Mapping OWL to LinkML
Section titled “Mapping OWL to LinkML”| OWL | LinkML |
|---|---|
owl:Ontology |
the schema, with the ontology IRI as id |
| class, or a class used without a declaration | class, named after the local part of its IRI, with class_uri when that differs from what LinkML would make. A class from another ontology becomes a class that stands for it, unless an imported schema has it (see below) |
| named superclasses | is_a for the first superclass from the same namespace as the subclass. mixin for other superclasses. |
| a superclass cycle | broken, since LinkML has none: a parent is left out and reported |
owl:allValuesFrom on a class |
the slot in the class’s slots, with slot_usage range when it narrows the property’s range |
owl:someValuesFrom |
required: true; when it narrows the range, also has_member: {range: …} (another one goes under all_of) |
| cardinalities | required, minimum_cardinality, maximum_cardinality, exact_cardinality |
owl:hasValue |
equals_string, with an IRI in full |
| a union of a class’s children as its superclass | abstract: true |
owl:equivalentClass to a named class |
exact_mappings |
owl:equivalentClass to an intersection |
the conjuncted classes become is_a and mixins, the restrictions become slots and slot_usage. |
owl:equivalentClass to a union of classes |
abstract: true, with the members as is_a children. |
owl:equivalentClass to another expression |
the expression read as superclasses (the “only if” half), and reported |
owl:disjointWith, owl:AllDisjointClasses |
disjoint_with, children_are_mutually_disjoint |
owl:disjointUnionOf |
abstract: true and children_are_mutually_disjoint: true, with the members as is_a children |
owl:hasKey |
unique_keys |
| object or data property | slot, snake_cased, with slot_uri |
| annotation property of the ontology’s own that it describes | slot with implements: [owl:AnnotationProperty] |
rdfs:range |
range, any_of for a union, Any (from linkml:extended_types) when there is none or when it mixes classes and datatypes |
rdfs:domain |
domain and the class’s slots; for a union, domain_of and each class’s slots |
owl:FunctionalProperty |
single-valued. Other slots are multivalued, as OWL allows any number of values |
owl:maxQualifiedCardinality 1 on xsd:string, for a property with range rdf:PlainLiteral or rdf:langString |
single-valued langstring slot |
rdfs:subPropertyOf |
is_a and mixins. A parent from another ontology becomes a slot that stands for it, of the same kind as the child |
owl:inverseOf, characteristics, owl:equivalentProperty |
inverse, transitive and friends, exact_mappings |
class defined by owl:oneOf |
enum. The text of a value is its skos:notation, else its local name |
| class with only individuals, used as a range | enum (turn off with enums_from_individuals: false). The class, or one of its parents, can be a range through schema:rangeIncludes too; its named equivalents become exact_mappings; and its parents become is_a and mixins, as for classes |
| XSD and RDF datatypes | LinkML’s types, plus a type for each other one that is declared or used (int, gYear, anyURI, …) |
| datatype restriction | minimum_value, maximum_value, minimum_length, maximum_length, pattern on the slot or the type (xsd:pattern abc becomes ^abc$). |
rdfs:subClassOf on a datatype |
typeof when the parent is another datatype. rdfs:Literal as the parent is dropped, since every datatype is a set of literals. The datatype stays a type, not a class |
a datatype defined by a list of literal values (owl:oneOf) |
a type with a pattern of the values, ^(Open|Closed)$. It stays a type, not an enum, so it is still a datatype after a round trip |
| annotations | the metaslots in the config’s metadata table, then LinkML annotations for the rest, with IRI values written as full IRIs. The default table means the usual vocabularies, whatever prefixes the ontology declares |
| language tags | the schema’s in_language when nearly all text written for people (titles, descriptions, comments and so on, not code examples or identifiers) is in one language. Text in another language, or in any language when the schema has none, keeps it, such as title: {en: Minute}. This holds for titles, descriptions, aliases, comments, notes and keywords. In a list (comments and so on), texts with a different language each are one entry; otherwise each text is an entry of its own. Annotations lose their tags |
Not imported: individuals that are not part of an enum, property values of individuals, property chains, restrictions on inverse properties, general class axioms, inverse functional properties, disjoint properties, owl:imports that the config does not map, what the ontology says about terms of an imported schema, and subproperties of annotation properties and of RDF, RDFS and OWL’s own. Restrictions that do not fit slot_usage are kept in the class’s notes, like OWL: inverse ssn:hasInput min 1.
Imports
Section titled “Imports”LinkML refers to classes and slots by name, so a term from another ontology needs an element in the schema. By default, the importer makes stubs for referenced-but-not-defined classes, such as Animal with class_uri: other:Animal.
When the imported ontology has a LinkML schema (e.g., it was converted to LinkML earlier), map the import to it in the config’s imports:
imports: http://www.w3.org/ns/sosa/: sosa # the ontology IRI -> the LinkML importThe schema then imports sosa, and the importer reads that schema to use its classes, slots, enums and types by name, matching them by IRI. A relative import is resolved against where the schema is written (the directory of --to, or the current one).
In the other direction, --only-root-schema writes an owl:imports for each imported schema, with the schema’s id as the ontology IRI. A schema imported from OWL has the ontology IRI as its id (or, for a vocabulary without an owl:Ontology such as DC terms, its namespace), so the two line up.
Config
Section titled “Config”--config takes the path to a YAML or JSON file. The JS and Python functions take the config itself, as an object or a dict, in their config option. The config’s format is defined as a LinkML schema in model/converter/importer/owl-config.yaml. Properties and classes can be written as CURIEs over the ontology’s prefixes, the config’s prefixes, or the well-known ones.
schema_id: https://example.org/my-schema # default: the ontology IRIname: my_schema # default: the ontology's rdfs:label if it is a usable name, else the default prefixdefault_prefix: d3f # default: the namespace most terms are inprefixes: d3f: http://d3fend.mitre.org/ontologies/d3fend.owl#naming: classes: keep # keep | snake | pascal | upper_snake slots: snake # default snake: hasValue becomes has_value permissible_values: keeprenames: # names for particular terms, by IRI or CURIE d3f:date: date_valuemetadata: # where each metaslot is read from, in order; replaces the default list for that metaslot description: [d3f:definition, rdfs:comment]annotations: true # keep other annotations as LinkML annotationsmultivalued: true # slots are multivalued unless functionalsome_values_from: required # required | range (also narrows the range, as SAREF-LinkML does)enums_from_individuals: truedatatypes: # LinkML types for datatype IRIs xsd:gYear: stringimports: # LinkML schemas to import in place of owl:imports (see Imports); others are reported http://www.w3.org/ns/sosa/: ./sosaThe default metadata table reads each metaslot from the property the LinkML metamodel gives it, then from the common alternatives:
| Metaslot | Read from, in order |
|---|---|
title |
rdfs:label, dcterms:title, dc:title, skos:prefLabel |
description |
skos:definition, obo:IAO_0000115, rdfs:comment, dcterms:description, dc:description |
aliases |
skos:altLabel, oboInOwl:hasExactSynonym, rdfs:label, skos:prefLabel |
comments |
skos:note, rdfs:comment, skos:scopeNote |
notes |
skos:editorialNote, skos:historyNote, skos:changeNote |
examples |
skos:example, obo:IAO_0000112 |
see_also |
rdfs:seeAlso |
contributors |
dcterms:contributor, dcterms:creator, dc:creator, dc:contributor |
created_on |
pav:createdOn, dcterms:created, dcterms:issued |
last_updated_on |
pav:lastUpdatedOn, dcterms:modified |
license |
dcterms:license, dc:rights, cc:license |
version |
pav:version, owl:versionInfo (and owl:versionIRI on the ontology) |
| others | the property from the metamodel, such as skos:exactMatch for exact_mappings |
A value goes to the first metaslot that lists its property and still has room. So a class with two rdfs:comments gets the first as its description and the second in comments. A label that is just the element’s name is dropped, and so is a comment that repeats the description.
Options
Section titled “Options”configOwlImportConfig--configHow to map the ontology: names, prefixes, which annotation properties fill which metaslots, and more. See https://linkml.ohi.dev/docs/importers/owl/.
formatJsonOutputFormatdefault yaml--formatThe serialization of the schema.
yamlYAML.jsonJSON.
full_urisbooleandefault false--full-urisScala fullUrisWhether to write URIs in full, instead of compacting them to CURIEs with the schema's prefixes.
input_formatRdfFormatdefault ttl--input-formatScala inputFormatThe RDF syntax of the ontology. Turtle also reads N-Triples.
ttlTurtle, with prefixes and pretty-printed.ntN-Triples, one statement per line.
schema_idstring--schema-idScala schemaIdThe id of the schema to produce, instead of the schema_id of config. The default is the ontology IRI.
schemasmap of FileThe LinkML schemas that the imports of config map owl:imports to, from file name to YAML text. By default, they are read from the file system.
OwlImportConfig
Settings for the OWL importer.
annotationsbooleanWhether to keep annotations that no metaslot uses, as LinkML annotations.
datatypesmap of DatatypeMappingLinkML types for datatype IRIs, on top of the built-in XSD and RDF ones.
default_prefixstringPrefix of the namespace most of the ontology's terms are in. Detected if not set.
enums_from_individualsbooleanWhether a class that is just a set of named individuals, and is the range of some property, becomes an enum. A class defined with owl:oneOf always does.
importsmap of ImportMappingLinkML schemas to import instead of owl:imports, keyed by ontology IRI. Other owl:imports are left out with a warning.
metadatamap of MetadataSourcesAnnotation properties to read each metaslot from, in order of preference. Replaces the default list only for the metaslots it lists.
multivaluedbooleanWhether slots are multivalued unless the property is functional. On by default, because OWL allows any number of values.
namestringThe schema name. Defaults to the ontology's rdfs:label if it is a valid name, else the default prefix.
namingNamingHow names are made from IRIs.
prefixesmap of NamespacePrefixExtra prefixes, on top of the ones the ontology declares.
renamesmap of TermRenameFixed names for specific terms, keyed by IRI or CURIE.
schema_idstringThe schema id. Defaults to the ontology IRI.
some_values_fromSomeValuesFromHow to read owl:someValuesFrom.
requiredSetsrequired: trueonpinC. IfFis narrower than the range ofp, also addshas_member: {range: F}, which matches the axiom exactly.rangeSetsrequired: trueandrange: F. This is stricter than the axiom, since every value must be anF, not just one.
DatatypeMapping
The LinkML type for a datatype IRI.
datatypestringrequiredtype_namestringrequiredImportMapping
The LinkML schema to import instead of an ontology.
ontologystringrequiredThe IRI of the imported ontology.
schemastringrequiredPath to the LinkML schema, relative to where the new schema is written.
MetadataSources
The annotation properties a metaslot is read from, in order of preference.
metaslotstringrequiredpropertieslist of stringrequiredNaming
How names are made from the local part of IRIs.
classesNameStyleStyle of class and enum names.
keepAs it is.snakehas_valuepascalHasValueupper_snakeHAS_VALUE
permissible_valuesNameStyleStyle of permissible value names made from individuals.
keepAs it is.snakehas_valuepascalHasValueupper_snakeHAS_VALUE
slotsNameStyleStyle of slot names.
keepAs it is.snakehas_valuepascalHasValueupper_snakeHAS_VALUE
NamespacePrefix
A prefix and the namespace it stands for.
namespacestringrequiredprefixstringrequiredTermRename
A fixed name for a term.
new_namestringrequiredtermstringrequiredThe term's IRI or CURIE.
File
A file and its content.
contentstringrequiredThe content of the file.
pathstringrequiredThe path of the file.