RDF 1.2#

This chapter is a short introduction to RDF 1.2 using rudof.

Preliminaries: install and configure rudof#

%pip install -q "pyrudof>=0.3.22"
%pip install -q plantuml
Note: you may need to restart the kernel to use updated packages.
Note: you may need to restart the kernel to use updated packages.
from pyrudof import Rudof, ResultDataFormat

rudof = Rudof()
import subprocess
import sys
from pathlib import Path

from IPython.display import Image


def show_graph(session, name="out"):
    """Render the RDF data currently loaded in `session` as a diagram."""
    Path(f"{name}.png").unlink(missing_ok=True)
    Path(f"{name}.puml").write_text(
        session.serialize_data(ResultDataFormat.PlantUML), encoding="utf-8"
    )
    subprocess.run(
        [sys.executable, "-m", "plantuml", f"{name}.puml"],
        check=True,
        capture_output=True,
    )
    return Image(f"{name}.png")

Saying things about statements#

RDF 1.2 answers a question that plain RDF handles badly: how do you say something about a statement? Who claimed it, when, with what confidence, for what period of time.

In RDF 1.1 the usual answer was to invent an intermediate node (the “reification” pattern, or Wikidata’s statement nodes) which works but scatters one fact across four or five triples and loses the connection to the original statement.

RDF 1.2 introduces triple terms: a triple can appear as the object of another triple, written between <<( and )>>. Combined with the new rdf:reifies predicate, that gives a direct way to attach information to a statement.

rudof.reset_all()
rudof.read_data("""
 prefix : <http://example.org/>
 prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
 prefix xsd: <http://www.w3.org/2001/XMLSchema#>

 :aliceBelief a :Statement ;
    rdf:reifies <<( :bob :knows :dave )>> ;
    :since 2025 ;
    :accordingTo :dave .
 """)

Read that as: :aliceBelief is a thing that reifies the statement “Bob knows Dave”, and that thing has been held since 2025, according to Dave. The statement itself is a term, so it can be pointed at; the annotations hang off the reifier, not off the statement.

rudof visualizes the two differently, which makes the distinction visible: a statement that is asserted as part of the graph is drawn as a box, while a triple term (which is not asserted) is drawn as a cloud.

show_graph(rudof)
_images/1080e83ac2fee213a6cd35732cc647674541ae492f2c036322b3af219dfebdc5.png

Three ways to write it#

RDF 1.2 Turtle offers three syntaxes for this, of increasing convenience. They all produce triples of the same shape, so the choice is about readability, not meaning.

Triple terms#

The explicit form: write the triple term between <<( and )>> and say what reifies it. This is what the example above used, and it is the form the other two expand into.

rudof.reset_all()
rudof.read_data("""
PREFIX :    <http://www.example.org/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

_:e38  :familyName                     "Smith" .
_:anno rdf:reifies <<( _:e38 :jobTitle "Designer" )>> .
_:anno :accordingTo                     :eric .
 """)

show_graph(rudof)
_images/88c2506a8fbe71c7b9b2035265421efe38f237497b0ed964c3cfe9814034e517.png

Reifying triples#

Enclosing a triple between << and >> is syntactic sugar: introduces a reifier for the triple, which can then be used as a subject. One line instead of three.

rudof.reset_all()
rudof.read_data("""
PREFIX :    <http://www.example.org/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

:employee38 :familyName "Smith" .
<< :employee38 :jobTitle "Assistant Designer" >> :accordingTo :employee22 .
 """)

show_graph(rudof)
_images/7002e4744df555490e358f0dbdabb76201ac3945f221dcc713d9d687ab128899.png

Annotation syntax#

The most compact form. Writing {| … |} right after a statement annotates that statement, without repeating it. The optional ~ :t names the reifier, so you can refer back to it later.

rudof.reset_all()
rudof.read_data("""
 PREFIX :    <http://example.com/>
 PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

 :alice :name "Alice" ~ :t {|
   :statedBy :bob ;
   :recorded "2021-07-07"^^xsd:date
 |} .
 """)

show_graph(rudof)
_images/21fa6c5bdfe05278c7dabffba8717181813fa5a15d26e97a2ee6f761b2babd76.png

Notice in the visualization that the statement :alice :name "Alice" is drawn as a box: the annotation syntax asserts the statement as well as annotating it. That is the difference from the explicit <<( … )>> form, where the triple term on its own asserts nothing.

A worked example#

The annotation syntax comes into its own when the same statement is true several times over with different qualifications. Here, Tim Berners-Lee worked at CERN in two separate spells (1984–1994 and 1980) and received the Princess of Asturias Award in 2002 together with Vinton Cerf.

rudof.reset_all()
rudof.read_data("""
prefix :       <http://example.org/>
prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>

:timbl rdfs:label "Tim Berners Lee" ;
       :employer :CERN {| :start "1984" ;
                          :end   "1994" |}
                       {| :start "1980" ;
                          :end   "1980" |} ;
       :award :PA {| :time "2002" ;
                     :togetherWith :vint |} .
:vint  rdfs:label "Vinton Cerf" .
""")

show_graph(rudof)
_images/caa238e91cb7f60cff0285394c3a239258481dbf2d5c20bf802496082271cd92.png

The next chapter shows how to query these annotations with SPARQL 1.2.

References#