Shape Tree Example

Shape Trees, as described by chapter 2 of the ShapeTrees specification, describe the expected layout of a tree of resources. The definitions of a Shape Tree can be hosted anywhere that is publicly accessible. They are used in Data Registrations, as described by chapter 6 of the solid interoperability specification, to structure data in a pod according to a flat hierarchy of subcontainers.

Planning ahead for the upcoming evolutions of the Solid specifications, in which the interoperability specification will play an important role, we will already use Shape Trees to set up a similar structure within the client-specific container in the user’s pod. From the corresponding Shape Trees, we will be able to automatically generate full Data Registrations. To do so we will assume that all data in each subcontainer adheres to the Shape Tree the container refers to.

In the rest of this document, we will look into an example of the RDF data (in Turtle format) that describes the Shape Trees for Projects and the Tasks included therein. All code snippets together can be seen as a single Turtle file, that uses the following prefixes (the prefix ex: is used for examples of external URIs; examples of internal URIs have the format <#xyz>).

PREFIX st: <http://www.w3.org/ns/shapetrees#>
PREFIX ex: <https://example.org/>

A Shape Tree defines what kind of resource each instance should be. This can be a Container, a Resource (i.e. containing RDF), or a Non-RDF Resource (the structure of which is not specified further). For Containers and (RDF) Resources, it can also point to a Shape (in ShEx or ShaCL format) that defines the internal structure. Containers can also point to another ShapeTree that defines what instances they themselves should contain. The following snippet describes a structure in which Projects are Containers that contain Tasks as (sub-)Containers, which in turn can contain any opaque resources.

<#example-project-tree>
  a st:ShapeTree ;
  st:expectsType st:Container ;     # or st:Resource, or st:NonRDFResource
  st:shape <#example-project-shape> ;
  st:contains <#example-task-tree> .

<#example-task-tree>
  a st:ShapeTree ;
  st:expectsType st:Container ;     # or st:Resource, or st:NonRDFResource
  st:shape <#example-task-shape> ;
  st:contains st:NonRDFResourceTree .

Alternatively, the same data (a project with tasks) could also be structured as a single RDF resource. To do so, concrete Shape Trees can easily be switched for virtual ones: Containers then become Resources that reference their previously contained Shape Trees via a predicate path in their Shape.

<#example-virtual-project-tree>
  a st:ShapeTree ;
  st:expectsType st:Resource ;
  st:shape <#example-project-shape> ;
  st:references [
    st:referencesShapeTree <#example-virtual-task-tree> ;
    st:viaShapePath "@<#example-project-shape>~ex:hasTask"
  ] .

<#example-virtual-task-tree>
  a st:ShapeTree ;
  st:expectsType st:Resource ;
  st:shape <#example-task-shape> ;
  st:references [
    st:referencesShapeTree st:NonRDFResourceTree ;
    st:viaShapePath "@<#example-task-shape>~ex:hasAttachment"
  ] .

Below you can find an example of a Shape expressed in ShEx, displaying a limited set of the expressive power of ShEx.

<#example-project-shape> {                            # A project
  a [ex:Project] ;                                    # is classified as such,
  ex:title xsd:string ;                               # has a title string
  ex:hasTask @<#example-task-shape> +                 # and one or more tasks.
}

<#example-task-shape> {                               # A task
  a [ex:Task] ;                                       # is classified as such,
  ex:hasDescription xsd:string ;                      # has a description,
  ex:hasAttachment @st:NonRDFResourceTree * ;         # has zero or more attachments,
  ex:taskManagerReference ( xsd:string OR IRI ) ? ;   # has a reference string or link,
  (
    ex:status ex:Backlogged                           # and is either backlogged,
  |
    ex:status [ ex:InProgress ex:Done ] ;             # or in progress or done
    ex:assignee @ex:Employee                          # by an assigned employee.
  )
}

Shape Trees must also be described in human-readable terms, as specified in chapter 6 of the ShapeTrees specification. Descriptions are bundled in Description Sets per language, and point to the Shape Tree they describe. They have a label and (optional) definition, and can additionally select a predicate IRI that for each instance has a human-readable description as its object.

<#example-description-set-en>
  a st:DescriptionSet ;
  st:usesLanguage "en"^^xsd:language .
  
<#example-description-project-en> {
  a st:Description ;
  st:inDescriptionSet <#example-description-set-en> ;
  st:describes <#example-project-tree> ;
  skos:prefLabel "Project"@en ;
  skos:definition "A project containing tasks."@en ;
  st:describesInstance ex:hasTitle .
}

<#example-description-task-en> {
  a st:Description ;
  st:inDescriptionSet <#example-description-set-en> ;
  st:describes <#example-task-tree> ;
  skos:prefLabel "Task"@en ;
  skos:definition "A task assignable to employees."@en ;
  st:describesInstance ex:hasDescription .
}