TAP to SHACL example

Last week I posted Application Profile to Validation with TAP to SHACL about converting a DCMI Tabular Application Profile (DC TAP) to SHACL in order to validate instance data. I ended by saying that I needed more examples in order to test that it worked: that is, not only check that the SHACL is valid, but also that validates / raises errors as expected when used with instance data.

SHACL Person Example

Here’s a simple example of a person and their employer, drawn from the example used in the SHACL spec. The TAP for this only has three lines:

Line 1: Person data may have zero or one ex:ssn property for Social Security Number, which must be a string matching the regular expression for nnn-nn-nnnn (n = any digit)

Line 2: Person data may have zero or more ex:worksFor properties with an IRI referencing a node that matches the CompanyShape.

Line 3: Company data must have one and only one occurrence of the rdf:type property with the value ex:Company (or equivalent IRI).

And the extended TAP Shapes csv has two:

Line 1: nodes of class ex:Person must match the PersonShape, which is closed, though any occurrence of rdf:type is ignored, any failure to match is a Violation of the application profile.

Line 2: object nodes of the ex:worksFor property must match the CompanyShape, which is open, any failure to match is a Violation of the application profile.

The SHACL produced is

# SHACL generated by python AP to shacl converter
@base <http://example.org/> .
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<PersonShape> a sh:NodeShape ;
    sh:closed true ;
    sh:description "Node shape for person class"@en ;
    sh:ignoredProperties ( rdf:type ) ;
    sh:name "Person Shape"@en ;
    sh:property <personshapeEmployer>,
        <personshapeSocialSecurityNumber> ;
    sh:targetClass <Person> .

<CompanyShape> a sh:NodeShape ;
    sh:class <Company> ;
    sh:closed false ;
    sh:description "Shape for employers"@en ;
    sh:name "Employer"@en ;
    sh:targetObjectsOf <woksFor> .

<personshapeEmployer> a sh:PropertyShape ;
    sh:name "Employer"@en-US ;
    sh:node <CompanyShape> ;
    sh:nodeKind sh:IRI ;
    sh:path <worksFor> .

<personshapeSocialSecurityNumber> a sh:PropertyShape ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
    sh:name "Social Security Number"@en-US ;
    sh:nodeKind sh:Literal ;
    sh:path <ssn> ;
    sh:pattern "^\\d{3}-\\d{2}-\\d{4}$" .

Results

This isn’t the same SHACL as in the spec, but when used with instance data (sample data in github, three are invalid taken from the spec, and one is valid data) in the Join-up itb SHACL validator it gives the same result as expected:

screen shot of validation results for RDF data showing two erros.

I had to make one change to the program to get this working, adding an “ignore props” column to the shape information sheet, which is then processed into sh:ignoredProperties.

I guess it is worth noting that the SHACL generated from tap2shacl is always more verbose than hand written examples: some defaults are always explicit, and property shapes are always identified objects in their own block rather than bnodes in the NodeShape. That can make the output less human readable. Also, as I currently re-use property shapes that recur on different node shapes, there can be unnecessary repetition. There is a mechanism in TAP for setting “defaults” which might be useful when, say, all instances of dct:description must follow the same rules whatever class they are used on.

My next example will (hopefully) be based on a well known real-world application profile; a lot longer, and introducing some more complex rules. Watch this space.

One thought on “TAP to SHACL example

Comments are closed.