Part 4: Working with RDF#
Previously we saw how to do basic validation of data using JSON-Schema.
This section demonstrates how to work with LinkML in conjunction with Linked Data/ RDF. If this is not of interest you can skip to the next section. However, even if this is the case you may wish to revisit this section. LinkML is intended to make it easy to get the benefits of Linked Data, while staying simple and working within a stack many developers are familiar with.
And even if you aren’t using RDF yourself, declaring URIs for your schema elements can help make your data FAIR, and in particular can serve as hooks to interoperate data!
Example schema#
Let’s start with the schema we developed in the previous section, with some minor modifications:
personinfo.yaml:
id: https://w3id.org/linkml/examples/personinfo
name: personinfo
prefixes:
linkml: https://w3id.org/linkml/
personinfo: https://w3id.org/linkml/examples/personinfo/
ORCID: https://orcid.org/
default_curi_maps:
- semweb_context
imports:
- linkml:types
default_prefix: personinfo
default_range: string
classes:
Person:
attributes:
id:
identifier: true
full_name:
required: true
description:
name of the person
aliases:
multivalued: true
description:
other names for the person
phone:
pattern: "^[\\d\\(\\)\\-]+$"
age:
range: integer
minimum_value: 0
maximum_value: 200
Container:
attributes:
persons:
multivalued: true
inlined_as_list: true
range: Person
We extended the previous schema in a few ways:
we included a prefix declaration for the ORCID IDs in our data records
we included an import of standard semantic web prefixes from
semweb_context
We will use this schema with a collection of data records
data.yaml:
persons:
- id: ORCID:1234
full_name: Clark Kent
age: 33
phone: 555-555-5555
- id: ORCID:4567
full_name: Lois Lane
age: 34
We can use the linkml conversion library to translate this to RDF (Turtle syntax default)
linkml-convert -s personinfo.yaml -t rdf data.yaml
Outputs:
@prefix ORCID: <https://orcid.org/> .
@prefix personinfo: <https://w3id.org/linkml/examples/personinfo/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ORCID:1234 a personinfo:Person ;
personinfo:age 33 ;
personinfo:full_name "Clark Kent" ;
personinfo:phone "555-555-5555" .
ORCID:4567 a personinfo:Person ;
personinfo:age 34 ;
personinfo:full_name "Lois Lane" .
[] a personinfo:Container ;
personinfo:persons ORCID:1234,
ORCID:4567 .
Note that each person is now represented by an ORCID URI. This is a start, but note we are still using classes and properties in our own namespace - there are existing vocabularies such as schema.org we could be reusing here.
Adding URIs to our schema#
Let’s enhance our schema, using two schema slots:
class_uri: to declare a URI/IRI for a class
slot_uri: the same thing for a slot
In both cases, we provide the value as a CURIE, and include a prefixes map that maps CURIEs to URIs.
personinfo-semantic.yaml:
id: https://w3id.org/linkml/examples/personinfo
name: personinfo
prefixes:
linkml: https://w3id.org/linkml/
schema: http://schema.org/ ## define a prefix for schema.org
personinfo: https://w3id.org/linkml/examples/personinfo/
ORCID: https://orcid.org/
imports:
- linkml:types
default_curi_maps:
- semweb_context
default_prefix: personinfo
default_range: string
classes:
Person:
class_uri: schema:Person ## reuse schema.org vocabulary
attributes:
id:
identifier: true
full_name:
required: true
description:
name of the person
slot_uri: schema:name ## reuse schema.org vocabulary
aliases:
multivalued: true
description:
other names for the person
phone:
pattern: "^[\\d\\(\\)\\-]+$"
slot_uri: schema:telephone ## reuse schema.org vocabulary
age:
range: integer
minimum_value: 0
maximum_value: 200
id_prefixes:
- ORCID
Container:
attributes:
persons:
multivalued: true
inlined_as_list: true
range: Person
Now let’s try converting the same YAML/JSON using the enhanced schema
linkml-convert -s personinfo-semantic.yaml -t rdf data.yaml
Outputs:
@prefix ORCID: <https://orcid.org/> .
@prefix personinfo: <https://w3id.org/linkml/examples/personinfo/> .
@prefix schema1: <http://schema.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ORCID:1234 a schema1:Person ;
schema1:name "Clark Kent" ;
schema1:telephone "555-555-5555" ;
personinfo:age 33 .
ORCID:4567 a schema1:Person ;
schema1:name "Lois Lane" ;
personinfo:age 34 .
[] a personinfo:Container ;
personinfo:persons ORCID:1234,
ORCID:4567 .
Note that the prefixes are hidden but the effect is to reuse URIs such as schema:telephone
This can be visualized using rdf-grapher as:
JSON-LD contexts#
You can also generate JSON-LD context files that can be used to add semantics to JSON documents:
gen-jsonld-context --no-metadata personinfo-semantic.yaml
Outputs:
{
"@context": {
"xsd": "http://www.w3.org/2001/XMLSchema#",
"ORCID": "https://orcid.org/",
"linkml": "https://w3id.org/linkml/",
"personinfo": "https://w3id.org/linkml/examples/personinfo/",
"schema": "http://schema.org/",
"@vocab": "https://w3id.org/linkml/examples/personinfo/",
"persons": {
"@type": "schema:Person",
"@id": "persons"
},
"age": {
"@type": "xsd:integer",
"@id": "age"
},
"aliases": {
"@id": "aliases"
},
"full_name": {
"@id": "schema:name"
},
"id": "@id",
"phone": {
"@id": "schema:telephone"
},
"Container": {
"@id": "Container"
},
"Person": {
"@id": "schema:Person"
}
}
}
NOTE: currently you need to declare your own type object and map this to rdf:type
for typing information to be shown
Using Shape Languages#
In the previous section we saw how to use JSON-Schema validators. LinkML also allows the use of ShEx (future versions will allow SPARQL)
gen-shex --no-metadata personinfo-semantic.yaml
Outputs:
# metamodel_version: 1.7.0
BASE <https://w3id.org/linkml/examples/personinfo/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX linkml: <https://w3id.org/linkml/>
PREFIX schema1: <http://schema.org/>
linkml:String xsd:string
linkml:Integer xsd:integer
linkml:Boolean xsd:boolean
linkml:Float xsd:float
linkml:Double xsd:double
linkml:Decimal xsd:decimal
linkml:Time xsd:time
linkml:Date xsd:date
linkml:Datetime xsd:dateTime
linkml:DateOrDatetime linkml:DateOrDatetime
linkml:Uriorcurie IRI
linkml:Curie xsd:string
linkml:Uri IRI
linkml:Ncname xsd:string
linkml:Objectidentifier IRI
linkml:Nodeidentifier NONLITERAL
linkml:Jsonpointer xsd:string
linkml:Jsonpath xsd:string
linkml:Sparqlpath xsd:string
<Container> CLOSED {
( $<Container_tes> <persons> @<Person> * ;
rdf:type [ <Container> ] ?
)
}
<Person> CLOSED {
( $<Person_tes> ( schema1:name @linkml:String ;
<aliases> @linkml:String * ;
schema1:telephone @linkml:String ? ;
<age> @linkml:Integer ?
) ;
rdf:type [ schema1:Person ]
)
}
gen-shacl --no-metadata personinfo-semantic.yaml > personinfo.shacl.ttl
Outputs:
More Info#
FAQ:
Generators:
RDF libraries and tools
rdflib (Python)