diff --git a/shacl12-core/index.html b/shacl12-core/index.html index 0c130e0d..b89778b7 100644 --- a/shacl12-core/index.html +++ b/shacl12-core/index.html @@ -2751,7 +2751,7 @@

Handling of Ill-formed Shapes Graphs

Handling of Recursive Shapes

-

+

The following properties are the so-called shape-expecting constraint parameters in SHACL Core:

@@ -2810,7 +2811,7 @@

Conformance Checking

conforms to a given shape, and false otherwise.

- Note that some constraint components of SHACL Core (e.g., those of sh:not, sh:or and sh:node) + Note that all shape-expecting constraint parameters of SHACL Core rely on conformance checking. In these cases, the validation results used to determine the outcome of conformance checking are separated from those of the surrounding validation process and typically do not end up in the same validation report @@ -5691,6 +5692,196 @@

sh:property

+
+

sh:memberShape

+

+ sh:memberShape requires that each value node be a SHACL list, + and that each member of that list conforms to the given node shape. +

+

+ Constraint Component IRI: sh:MemberShapeConstraintComponent +

+ +
Parameters:
+ + + + + + + + + +
PropertySummary and Syntax Rules
sh:memberShape + The shape that all members of the SHACL list must conform to. + The value of sh:memberShape must be a well-formed node shape. +
+
+
TEXTUAL DEFINITION
+
+ Let $memberShape be a value of sh:memberShape. + Each value node v must be a SHACL list - a failure MUST be produced if v is not a SHACL list. + For each member m of the list v + a failure MUST be produced if the conformance checking of m against $memberShape produces a failure. + There MUST be at most one validation result for each list v and set of triggering constraints. + + The sh:detail property SHOULD be used to specify the validation results for each member m of the list that fails conformance checking. + m SHOULD be used as the sh:focusNode for each sh:detail result. + + The sh:detail property SHOULD also be used to specify when v is not a valid SHACL list. + v SHOULD be used as the sh:focusNode for this sh:detail result. +
+
+

The remainder of this section is informative.

+

+ In the following example, all values of the property ex:speakerOrder must be RDF lists, and each member of those lists must be an IRI. + Note that sh:memberShape can be used in both property shapes (as shown) and node shapes (where it would apply to the focus node itself). + + Examples of how to generate sh:details can be found in the test cases for sh:memberShape in the SHACL test suite: memberShape-001.ttl. +

+ +
+

sh:qualifiedValueShape, sh:qualifiedMinCount, sh:qualifiedMaxCount

diff --git a/shacl12-test-suite/tests/core/node/manifest.ttl b/shacl12-test-suite/tests/core/node/manifest.ttl index 59b174b3..6dd5e465 100644 --- a/shacl12-test-suite/tests/core/node/manifest.ttl +++ b/shacl12-test-suite/tests/core/node/manifest.ttl @@ -26,6 +26,7 @@ mf:include ; mf:include ; mf:include ; + mf:include ; mf:include ; mf:include ; mf:include ; diff --git a/shacl12-test-suite/tests/core/node/memberShape-001.ttl b/shacl12-test-suite/tests/core/node/memberShape-001.ttl new file mode 100644 index 00000000..b2518285 --- /dev/null +++ b/shacl12-test-suite/tests/core/node/memberShape-001.ttl @@ -0,0 +1,241 @@ +@prefix dash: . +@prefix ex: . +@prefix mf: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix sht: . +@prefix xsd: . + +ex:IRIShape a sh:NodeShape ; + sh:nodeKind sh:IRI . + +ex:IRIListShape + rdf:type sh:NodeShape ; + sh:memberShape ex:IRIShape ; + sh:targetNode ( ex:Alice ex:Bob ) ; + # Valid empty list + sh:targetNode () ; + # _:b1 is valid, the remainder trigger violations (including _:b3 which has no properties in this graph) + sh:targetNode _:b1, _:b2, _:b3, _:b4, _:b5, _:b6, _:b7, _:b9 ; +. + +_:b1 + rdf:first ex:Alice ; + rdf:rest rdf:nil ; + ex:extraProperty "extra" ; +. + +_:b2 + rdf:first ex:Alice ; + rdf:rest ( + "Bob" + ) +. + +_:b4 + rdf:first ex:Alice ; +. + +_:b5 + rdf:first "Charlie" ; + rdf:rest ( + "Donna" + ) +. + +_:b6 + rdf:first "Charlie" ; +. + +_:b8 rdfs:label "Malformed SHACL List" . + +_:b7 + rdf:first "Charlie" ; + rdf:rest _:b8 ; +. + +# using _:b9 and _:b10 to test recursive list error +_:b9 + rdf:first ex:Alice ; + rdf:rest _:b10 ; +. + +_:b10 + rdf:first "Bob" ; + rdf:rest _:b9 . + +<> + rdf:type mf:Manifest ; + mf:entries ( + + ) ; +. + + + rdf:type sht:Validate ; + rdfs:label "Test of sh:memberShape on node shape 001" ; + 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 _:b2 ; + sh:resultMessage "Value does not conform to Shape ex:IRIListShape. See details for more information." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode "Bob" ; + sh:resultMessage "Value is not of Node Kind sh:IRI" ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeKindConstraintComponent ; + sh:sourceShape ex:IRIShape ; + sh:value "Bob" ; + ] ; + ] ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b3 ; + sh:resultMessage "Value is a malformed SHACL List." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:value _:b3 ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b3 ; + sh:resultMessage "Value is a malformed SHACL List. _:b3 is missing rdf:first and rdf:rest properties." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:value _:b3 ; + ] ; + ] ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b4 ; + sh:resultMessage "Value does not conform to Shape ex:IRIListShape. See details for more information." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:value _:b4 ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b4 ; + sh:resultMessage "Value is a malformed SHACL List. _:b4 is missing rdf:rest property." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:value _:b4 ; + ] ; + ] ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b5 ; + sh:resultMessage "Value does not conform to Shape ex:IRIListShape. See details for more information." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode "Charlie" ; + sh:resultMessage "Value is not of Node Kind sh:IRI" ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeKindConstraintComponent ; + sh:sourceShape ex:IRIShape ; + sh:value "Charlie" ; + ], [ + rdf:type sh:ValidationResult ; + sh:focusNode "Donna" ; + sh:resultMessage "Value is not of Node Kind sh:IRI" ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeKindConstraintComponent ; + sh:sourceShape ex:IRIShape ; + sh:value "Donna" ; + ] + ] ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b6 ; + sh:resultMessage "Value does not conform to Shape ex:IRIListShape. See details for more information." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode "Charlie" ; + sh:resultMessage "Value is not of Node Kind sh:IRI" ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeKindConstraintComponent ; + sh:sourceShape ex:IRIShape ; + sh:value "Charlie" ; + ], [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b6 ; + sh:resultMessage "Value is a malformed SHACL List. _:b6 is missing rdf:rest property." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:value _:b6 ; + ] ; + ] ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b7 ; + sh:resultMessage "Value does not conform to Shape ex:IRIListShape. See details for more information." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode "Charlie" ; + sh:resultMessage "Value is not of Node Kind sh:IRI" ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeKindConstraintComponent ; + sh:sourceShape ex:IRIShape ; + sh:value "Charlie" ; + ], [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b8 ; + sh:resultMessage "Value is a malformed SHACL List. _:b8 is missing rdf:first and rdf:rest property." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:value _:b8 ; + ] ; + ] ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b9 ; + sh:resultMessage "Value does not conform to Shape ex:IRIListShape. See details for more information." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode "Charlie" ; + sh:resultMessage "Value is not of Node Kind sh:IRI" ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeKindConstraintComponent ; + sh:sourceShape ex:IRIShape ; + sh:value "Charlie" ; + ], [ + rdf:type sh:ValidationResult ; + sh:focusNode _:b10 ; + sh:resultMessage "Value is a malformed SHACL List. A list cannot have itself in the rdf:rest+ path. List _:b9 follows from _:b10." ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:IRIListShape ; + sh:value _:b9 ; + ] ; + ] ; + ] ; + mf:status sht:approved ; +. diff --git a/shacl12-test-suite/tests/core/property/manifest.ttl b/shacl12-test-suite/tests/core/property/manifest.ttl index 2ab26040..b6463005 100644 --- a/shacl12-test-suite/tests/core/property/manifest.ttl +++ b/shacl12-test-suite/tests/core/property/manifest.ttl @@ -26,6 +26,7 @@ mf:include ; mf:include ; mf:include ; + mf:include ; mf:include ; mf:include ; mf:include ; diff --git a/shacl12-test-suite/tests/core/property/memberShape-001.ttl b/shacl12-test-suite/tests/core/property/memberShape-001.ttl new file mode 100644 index 00000000..40bc2c7b --- /dev/null +++ b/shacl12-test-suite/tests/core/property/memberShape-001.ttl @@ -0,0 +1,89 @@ +@prefix dash: . +@prefix ex: . +@prefix mf: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix sht: . +@prefix xsd: . + +ex:IRIShape a sh:NodeShape ; + sh:nodeKind sh:IRI . + +ex:TestShape + rdf:type sh:NodeShape ; + sh:targetClass ex:ListSubject ; + sh:property ex:TestShape-testProperty ; +. + +ex:TestShape-testProperty + sh:path ex:testProperty ; + sh:memberShape ex:IRIShape ; +. + +# Valid with all IRIs in the list +ex:list1 + rdf:type ex:ListSubject ; + ex:testProperty ( + ex:Alice + ex:Bob + ex:Charlie + ) ; +. + +_:bc rdfs:label "Blank node which triggers violation" . + +# Invalid with a blank node in the list +ex:list2 + rdfs:label "List with a blank node" ; + rdf:type ex:ListSubject ; + ex:testProperty _:b1 ; +. + +_:b1 + rdf:first ex:Bob ; + rdf:rest ( + _:bc + ) +. + +<> + rdf:type mf:Manifest ; + mf:entries ( + + ) ; +. + + + rdf:type sht:Validate ; + rdfs:label "Test of sh:memberShape at property shape 001" ; + 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:list2 ; + sh:resultMessage "Value does not conform to Shape ex:TestShape. See details for more information." ; + sh:resultPath ex:testProperty ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MemberShapeConstraintComponent ; + sh:sourceShape ex:TestShape ; + sh:value _:b1 ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:focusNode _:bc ; + sh:resultMessage "Value is not of Node Kind sh:IRI" ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeKindConstraintComponent ; + sh:sourceShape ex:IRIShape ; + sh:value _:bc ; + ] ; + ] ; + ] ; + mf:status sht:approved ; +. diff --git a/shacl12-vocabularies/shacl-shacl.ttl b/shacl12-vocabularies/shacl-shacl.ttl index 043cccbb..08c78d98 100644 --- a/shacl12-vocabularies/shacl-shacl.ttl +++ b/shacl12-vocabularies/shacl-shacl.ttl @@ -72,10 +72,11 @@ shsh:ShapeShape sh:targetSubjectsOf sh:targetClass, sh:targetNode, sh:targetObjectsOf, sh:targetSubjectsOf ; sh:targetSubjectsOf sh:and, sh:class, sh:closed, sh:datatype, sh:disjoint, sh:equals, sh:flags, sh:hasValue, sh:ignoredProperties, sh:in, sh:languageIn, sh:lessThan, sh:lessThanOrEquals, sh:maxCount, sh:maxExclusive, - sh:maxInclusive, sh:maxLength, sh:minCount, sh:minExclusive, sh:minInclusive, sh:minLength, sh:node, sh:nodeKind, + sh:maxInclusive, sh:maxLength, sh:memberShape, sh:minCount, sh:minExclusive, sh:minInclusive, sh:minLength, sh:node, sh:nodeKind, sh:not, sh:or, sh:pattern, sh:property, sh:qualifiedMaxCount, sh:qualifiedMinCount, sh:qualifiedValueShape, sh:qualifiedValueShape, sh:qualifiedValueShapesDisjoint, sh:qualifiedValueShapesDisjoint, sh:uniqueLang, sh:xone ; + sh:targetObjectsOf sh:memberShape ; # memberShape-node sh:targetObjectsOf sh:node ; # node-node sh:targetObjectsOf sh:not ; # not-node sh:targetObjectsOf sh:property ; # property-node @@ -223,6 +224,10 @@ shsh:ShapeShape sh:maxCount 1 ; # minLength-maxCount sh:minInclusive 0 ; # minLength-minInclusive ] ; + sh:property [ + sh:path sh:memberShape ; + sh:node shsh:NodeShapeShape ; # memberShape-node + ] ; sh:property [ sh:path sh:nodeKind ; sh:in ( sh:BlankNode sh:IRI sh:Literal sh:BlankNodeOrIRI sh:BlankNodeOrLiteral sh:IRIOrLiteral ) ; # nodeKind-in diff --git a/shacl12-vocabularies/shacl.ttl b/shacl12-vocabularies/shacl.ttl index 67ee87b3..2b26c205 100644 --- a/shacl12-vocabularies/shacl.ttl +++ b/shacl12-vocabularies/shacl.ttl @@ -894,6 +894,25 @@ sh:minLength rdfs:range xsd:integer ; rdfs:isDefinedBy sh: . +sh:MemberShapeConstraintComponent + a sh:ConstraintComponent ; + rdfs:label "Member shape constraint component"@en ; + rdfs:comment "Can be used to specify constraints on the members of a given SHACL list. A violation is reported for each member of the list that does not comply with the constraints specified by the given shape. A violation is reported if the value is not a valid SHACL list."@en ; + sh:parameter sh:MemberShapeConstraintComponent-memberShape ; + rdfs:isDefinedBy sh: . + +sh:MemberShapeConstraintComponent-memberShape + a sh:Parameter ; + sh:path sh:memberShape ; + sh:node sh:NodeShape ; + rdfs:isDefinedBy sh: . + +sh:memberShape + a rdf:Property ; + rdfs:label "member shape"@en ; + rdfs:comment "Specifies the shape that all members of RDF lists must conform to."@en ; + rdfs:range sh:NodeShape ; + rdfs:isDefinedBy sh: . sh:NodeConstraintComponent a sh:ConstraintComponent ;