Validating RDF data with SHACL

latest update 2022-11-17  

Introduction

This topic is an attempt to apply the new W3C Recommendation "Shapes Constraint Language (SHACL)", dated 20 July 2017, to ISO 15926-7/8 data.

Reference is made to that Recommendation https://www.w3.org/TR/shacl/ and a (free) on-line book with ample examples http://book.validatingrdf.com and in particular Chapter 5.

Also of interest: SHACL Test Suite and Implementation Report - W3C Document 09 January 2018

Refer to the topic SHACL Portal for an overview of SHACL (is still Work In Progress).

This technology is very well suited for the validation of ISO 15926-8 exchange files.

Validating a declared object

Assume the declaration of a PhysicalObject:

      :112473e9-7642-45d2-ade2-c68a5ed47cf3
            rdf:type lci:InanimatePhysicalObject, dm:ActualIndividual, dm:WholeLifeIndividual, rdl:RDS414081061 ;  # PUMP SYSTEM
            rdf:label "TK349834-a" ;
            meta:hasLifecycleActivity rdl:RDS9661877 ; # MANUFACTURING
            meta:valEffectiveDate "2018-01-09T14:05:23Z"^^xsd:dateTime . 

This is the RDF code that shall be validated with SHACL, thereby referring to the code below that generically applies to the above declaration.

Related SHACL code

@prefix : <http://data.15926.org/shacl/> 
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh:    <http://www.w3.org/ns/shacl#> .
@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
@prefix dm:    <http://data.15926.org/dm/> .
@prefix lci:   <http://data.15926.org/lci/> .
@prefix meta:  <http://data.15926.org/meta/> .
@prefix rdl:   <http://data.15926.org/rdl/> .
.
:InanimatePhysicalObjectShape a sh:NodeShape ; # NOTE - For WholeLifeIndividuals only, see separate shape for TemporalParts.
    sh:nodeKind sh:IRI ;
    sh:targetClass lci:InanimatePhysicalObject ;
# Any InanimatePhysicalObject shall be typed with lci:InanimatePhysicalObject
    sh:property [
        sh:path rdf:type ;
        sh:hasValue lci:InanimatePhysicalObject  ;
        sh:qualifiedMinCount 1 ;
        sh:qualifiedMaxCount 1 ;
    ] ;
# The InanimatePhysicalObject shall at least be typed with one member of dm:ClassOfInanimatePhysicalObject (= a member of a subclass of lci:ClassOfPhysicalObject, e.g. rdl:RDS414081061 = PUMP SYSTEM)
    sh:property [
        sh:path (rdf:type rdf:type) ;
        sh:hasValue dm:ClassOfInanimatePhysicalObject ;
        sh:minCount 1 ;
    ];
# The InanimatePhysicalObject shall be typed with exactly one of the classes in the list
    sh:property [
        sh:path rdf:type ;
        sh:in   ( lci:NonActualIndividual dm:ActualIndividual ) ;
        sh:qualifiedMinCount 1 ;
        sh:qualifiedMaxCount 1 ;
    ] ;
# The InanimatePhysicalObject shall be typed with dm:WholeLifeIndividual
    sh:property [
        sh:path rdf:type ;
        sh:hasValue dm:WholeLifeIndividual ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
# The InanimatePhysicalObject shall have one rdfs:label
    sh:property [
        sh:path rdfs:label ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
# The InanimatePhysicalObject may have one or more skos:altLabel
    sh:property [
        sh:path skos:altLabel ;
        sh:datatype xsd:string ;
        sh:minCount 0 ;
    ] ;
# The InanimatePhysicalObject may have one meta:hasLifecycleActivity
    sh:property [
        sh:path meta:hasLifecycleActivity ;
        sh:in ( rdl:RDS9661877 rdl:RDS9664082 rdl:RDS2229993 rdl:RDS9685637 rdl:RDS9658862 rdl:RDS9686717  rdl:RDS9660257 rdl:RDS9661742 rdl:RDS14526342 ) ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# The InanimatePhysicalObject shall have exactly one meta:valEffectiveDate
    sh:property [
        sh:path meta:valEffectiveDate ;
        sh:datatype xsd:dateTime ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
# The InanimatePhysicalObject may not have a meta:valDeprecationDate (after all this is about a declaration)
    sh:property [
        sh:path meta:valDeprecationDate ;
        sh:datatype xsd:dateTime ;
        sh:minCount 0 ;
        sh:maxCount 0 ;
    ] .

Validating a template instance

Assume the instance of the template IndividualHasPropertyWithValue:

:456c1b40-9e4d-4751-a3b2-cd7882e93c31
    rdf:type tpl:IndividualHasPropertyWithValue ;
    rdfs:label "[Stream] individual [P-101-S-IN] has a [GAUGE PRESSURE] of [15.8] [BAR GAUGE]" ;
    tpl:hasPropertyPossessor :a0cb1994-fcfd-4203-a492-be481a6f1dcc ; # Stream P-101-S-IN (earlier declared)
    tpl:hasPropertyType rdl:RDS7345161 ; # GAUGE PRESSURE
    tpl:valPropertyValue "15.8"^^xsd:decimal ;
    tpl:hasScale rdl:RDS1348874 ; # BAR GAUGE
    meta:hasLifecycleActivity rdl:RDS2227510 ; # OPERATIONS
    meta:valEffectiveDate "2018-11-15T13:45:23Z"^^xsd:dateTime . 

Related SHACL code

@prefix : <http://data.15926.org/shacl/> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh:    <http://www.w3.org/ns/shacl#> .
@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
@prefix dm:    <http://data.15926.org/dm/> .
@prefix lci:   <http://data.15926.org/lci/> .
@prefix meta:  <http://data.15926.org/meta/> .
@prefix rdl:   <http://data.15926.org/rdl/> .

:IndividualHasPropertyWithValueShacl a sh:NodeShape ;
    sh:targetClass lci:Template ;
    sh:nodeKind sh:IRI ;
    sh:closed true ; # Only the sh:path's shown below are acceptable
    sh:class tpl:IndividualHasPropertyWithValue ;
# The Template shall have exactly one sh:property called tpl:hasPropertyPossessor of which the object shall be typed dm:PossibleIndividual or a subtype thereof
    sh:property [
        sh:path (tpl:hasPropertyPossessor rdf:type);
        sh:hasValue dm:PossibleIndividual ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path tpl:hasPropertyPossessor ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ];
# The Template shall have exactly one sh:property called tpl:hasPropertyType of which the object shall be a subclass of dm:Property
    sh:property [
        sh:path (tpl:hasPropertyType rdfs:subClassOf) ;
        sh:hasValue dm:Property ;
        sh:minCount 1 ;
    ];
    sh:property [
        sh:path tpl:hasPropertyType ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ];
# The Template shall have exactly one sh:property called tpl:valPropertyValue with an xsd:decimal data value
    sh:property [
        sh:path     tpl:valPropertyValue ;
        sh:datatype xsd:decimal ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .
# The Template shall have exactly one sh:property called tpl:hasScale of which the object shall be typed dm:Scale
    sh:property [
        sh:path (tpl:hasScale rdf:type) ;
        sh:hasValue dm:Scale ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path tpl:hasScale ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ];
# The Template shall have exactly one meta:valEffectiveDate
    sh:property [
        sh:path     meta:valEffectiveDate ;
        sh:datatype xsd:dateTime ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
# The Template may have one meta:valDeprecationDate
    sh:property [
        sh:path     meta:valDeprecationDate ;
        sh:datatype xsd:dateTime ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] .

Validating an Information Model

Assume an information model of a centrifugal pump:

For each node a sh:NodeShape is defined, and above relations are defined as sh:Property of the Node that is the subject of that Property.

For example between PUMP and PUMP DESIGN CLASS:

Related SHACL code

@prefix : <http://data.15926.org/shacl/> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh:    <http://www.w3.org/ns/shacl#> .
@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
@prefix dm:    <http://data.15926.org/dm/> .
@prefix lci:   <http://data.15926.org/lci/> .
@prefix meta:  <http://data.15926.org/meta/> .
@prefix rdl:   <http://data.15926.org/rdl/> .
@prefix pred: <http://data.15926.org/pred/> . # a collection of standard Property Shapes, that are defined in the background with one or more ISO 15926-7/8 Templates.

<> owl:imports  . # imports all SHACL code for declared objects (like :PhysicalObjectShape above) so that we re-use that code. # NOTE - This obviously still has to be built

# ANY INDIVIDUAL DESIGNED CENTRIFUGAL PUMP
api610:DesignCentrifugalPumpShape a sh:NodeShape ;
    sh:node :API610Shape ; 
    sh:targetClass :InanimatePhysicalObjectShape ;
# The (individual) DesignCentrifugalPump is classified (typed) with CentrifugalPumpAndPumpSystem
    sh:property [
        sh:path pred:type ;
        sh:targetNode :CentrifugalPumpAndPumpSystem ;
        sh:minCount 1 ;
    ] ;
# The (individual) DesignCentrifugalPump may contain one StreamSegmentThroughPump
    sh:property [
        sh:path pred:contains ;
        sh:targetNode :StreamSegmentThroughPump ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# The (individual) DesignCentrifugalPumpSystem is required by ProcessEngineeringActivity
    sh:property [
        sh:path pred:requiredBy ;
        sh:targetNode :ProcessEngineeringActivity ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
# The (individual) DesignCentrifugalPump may be located in one DesignPumpLocation
    sh:property [
        sh:path pred:locatedIn ;
        sh:targetNode :DesignPumpLocation ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] .

# ANY CLASS OF DESIGNED CENTRIFUGAL PUMP SYSTEM
:DesignCentrifugalPumpSystemShape a sh:NodeShape ;
    sh:node :CentrifugalPumpSystemShape;
    sh:targetClass rdl:RDS416834 ;
# A DesignCentrifugalPumpSystem may be defined by CentrifugalPumpDataSheet
    sh:property [
        sh:path pred:isDefinedBy ;
        sh:targetNode :CentrifugalPumpDataSheet ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# Members of DesignCentrifugalPumpSystem have exactly one CentrifugalBarePump as a part
    sh:property [
        sh:path pred:hasAssemblyPart ;
        sh:targetNode :CentrifugalBarePump ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may have one :BearingLubricationSystem as a part
    sh:property [
        sh:path pred:hasAssemblyPart ;
        sh:targetNode :BearingLubricationSystem ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may have one or two PumpDrivers as a part
    sh:property [
        sh:path pred:hasAssemblyPart ;
        sh:targetNode :PumpDriver ;
        sh:minCount 1 ;
        sh:maxCount 2 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may have zero, one or two PumpCouplings as a part
    sh:property [
        sh:path pred:hasAssemblyPart ;
        sh:targetNode :PumpCoupling ;
        sh:minCount 0 ;
        sh:maxCount 2 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may have one PipingPlan as a part
    sh:property [
        sh:path pred:hasAssemblyPart ;
        sh:targetNode :PumpPipingPlan ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may be subjected to one ShopInspectionHydrostatic (at a time)
    sh:property [
        sh:path pred:subjectedTo ;
        sh:targetNode :ShopInspectionHydrostatic ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may be subjected to one ShopInspectionPerformanceVibration (at a time)
    sh:property [
        sh:path pred:subjectedTo ;
        sh:targetNode :ShopInspectionPerformanceVibration ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may be subjected to one ShopInspectionTwoHourRun (at a time)
    sh:property [
        sh:path pred:subjectedTo ;
        sh:targetNode :ShopInspectionTwoHourRun ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] ;
# Members of DesignCentrifugalPumpSystem may perform one :PumpingActivity (at a time)
    sh:property [
        sh:path pred:performerOf ;
        sh:targetNode :PumpingActivity ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
    ] .

Tools

Below is a screen dump of a tool called Playground, that can be found at http://shacl.org/playground/


Work To Do

 


SHACL Constructs used in this Topic

(copied for quick reference from the W3C Recommendation)

This is only a subset of SHACL. Implementing ISO 15926 requires only the basic functions of whatever language is used. That is caused by the fact that ISO 15926 has an upper ontology (Part 2) that covers non-modal logic and is meant to register facts-only because it is to put life-cycle information on record in an integrated manner. It also assumes that the data coming from the source applications, and mapped to ISO 15926-8 format, are what they are and don't need to be semantically validated. That responsibility lies with those applications (and its users of course).


Tables in the book

SHACL validation result properties

Property

Description

sh:focusNode

The focus node that was being validated when the error happened

sh:resultPath

The path from the focus node. This property is optional usually corresponds to the sh:path declaration of property shapes

sh:value

The value that violated the constraint, when available

sh:sourceShape

The shape that the focus node was validated against when the constraint was violated.

sh:sourceConstraintComponent

The IRI that identifies the component that caused the violation.

sh:detail

May point to further details about the cause of the error. This property can be used for reporting errors in nested nested shapes.

sh:resultMessage

Textual details about the error. This message can be affected by the sh:message property (see section 5.6.4)

sh:resultSeverity

A value which is equal to the sh:severity value of the shape that caused the violation error. If the shape doesn’t have sh:severity declaration then the default value will be sh:Violation.

 

Table 5.2: SHACL and SPARQL paths
SHACL path SPARQL path
schema:name schema:name
[sh:inversePath schema:knows] ^schema:knows
(schema:knows schema:name) schema:knows/schema:name
[sh:alternativePath (schema:knows schema:follows)] schema:knows| schema:follows
[sh:zeroOrOnePath schema:knows] schema:knows?
[sh:oneOrMorePath schema:knows] schema:knows+
([sh:zeroOrMorePath schema:knowsschema:name) schema:knows*/schema:name

 

Target declarations

Value

Description

sh:targetNode

Directly point to a node

sh:targetClass

All nodes that are instances of some class

sh:targetSubjectsOf

All nodes that are subjects of some predicate

sh:targetObjectsOf

All nodes that are objects of some predicate

 

Constraints on values

Operation

Description

sh:datatype

Specifies the values must be literals with some datatype

sh:class

Specifies that values must be SHACL instances of some class

sh:nodeKind

Possible values: sh:BlankNode, sh:IRI, sh:Literal, sh:BlankNodeOrIRI, sh:BlankNodeOrLiteral, sh:IRIOrLiteral

sh:in

Enumerates the value nodes that a property is allowed to have

sh:hasValue

A node must have a given value

 

Node kinds

Nodekind

Description

sh:IRI

Nodes must be IRIs

sh:BlankNode

Nodes must be Blank nodes

sh:Literal

Nodes must be Literals

sh:BlankNodeOrLiteral

Nodes must be Blank nodes or literals

sh:BlankNodeOrIRI

Nodes must be Blank nodes or IRIs

sh:IRIOrLiteral

Nodes must be IRIs or literals

 

Logical operators

Operation

Description

sh:and

sh:and (S1 ... SN) specifies that each value node must conform to all the shapes S1SN.

sh:or

sh:or (S1 ... SN) specifies that each value node conforms to at least one of the shapes S1SN.

sh:not

sh:not S specifies that each value node must not conform to S.

sh:xone

sh:xone (S1 ... SN) specifies that exactly one node conforms to one of the shapes S1SN.

 

Closed shapes

Parameter

Description

sh:closed

Valid resources must only have values for properties that appear as values of sh:path in property shapes.

sh:ignoredProperties

List of predicates that are also permitted in addition to those that are explicitly enumerated

 

Property pair constraints

Operation

Description

sh:equals

The sets of values from both properties at a given focus node must be equal

sh:disjoint

The sets of values from both properties at a given focus node must be different

sh:lessThan

Current values must be smaller than another property values

sh:lessThanOrEquals

Current values must be smaller or equal than another property values