diff --git a/shacl12-core/index.html b/shacl12-core/index.html index 0c130e0d..354d4af2 100644 --- a/shacl12-core/index.html +++ b/shacl12-core/index.html @@ -2520,6 +2520,7 @@

Node Expressions

@@ -2806,7 +2807,7 @@

Conformance Checking

has been reported by it.

- Conformance checking produces true if and only if a given focus node + Conformance checking produces true if and only if a given focus node conforms to a given shape, and false otherwise.

@@ -6074,6 +6075,62 @@

sh:reifierShape, sh:reificationRequired

+ +
+

sh:nodeByExpression

+

+ sh:nodeByExpression specifies the condition that each value node conforms to the + node shapes produced by a node expression. + The evaluation of these node expressions is repeated for all value nodes of the shape + as the focus node. +

+

+ Constraint Component IRI: sh:NodeByExpressionConstraintComponent +

+ +
Parameters:
+ + + + + + + + + +
PropertySummary and Syntax Rules
sh:nodeByExpression + The node shapes that all value nodes need to conform to. + The values of sh:nodeByExpression in a shape must be well-formed node expressions. +
+
+
TEXTUAL DEFINITION
+
+Let $expr be a value of sh:nodeByExpression. +For each value node v: perform a conformance check of +v against each output node of evalExpr(expr, +data graph, v, {}) s. A failure +MUST be produced if the conformance check of v against +s produces a failure. Otherwise, if v does +not conform to s, there is a validation result +with v as sh:value and a deep copy of +s as sh:sourceConstraint. +
+
+

The remainder of this section is informative.

+

+ sh:nodeByExpression functions similarly to sh:node, but instead of referencing a fixed node shape, + a referenced node expression is used to dynamically compute the set of node shapes to which each value node must conform. +

+

+ Note that `sh:node` and `sh:nodeByExpression` exhibit the same behavior when given a value that is an IRI of a node shape. + In this case, `sh:node` directly validates against the specified node shape, whereas `sh:nodeByExpression` interprets the IRI + as an IRI expression that evaluates to a set containing the same node shape. +

+

+ For a simple example of its use, refer to the example for sh:node, + replacing `sh:node` with `sh:nodeByExpression`. +

+
@@ -6975,6 +7032,7 @@

Changes between SHACL 1.0 Core and SHACL 1.2 Core

  • Added the new class sh:ShapeClass for implicit class targets; see Issue 212
  • Moved SPARQL-based validators from Core to an Appendix of SHACL-SPARQL; see Issue 271
  • Added the new constraint component sh:expression; see Issue 357
  • +
  • Added the new constraint component sh:nodeByExpression, see Issue 408
  • Added the new value sh:ByTypes for sh:closed; see Issue 172
  • The values of sh:class and sh:datatype can now also be lists, indicating a union of choices; see Issue 160
  • diff --git a/shacl12-test-suite/tests/core/node/manifest.ttl b/shacl12-test-suite/tests/core/node/manifest.ttl index 59b174b3..16be05e8 100644 --- a/shacl12-test-suite/tests/core/node/manifest.ttl +++ b/shacl12-test-suite/tests/core/node/manifest.ttl @@ -32,6 +32,7 @@ mf:include ; mf:include ; mf:include ; + mf:include ; mf:include ; mf:include ; mf:include ; diff --git a/shacl12-test-suite/tests/core/node/nodeByExpression-001.ttl b/shacl12-test-suite/tests/core/node/nodeByExpression-001.ttl new file mode 100644 index 00000000..b687e6ce --- /dev/null +++ b/shacl12-test-suite/tests/core/node/nodeByExpression-001.ttl @@ -0,0 +1,59 @@ +@prefix dash: . +@prefix ex: . +@prefix mf: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix sht: . +@prefix xsd: . + +ex:InvalidInstance + rdf:type ex:TestClass ; + rdfs:label "Invalid instance" ; +. +ex:TestClass + rdf:type rdfs:Class ; + rdf:type sh:NodeShape ; + rdfs:label "Test class" ; + rdfs:subClassOf rdfs:Resource ; + # Only using an IRI Expression here because Core doesn't define interesting node expressions + sh:nodeByExpression ex:TestNodeShape ; +. +ex:TestNodeShape + rdf:type sh:NodeShape ; + sh:class ex:OtherClass ; +. +ex:ValidInstance + rdf:type ex:OtherClass ; + rdf:type ex:TestClass ; + rdfs:label "Valid instance" ; +. +<> + rdf:type mf:Manifest ; + mf:entries ( + + ) ; +. + + rdf:type sht:Validate ; + rdfs:label "Test of sh:nodeByExpression at 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 ex:InvalidInstance ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraint ex:TestNodeShape ; + sh:sourceConstraintComponent sh:NodeConstraintComponent ; + sh:sourceShape ex:TestClass ; + sh:value ex:InvalidInstance ; + ] ; + ] ; + 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..50991ab0 100644 --- a/shacl12-test-suite/tests/core/property/manifest.ttl +++ b/shacl12-test-suite/tests/core/property/manifest.ttl @@ -33,6 +33,7 @@ mf:include ; mf:include ; mf:include ; + mf:include ; mf:include ; mf:include ; mf:include ; diff --git a/shacl12-test-suite/tests/core/property/nodeByExpression-001.ttl b/shacl12-test-suite/tests/core/property/nodeByExpression-001.ttl new file mode 100644 index 00000000..9de6e0df --- /dev/null +++ b/shacl12-test-suite/tests/core/property/nodeByExpression-001.ttl @@ -0,0 +1,117 @@ +@prefix dash: . +@prefix ex: . +@prefix mf: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix sht: . +@prefix xsd: . + +ex:Anon + rdf:type ex:Person ; + ex:firstName "Anon" ; +. +ex:AssignedToShape + rdf:type sh:NodeShape ; + rdfs:comment "All assignees must have an email and a last name." ; + sh:property [ + sh:path ex:email ; + sh:maxCount 1 ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ex:lastName ; + sh:maxCount 1 ; + sh:minCount 1 ; + ] ; +. +ex:Issue + rdf:type rdfs:Class ; + rdf:type sh:NodeShape ; + rdfs:label "Issue" ; + rdfs:subClassOf rdfs:Resource ; + sh:property ex:Issue-assignedTo ; + sh:property ex:Issue-submittedBy ; +. +ex:Issue-assignedTo + sh:path ex:assignedTo ; + sh:class ex:Person ; + # Only using an IRI Expression here because Core doesn't define interesting node expressions + sh:nodeByExpression ex:AssignedToShape ; +. +ex:Issue-submittedBy + sh:path ex:submittedBy ; + sh:class ex:Person ; + sh:minCount 1 ; +. +ex:Issue_1 + rdf:type ex:Issue ; + ex:assignedTo ex:Anon ; + ex:submittedBy ex:Anon ; + rdfs:label "Issue 1" ; +. +ex:Issue_2 + rdf:type ex:Issue ; + ex:assignedTo ex:JohnDoeWithEmail ; + ex:submittedBy ex:Anon ; + rdfs:label "Issue 2" ; +. +ex:JohnDoeWithEmail + rdf:type ex:Person ; + ex:email "john@doe.com" ; + ex:firstName "John" ; + ex:lastName "Doe" ; +. +ex:Person + rdf:type rdfs:Class ; + rdf:type sh:NodeShape ; + rdfs:label "Person" ; + rdfs:subClassOf rdfs:Resource ; + sh:property [ + sh:path ex:email ; + ex:datatype xsd:string ; + rdfs:label "email" ; + ] ; + sh:property [ + sh:path ex:firstName ; + rdfs:label "first name" ; + sh:datatype xsd:string ; + sh:maxCount 1 ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ex:lastName ; + rdfs:label "last name" ; + sh:datatype xsd:string ; + ] ; +. +<> + rdf:type mf:Manifest ; + mf:entries ( + + ) ; +. + + rdf:type sht:Validate ; + rdfs:label "Test of sh:nodeByExpression 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:Issue_1 ; + sh:resultPath ex:assignedTo ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraint ex:AssignedToShape ; + sh:sourceConstraintComponent sh:NodeConstraintComponent ; + sh:sourceShape ex:Issue-assignedTo ; + sh:value ex:Anon ; + ] ; + ] ; + mf:status sht:approved ; +. diff --git a/shacl12-vocabularies/shacl.ttl b/shacl12-vocabularies/shacl.ttl index 67ee87b3..d889b971 100644 --- a/shacl12-vocabularies/shacl.ttl +++ b/shacl12-vocabularies/shacl.ttl @@ -915,6 +915,26 @@ sh:node rdfs:isDefinedBy sh: . +sh:NodeByExpressionConstraintComponent + a sh:ConstraintComponent ; + rdfs:label "Node by expression constraint component"@en ; + rdfs:comment "A constraint component that can be used to verify that all value nodes conform to the node shape(s) produced by the given node expression."@en ; + sh:parameter sh:NodeByExpressionConstraintComponent-nodeByExpression ; + rdfs:isDefinedBy sh: . + +sh:NodeByExpressionConstraintComponent-nodeByExpression + a sh:Parameter ; + sh:path sh:nodeByExpression ; + rdfs:isDefinedBy sh: . + +sh:nodeByExpression + a rdf:Property ; + rdfs:label "node by expression"@en ; + rdfs:comment "Links a shape to node expressions, indicating the node shape(s) that all value nodes must conform to."@en ; + # rdfs:range sh:NodeShape ; (node expression) + rdfs:isDefinedBy sh: . + + sh:NodeKindConstraintComponent a sh:ConstraintComponent ; rdfs:label "Node-kind constraint component"@en ;