Validating RDF data with SHACL

latest update: 2018-01-12

Introduction

This topic is a first 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 .

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

This technology is brandnew and still in development, but, as may be concluded, very well suited for the validation of ISO 15926-8 exchange files.

Validating a declared object

Assume the signature of the TIP for the declaration of a PhysicalObject with the shown example data values:

tip:DeclarationOfPhysicalObject

Role No

Role Variable

Description of Variable

Example values

1

var_FuncType

"Declaration Class" (ClassOfFunctionalObject) that represents the function (not the role) of the PhysicalObject

rdl:RDS327239

2

var_PhysicalObjectType Select from dm:PhysicalObject, dm:FunctionalPhysicalObject or dm:MaterializedPhysicalObject

dm:FunctionalPhysicalObject

3

var_EntityType

ISO 15926 entity type of which the declared PhysicalObject is an instance

lci:InanimatePhysicalObject

4

var_WorldType

Does the PhysicalObject exists in the real world or in some imaginary world like Design World ?

lci:ImaginaryIndividual

5

var_IdentifierValue

Identifier of the PhysicalObject that is being declared and that, normally, shall stay with it forever

P-101

6

var_dateTime

The effectivity dateTime of the information represented here

2018-01-09T14:05:23Z

 

These data in the TIP signature are filled in into the generic code below:

 

:id(var_IdentifierValue)

    rdf:type <var_FuncType>, <var_EntityType>, <var_PhysicalObjectType>, <var_WorldType>, dm:WholeLifeIndividual ;

    rdfs:label  "var_IdentifierValue" ;

    meta:valEffectiveDate "var_dateTime"^^xsd:dateTime .

 

which results, when using the given example data values, in something like:

 

:A3F5BFE4F31F4C65A9DF7B7E2AEE4672

    rdf:type rdl:RDS327239, dm:FunctionalPhysicalObject, lci:InanimatePhysicalObject, lci:ImaginaryIndividual, dm:WholeLifeIndividual ;

    rdfs:label  "P-101" ;

    meta:valEffectiveDate "2018-01-09T14:05:23Z"^^xsd:dateTime .

 

NOTES - The meta:valDeprecationDate will be applicable (much) later and will be added then in the form of an extra triple.

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

Related SHACL code

@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 shacl: <http://data.15926.org/shacl/> .

 

:PhysicalObjectShacl a sh:NodeShape ; # Note that the word Shape has been replaced with Shacl, because Shape is too confusing in the context of ISO 15926.

    sh:nodeKind sh:IRI ;

    sh:targetClass dm:PhysicalObject ;

# The PhysicalObject shall at least be typed with one subclass of lci:FunctionalObject

    sh:property [

        sh:path (rdf:type rdfs:subClassOf) ;

        sh:hasValue lci:FunctionalObject ;

        sh:minCount 1 ;

    ];

# The PhysicalObject shall be typed with exactly one of the classes in the list

    sh:property [

        sh:path rdf:type ;

        sh:in   ( dm:PhysicalObject dm:FunctionalPhysicalObject dm:MaterializedPhysicalObject ) ;

        sh:minCount 1 ;

        sh:maxCount 1 ;

    ] ;

# The PhysicalObject shall be typed with exactly one of the classes in the list

    sh:property [

        sh:path rdf:type ;

        sh:in   ( lci:InanimatePhysicalObject lci:Person lci:Organism lci:Organization lci:Feature lci:InformationObject lci:Energy dm:Stream dm:SpatialLocation ) ;

        sh:minCount 1 ;

    ] ;

# The PhysicalObject shall be typed with exactly one of the classes in the list

    sh:property [

        sh:path rdf:type ;

        sh:in   ( dm:ActualIndividual lci:ImaginaryIndividual ) ;

        sh:minCount 1 ;

        sh:maxCount 1 ;

    ] ;

# The PhysicalObject shall be typed with dm:WholeLifeIndividual

    sh:property [

        sh:path rdf:type ;

        sh:hasValue dm:WholeLifeIndividual ;

        sh:minCount 1 ;

        sh:maxCount 1 ;

    ] ;

# The PhysicalObject shall at least have one rdfs:label

    sh:property [

        sh:path rdfs:label ;

        sh:datatype xsd:string ;

        sh:minCount 1 ;

    ] ;

# The PhysicalObject shall have exactly one meta:valEffectiveDate

    sh:property [

        sh:path meta:valEffectiveDate ;

        sh:datatype xsd:dateTime ;

        sh:minCount 1 ;

        sh:maxCount 1 ;

    ] ;

# The PhysicalObject may have one meta:valDeprecationDate

    sh:property [

        sh:path meta:valDeprecationDate ;

        sh:datatype xsd:dateTime ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] .

Validating a template instance

Assume the signature of the template IndividualHasPropertyWithValue:

IndividualHasPropertyWithValue

Role No

Role Name

Role Object Type

Example values

1

hasPropertyPossessor dm:PossibleIndividual

:8875A120FE284D45A954ABBEB472BB00

2

hasPropertyType dm:SinglePropertyDimension

rdl:RDS7345161 (gauge pressure)

3

valPropertyValue xsd:decimal

15.8

4

hasScale dm:Scale

rdl:RDS1348874 (bar gauge)

 

The example data values in the template signature result in a template instance:

 

:73131D3A1E3042FD8A4E9AEC8E70C99F rdf:type tpl:IndividualHasPropertyWithValue ;

    tpl:hasPropertyPossessor :8875A120FE284D45A954ABBEB472BB00 ;

    tpl:hasPropertyType rdl:RDS7345161 ;

    tpl:valPropertyValue "15.8"^^xsd:decimal ;

    tpl:hasScale rdl:RDS1348874 ;

    meta:valEffectiveDate "2018-01-09T14:05:23Z"^^xsd:dateTime .

Related SHACL code

@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 shacl: <http://data.15926.org/shacl/> .

 

:IndividualHasPropertyWithValueShacl a sh:NodeShape ;

    sh:targetClass tm: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:

  • define the PUMP with three sh:Property's : 1x classifier, 1x contains, and 1x locatedAt;
  • define PUMP DESIGN CLASS with 7x hasPart, 3x subjectedTo, and 1x performs.

Related SHACL code

@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 shacl: <http://data.15926.org/shacl/> .

<> owl:imports <http://data.15926.org/shacl/declared_objects> . # 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

:CentrifugalPumpShacl a sh:NodeShape ;

    sh:node :PumpShacl ; # re-using the generic shacl:PumpShacl that in turn is re-using the shacl:PhysicalObjectShacl NodeShape.

    sh:targetClass lci:InanimatePhysicalObject ;

# The (individual) CentrifugalPump is classified (typed) with :CentrifugalPumpClass

    sh:property [

        sh:path imod:classifier ;

        sh:targetNode :CentrifugalPumpClass ;

        sh:minCount 1 ;

    ] ;

# The (individual) CentrifugalPump may contain one :Stream

    sh:property [

        sh:path imod:contains ;

        sh:targetNode :Stream ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] ;

# The (individual) CentrifugalPump may be located at one :PumpLocation

    sh:property [

        sh:path imod:locatedAt ;

        sh:targetNode :PumpLocation ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] .

 

:CentrifugalPumpClassShacl a sh:NodeShape ;

    sh:node :DynamicPumpClassShacl ;

    sh:targetClass rdl:RDS416834 ;

# Members of CentrifugalPumpClass have exactly one :PumpCasing as a part

    sh:property [

        sh:path imod:hasPart ;

        sh:targetNode :PumpCasing ;

        sh:minCount 1 ;

        sh:maxCount 1 ;

    ] ;

# Members of CentrifugalPumpClass have at least one :Impeller as a part

    sh:property [

        sh:path imod:hasPart ;

        sh:targetNode :Impeller ;

        sh:minCount 1 ;

# Members of CentrifugalPumpClass have at least one :Bearing as a part

    ] ;

    sh:property [

        sh:path imod:hasPart ;

        sh:targetNode :Bearing ;

        sh:minCount 1 ;

    ] ;

# Members of CentrifugalPumpClass may have one :BearingLubricationSystem as a part

    sh:property [

        sh:path imod:hasPart ;

        sh:targetNode :BearingLubricationSystem ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] ;

# Members of CentrifugalPumpClass have at least one :PumpDriver as a part

    sh:property [

        sh:path imod:hasPart ;

        sh:targetNode :PumpDriver ;

        sh:minCount 1 ;

    ] ;

# Members of CentrifugalPumpClass have at least one :PumpCoupling as a part

    sh:property [

        sh:path imod:hasPart ;

        sh:targetNode :PumpCoupling ;

        sh:minCount 1 ;

    ] ;

# Members of CentrifugalPumpClass may have one :PumpPipingPlan as a part

    sh:property [

        sh:path imod:hasPart ;

        sh:targetNode :PumpPipingPlan ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] ;

# Members of CentrifugalPumpClass may be subjected to one :ShopInspectionHydrostatic (at a time)

    sh:property [

        sh:path imod:subjectedTo ;

        sh:targetNode :ShopInspectionHydrostatic ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] ;

# Members of CentrifugalPumpClass may be subjected to one :ShopInspectionHydrostatic (at a time)

    sh:property [

        sh:path imod:subjectedTo ;

        sh:targetNode :ShopInspectionPerformanceVibration ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] ;

# Members of CentrifugalPumpClass may be subjected to one :ShopInspectionTwoHourRun (at a time)

    sh:property [

        sh:path imod:subjectedTo ;

        sh:targetNode :ShopInspectionTwoHourRun ;

        sh:minCount 0 ;

        sh:maxCount 1 ;

    ] ;

# Members of CentrifugalPumpClass may perform one :PumpingActivity (at a time)

    sh:property [

        sh:path imod:performs ;

        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

  • Validate the above.
  • Study SHACL-SPARQL, extending SHACL-Core with SPARQL; presumably we will need that because of the federated architecture of ISO 15926.
  • Find out if and how we can work with pick-lists in shape definitions

 

 

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