Skip to content

#160: Allow lists at sh:class and sh:datatype #395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 19, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 79 additions & 9 deletions shacl12-core/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3008,7 +3008,7 @@ <h3>Value Type Constraint Components</h3>
<section id="ClassConstraintComponent">
<h4>sh:class</h4>
<p>
The condition specified by <code>sh:class</code> is that each <a>value node</a> is a <a>SHACL instance</a> of a given type.
The condition specified by <code>sh:class</code> is that each <a>value node</a> is a <a>SHACL instance</a> of the given type(s).
</p>
<p>
<span class="component-class">Constraint Component IRI</span>: <code>sh:ClassConstraintComponent</code>
Expand All @@ -3024,23 +3024,29 @@ <h4>sh:class</h4>
<td><code>sh:class</code></td>
<td>
The type of all value nodes.
<span data-syntax-rule="class-nodeKind">The values of <code>sh:class</code> in a shape are IRIs.</span>
<span data-syntax-rule="class-nodeKind">The values of <code>sh:class</code> in a shape are either <a>IRIs</a>
or <a>blank nodes</a> that are <a>well-formed</a> <a>SHACL lists</a> where all <a>members</a> are <a>IRIs</a>.</span>
</td>
</tr>
</table>
<div class="def def-text">
<div class="def-header">TEXTUAL DEFINITION</div>
<div class="def-text-body" data-validator="Class">
Let <code>$class</code> be a <a>parameter value</a> for <code>sh:class</code>.
Let <code>classes</code> be a set of <a>IRIs</a> so that
when <code>$class</code> is an <a>IRI</a> then the set only consists of exactly that IRI,
and when <code>$class</code> is a <a>blank node</a> <a>SHACL list</a> then the set consists of
exactly the members of the list.<br /><br />
For each <a>value node</a>
that is either a <a>literal</a>, or a non-literal that is not a <a>SHACL instance</a> of <code>$class</code> in the <a>data graph</a>,
that is either a <a>literal</a>, or a non-literal that is not a <a>SHACL instance</a> of any of the <code>classes</code> in the <a>data graph</a>,
there is a <a>validation result</a> with the <a>value node</a> as <code>sh:value</code>.
</div>
</div>
<p><em>The remainder of this section is informative.</em></p>
<p>
Note that multiple values for <code>sh:class</code> are interpreted as a conjunction,
i.e. the values need to be SHACL instances of all of them.
Use lists for union semantics.
</p>
<aside class="example">
<div class="shapes-graph">
Expand Down Expand Up @@ -3114,6 +3120,37 @@ <h4>sh:class</h4>
</div>
</div>
</aside>
<p>
The following example illustrates the list-based syntax for <code>sh:class</code>,
meaning that the values of the property <code>ex:pet</code> must be either cats or dogs.
</p>
<aside class="example">
<div class="shapes-graph">
<div class="turtle">
ex:ClassListExampleShape
a sh:NodeShape ;
<span class="target-can-be-skipped">sh:targetClass ex:Person ;</span>
sh:property [
sh:path ex:pet ;
sh:class ( ex:Cat ex:Dog ) ;
] .
</div>
<div class="jsonld">
</div>
</div>
<div class="data-graph">
<div class="turtle">
ex:Tessie a ex:Cat .
ex:Rusty a ex:Dog .
ex:Fluffy a ex:Unicorn .

ex:Alice a ex:Person ; ex:pet ex:Tessie, ex:Rusty .
<span class="focus-node-error">ex:Bob</span> a ex:Person ; ex:pet ex:Fluffy .
</div>
<div class="jsonld">
</div>
</div>
</aside>
</section>

<section id="DatatypeConstraintComponent">
Expand All @@ -3134,19 +3171,24 @@ <h4>sh:datatype</h4>
<tr>
<td><code>sh:datatype</code></td>
<td>
The datatype of all value nodes (e.g., <code>xsd:integer</code>).
<span data-syntax-rule="datatype-nodeKind">The values of <code>sh:datatype</code> in a shape are <a>IRIs</a>.</span>
The allowed datatype(s) of all value nodes (e.g., <code>xsd:integer</code>).
<span data-syntax-rule="datatype-maxCount">A shape has at most one value for <code>sh:datatype</code>.</span>
<span data-syntax-rule="datatype-nodeKind">The value of <code>sh:datatype</code> in a shape is either an <a>IRI</a>
or a <a>blank node</a> that is a <a>well-formed</a> <a>SHACL list</a> where all <a>members</a> are <a>IRIs</a>.</span>
</td>
</tr>
</table>
<div class="def def-text">
<div class="def-header">TEXTUAL DEFINITION</div>
<div class="def-text-body" data-validator="Datatype">
Let <code>$datatype</code> be a <a>parameter value</a> for <code>sh:datatype</code>.
Let <code>datatypes</code> be a set of <a>IRIs</a> so that
when <code>$datatype</code> is an <a>IRI</a> then the set only consists of exactly that IRI,
and when <code>$datatype</code> is a <a>blank node</a> <a>SHACL list</a> then the set consists of
exactly the members of the list.<br /><br />
For each <a>value node</a>
that is not a <a>literal</a>, or is a <a>literal</a> with a datatype that does not match <code>$datatype</code>,
there is a <a>validation result</a> with the <a>value node</a> as <code>sh:value</code>.
that is not a <a>literal</a>, or is a <a>literal</a> with a datatype that matches none of the <code>datatypes</code>,
there is a <a>validation result</a> with the <a>value node</a> as <code>sh:value</code>.<br /><br />
The datatype of a literal is determined following the <a data-cite="sparql12-query/#func-datatype">datatype</a> function of SPARQL 1.2.
A <a>literal</a> matches a datatype if the <a>literal</a>'s datatype has the same <a>IRI</a>
and, for the datatypes supported by SPARQL 1.2, is not an <a data-cite="rdf12-concepts#section-Graph-Literal">ill-typed</a> literal.
Expand All @@ -3155,9 +3197,8 @@ <h4>sh:datatype</h4>
<p><em>The remainder of this section is informative.</em></p>
<p>
The values of <code>sh:datatype</code> are typically <a>datatypes</a>, such as <code>xsd:string</code>.
Note that using <code>rdf:langString</code> as value of <code>sh:datatype</code> can be used to test if value nodes have a language tag.
</p>
<aside class="example" title="Shape with sh:datatype property constraint">
<aside class="example" title="Shape with an IRI as sh:datatype property constraint">
<div class="shapes-graph">
<div class="turtle">
ex:DatatypeExampleShape
Expand Down Expand Up @@ -3226,6 +3267,34 @@ <h4>sh:datatype</h4>
</div>
</div>
</aside>
<p>
The following example illustrates the list-based syntax, meaning that all values of
<code>rdfs:label</code> must be either <code>xsd:string</code> or <code>rdf:langString</code>.
Note that using <code>rdf:langString</code> as value of <code>sh:datatype</code> can be used to test if value nodes have a language tag.
</p>
<aside class="example" title="Shape with a list of IRIs as sh:datatype property constraint">
<div class="shapes-graph">
<div class="turtle">
ex:TextExampleShape
a sh:NodeShape ;
<span class="target-can-be-skipped">sh:targetNode ex:Estonia, ex:GreatBritain ;</span>
sh:property [
sh:path rdfs:label ;
sh:datatype ( xsd:string rdf:langString ) ;
] .
</div>
<div class="jsonld">
</div>
</div>
<div class="data-graph">
<div class="turtle">
ex:Estonia rdfs:label "Estonia", "Estland"@de .
<span class="focus-node-error">ex:GreatBritain</span> rdfs:label "Great Britain", "&lt;b&gt;Great&lt/b&gt; Britain"^^rdf:HTML .
</div>
<div class="jsonld">
</div>
</div>
</aside>
</section>

<section id="NodeKindConstraintComponent">
Expand Down Expand Up @@ -6656,6 +6725,7 @@ <h2>Changes between SHACL 1.0 Core and SHACL 1.2 Core</h2>
<li>Added the new class <a href="#ShapeClass"><code>sh:ShapeClass</code></a> for implicit class targets, see <a href="https://github.com/w3c/data-shapes/issues/212">Issue 212</a></li>
<li>Moved SPARQL-based validators from Core to an Appendix of SHACL-SPARQL, see <a href="https://github.com/w3c/data-shapes/issues/271">Issue 271</a></li>
<li>Added the new constraint component <a href="#ExpressionConstraintComponent"><code>sh:expression</code></a>, see <a href="https://github.com/w3c/data-shapes/issues/357">Issue 357</a></li>
<li>The values of <a href="#ClassConstraintComponent"><code>sh:class</code></a> and <a href="#DatatypeConstraintComponent"><code>sh:datatype</code></a> can now also be lists, indicating a union of choices, see <a href="https://github.com/w3c/data-shapes/issues/160">Issue 160</a></li>
</ul>
</section>
</body>
Expand Down
45 changes: 45 additions & 0 deletions shacl12-test-suite/tests/core/node/datatype-003.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://datashapes.org/sh/tests/core/node/datatype-003.test#> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@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 sht: <http://www.w3.org/ns/shacl-test#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:TestShape
rdf:type sh:NodeShape ;
sh:datatype ( xsd:string rdf:langString ) ;
sh:targetNode "<span>Hello</span>"^^rdf:HTML ;
sh:targetNode "G'day"@en-AU ;
sh:targetNode "Hallo"@de ;
sh:targetNode "Hello" ;
.
<>
rdf:type mf:Manifest ;
mf:entries (
<datatype-003>
) ;
.
<datatype-003>
rdf:type sht:Validate ;
rdfs:label "Test of sh:datatype at node shape 003" ;
mf:action [
sht:dataGraph <> ;
sht:shapesGraph <> ;
] ;
mf:result [
rdf:type sh:ValidationReport ;
sh:conforms "false"^^xsd:boolean ;
sh:result [
rdf:type sh:ValidationResult ;
sh:focusNode "<span>Hello</span>"^^rdf:HTML ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:DatatypeConstraintComponent ;
sh:sourceShape ex:TestShape ;
sh:value "<span>Hello</span>"^^rdf:HTML ;
] ;
] ;
mf:status sht:approved ;
.
1 change: 1 addition & 0 deletions shacl12-test-suite/tests/core/node/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
mf:include <closed-002.ttl> ;
mf:include <datatype-001.ttl> ;
mf:include <datatype-002.ttl> ;
mf:include <datatype-003.ttl> ;
mf:include <disjoint-001.ttl> ;
mf:include <equals-001.ttl> ;
mf:include <expression-001.ttl> ;
Expand Down
98 changes: 98 additions & 0 deletions shacl12-test-suite/tests/core/property/class-002.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://datashapes.org/sh/tests/core/property/class-002.test#> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@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 sht: <http://www.w3.org/ns/shacl-test#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:InvalidResource1
rdf:type rdfs:Resource ;
ex:testProperty ex:InvalidResource1 ;
ex:testProperty "A string" ;
.
ex:OtherClass
rdf:type rdfs:Class ;
.
ex:OtherClassInstance
rdf:type ex:OtherClass ;
.
ex:SubClass
rdf:type rdfs:Class ;
rdfs:subClassOf ex:SuperClass ;
.
ex:SubClassInstance
rdf:type ex:SubClass ;
.
ex:SuperClass
rdf:type rdfs:Class ;
.
ex:SuperClassInstance
rdf:type ex:SuperClass ;
.
ex:TestShape
rdf:type sh:NodeShape ;
sh:property ex:TestShape-testProperty ;
sh:targetNode ex:InvalidResource1 ;
sh:targetNode ex:ValidResource1 ;
sh:targetNode ex:ValidResource2 ;
.
ex:TestShape-testProperty
sh:path ex:testProperty ;
rdfs:label "test property" ;
sh:class ( ex:SuperClass ex:OtherClass ) ;
.
ex:ValidResource1
rdf:type rdfs:Resource ;
ex:testProperty ex:OtherClassInstance ;
ex:testProperty ex:SubClassInstance ;
ex:testProperty ex:SuperClassInstance ;
.
ex:ValidResource2
rdf:type rdfs:Resource ;
ex:testProperty [
rdf:type ex:SubClass ;
] ;
ex:testProperty [
rdf:type ex:SuperClass ;
] ;
.
<>
rdf:type mf:Manifest ;
mf:entries (
<class-002>
) ;
.
<class-002>
rdf:type sht:Validate ;
rdfs:label "Test of sh:class at property shape 002" ;
mf:action [
sht:dataGraph <> ;
sht:shapesGraph <> ;
] ;
mf:result [
rdf:type sh:ValidationReport ;
sh:conforms "false"^^xsd:boolean ;
sh:result [
rdf:type sh:ValidationResult ;
sh:focusNode ex:InvalidResource1 ;
sh:resultPath ex:testProperty ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:ClassConstraintComponent ;
sh:sourceShape ex:TestShape-testProperty ;
sh:value ex:InvalidResource1 ;
] ;
sh:result [
rdf:type sh:ValidationResult ;
sh:focusNode ex:InvalidResource1 ;
sh:resultPath ex:testProperty ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:ClassConstraintComponent ;
sh:sourceShape ex:TestShape-testProperty ;
sh:value "A string" ;
] ;
] ;
mf:status sht:approved ;
.
62 changes: 62 additions & 0 deletions shacl12-test-suite/tests/core/property/datatype-004.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://datashapes.org/sh/tests/core/property/datatype-004.test#> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@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 sht: <http://www.w3.org/ns/shacl-test#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:InvalidInstance1
ex:value 42 ;
.
ex:TestShape
rdf:type sh:NodeShape ;
sh:property ex:TestShape-value ;
sh:targetNode ex:InvalidInstance1 ;
sh:targetNode ex:ValidInstance1 ;
sh:targetNode ex:ValidInstance2 ;
sh:targetNode ex:ValidInstance3 ;
.
ex:TestShape-value
sh:path ex:value ;
sh:datatype ( xsd:string rdf:langString ) ;
.
ex:ValidInstance1
ex:value "A" ;
.
ex:ValidInstance2
ex:value "A" ;
.
ex:ValidInstance3
ex:value "A"@en ;
.
<>
rdf:type mf:Manifest ;
mf:entries (
<datatype-004>
) ;
.
<datatype-004>
rdf:type sht:Validate ;
rdfs:label "Test of sh:datatype at property shape 004" ;
mf:action [
sht:dataGraph <> ;
sht:shapesGraph <> ;
] ;
mf:result [
rdf:type sh:ValidationReport ;
sh:conforms "false"^^xsd:boolean ;
sh:result [
rdf:type sh:ValidationResult ;
sh:focusNode ex:InvalidInstance1 ;
sh:resultPath ex:value ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:DatatypeConstraintComponent ;
sh:sourceShape ex:TestShape-value ;
sh:value 42 ;
] ;
] ;
mf:status sht:approved ;
.
2 changes: 2 additions & 0 deletions shacl12-test-suite/tests/core/property/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
rdfs:label "Tests converted from http://datashapes.org/sh/tests/tests/core/property" ;
mf:include <and-001.ttl> ;
mf:include <class-001.ttl> ;
mf:include <class-002.ttl> ;
mf:include <datatype-001.ttl> ;
mf:include <datatype-002.ttl> ;
mf:include <datatype-003.ttl> ;
mf:include <datatype-004.ttl> ;
mf:include <datatype-ill-formed.ttl> ;
mf:include <disjoint-001.ttl> ;
mf:include <equals-001.ttl> ;
Expand Down