diff --git a/ontology/uco/types/types.ttl b/ontology/uco/types/types.ttl
index e70157c..2f646d9 100644
--- a/ontology/uco/types/types.ttl
+++ b/ontology/uco/types/types.ttl
@@ -59,37 +59,47 @@ types:Dictionary
;
rdfs:subClassOf core:UcoInherentCharacterizationThing ;
rdfs:label "Dictionary"@en ;
- rdfs:comment "A dictionary is list of (term/key, value) pairs with each term/key existing no more than once."@en ;
- rdfs:seeAlso [
- a sh:NodeShape ;
- rdfs:comment "This anonymous shape is attached to types:Dictionary with rdfs:seeAlso in order to associate a warning-severity SPARQL-based shape, that will only be necessary as an independent shape until UCO 2.0.0."@en ;
- sh:severity sh:Warning ;
- sh:sparql [
- a sh:SPARQLConstraint ;
- sh:message "A key in a dictionary can appear no more than once."@en ;
- sh:select """
- PREFIX types:
- SELECT $this ?value
- WHERE {
- $this
- types:entry/types:key ?value ;
- .
- }
- GROUP BY ?value
- HAVING (COUNT(?value) > 1)
- """ ;
- ] ;
- sh:targetClass types:Dictionary ;
- ] ;
+ rdfs:comment "A dictionary is list of (term/key, value) pairs with each term/key having an expectation to exist no more than once. types:Dictionary alone does not validate this expectation, but validation is available. For use cases where this expectation must be validated, the subclass types:ProperDictionary should be used instead of types:Dictionary. For instances where this expectation has been found to be violated, the subclass types:ImproperDictionary should be used instead of types:Dictionary."@en ;
sh:property [
sh:class types:DictionaryEntry ;
- sh:minCount "1"^^xsd:integer ;
sh:nodeKind sh:IRI ;
sh:path types:entry ;
] ;
sh:targetClass types:Dictionary ;
.
+types:Dictionary-keyUniqueness-shape
+ a sh:NodeShape ;
+ sh:description "This shape is separated from the types:Dictionary class-shape in order to associate a warning-severity SPARQL-based shape."@en ;
+ sh:severity sh:Warning ;
+ sh:sparql [
+ a sh:SPARQLConstraint ;
+ sh:message "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property."@en ;
+ sh:select """
+ PREFIX types:
+ SELECT $this ?value
+ WHERE {
+ $this
+ types:entry/types:key ?value ;
+ .
+ FILTER NOT EXISTS {
+ $this
+ a types:ImproperDictionary ;
+ .
+ }
+ FILTER NOT EXISTS {
+ $this
+ a types:ProperDictionary ;
+ .
+ }
+ }
+ GROUP BY ?value
+ HAVING (COUNT(?value) > 1)
+ """ ;
+ ] ;
+ sh:targetClass types:Dictionary ;
+ .
+
types:DictionaryEntry
a
owl:Class ,
@@ -188,11 +198,64 @@ types:Identifier
rdfs:comment "An identifier is a string conformant to the specified UUID-based format for UCO object identifiers."@en ;
.
+types:ImproperDictionary
+ a
+ owl:Class ,
+ sh:NodeShape
+ ;
+ rdfs:subClassOf types:Dictionary ;
+ rdfs:label "ImproperDictionary"@en ;
+ owl:disjointWith types:ProperDictionary ;
+ sh:property [
+ sh:datatype xsd:string ;
+ sh:nodeKind sh:Literal ;
+ sh:path types:repeatsKey ;
+ ] ;
+ sh:targetClass types:ImproperDictionary ;
+ .
+
+types:ImproperDictionary-disjointWith-ProperDictionary-shape
+ a sh:NodeShape ;
+ sh:message "types:ImproperDictionary and types:ProperDictionary are disjoint classes."@en ;
+ sh:not [
+ a sh:NodeShape ;
+ sh:class types:ProperDictionary ;
+ ] ;
+ sh:targetClass types:ImproperDictionary ;
+ .
+
types:NativeFormatString
a rdfs:Datatype ;
rdfs:comment "Specifies data in its native format of some external language. The data may be encoded in Base64 per [RFC4648]. Data encoded in Base64 must be denoted as such using the encoded property."@en ;
.
+types:ProperDictionary
+ a
+ owl:Class ,
+ sh:NodeShape
+ ;
+ rdfs:subClassOf types:Dictionary ;
+ rdfs:label "ProperDictionary"@en ;
+ rdfs:comment "A proper dictionary is list of (term/key, value) pairs with each term/key existing no more than once."@en ;
+ owl:disjointWith types:ImproperDictionary ;
+ sh:sparql [
+ a sh:SPARQLConstraint ;
+ sh:message "A key in a proper dictionary can appear no more than once."@en ;
+ sh:select """
+ PREFIX types:
+ SELECT $this ?value
+ WHERE {
+ $this
+ types:entry/types:key ?value ;
+ .
+ }
+ GROUP BY ?value
+ HAVING (COUNT(?value) > 1)
+ """ ;
+ ] ;
+ sh:targetClass types:ProperDictionary ;
+ .
+
types:StructuredText
a rdfs:Datatype ;
rdfs:comment "Expresses string-based data in some information structuring format (e.g., HTML5)."@en ;
@@ -290,6 +353,20 @@ types:key
rdfs:range xsd:string ;
.
+types:repeatsKey
+ a owl:DatatypeProperty ;
+ rdfs:label "repeatsKey"@en ;
+ rdfs:comment "A key found to be repeated in multiple dictionary entries within one dictionary."@en ;
+ rdfs:domain types:ImproperDictionary ;
+ rdfs:range xsd:string ;
+ .
+
+types:repeatsKey-subjects-shape
+ a sh:NodeShape ;
+ sh:class types:ImproperDictionary ;
+ sh:targetSubjectsOf types:repeatsKey ;
+ .
+
types:threadNextItem
a owl:ObjectProperty ;
rdfs:subPropertyOf types:threadSuccessor ;
diff --git a/tests/examples/Makefile b/tests/examples/Makefile
index a379873..770d303 100644
--- a/tests/examples/Makefile
+++ b/tests/examples/Makefile
@@ -29,6 +29,7 @@ all: \
database_records_PASS_validation.ttl \
database_records_XFAIL_validation.ttl \
dictionary_PASS_validation.ttl \
+ dictionary_XFAIL_validation.ttl \
disjointedness_PASS_validation.ttl \
event_XFAIL_validation.ttl \
file_url_PASS_validation.ttl \
@@ -105,6 +106,7 @@ check: \
database_records_PASS_validation.ttl \
database_records_XFAIL_validation.ttl \
dictionary_PASS_validation.ttl \
+ dictionary_XFAIL_validation.ttl \
disjointedness_PASS_validation.ttl \
event_XFAIL_validation.ttl \
file_url_PASS_validation.ttl \
diff --git a/tests/examples/README.md b/tests/examples/README.md
index 4b00de9..03abedd 100644
--- a/tests/examples/README.md
+++ b/tests/examples/README.md
@@ -9,6 +9,32 @@ Two instance data files are currently in the directory:
SHACL validation results are stored in corresponding files named `..._validation.ttl`, to present the current state of validation conditions.
+## Design of the Dictionary tests
+
+The `Dictionary` objects in the `dictionary_*.json` files cover these combinations of asserted type (proper dictionary, improper dictionary, or the generic parent class), whether a dictionary entry key is repeated in the data, and whether the `repeatsKey` property is asserted. (P/X denotes whether the instance is a PASS or XFAIL test case.)
+
+| uuid | P/X | Dictionary type | Key repeats | repeatsKey asserted |
+| --- | --- | --- | --- | --- |
+| `3bb38b3e` | P | `Dictionary` | no | no |
+| `e6dc9c2e` | X | `Dictionary` | no | yes |
+| `e9adf6c1` | P | `Dictionary` | yes | no |
+| `34ac0c49` | X | `Dictionary` | yes | yes |
+| `cbc1c80d` | P | `ImproperDictionary` | no | no |
+| `7fa3ea45` | P | `ImproperDictionary` | no | yes |
+| `14e28425` | P | `ImproperDictionary` | yes | no |
+| `a8e5e8e1` | P | `ImproperDictionary` | yes | yes |
+| `eaded28e` | P | `ProperDictionary` | no | no |
+| `8114819f` | X | `ProperDictionary` | no | yes |
+| `b2baf8af` | X | `ProperDictionary` | yes | no |
+| `f5ae2e6a` | X | `ProperDictionary` | yes | yes |
+
+Other miscellaneous tests are added without full combinatoric review:
+
+* `kb:ProperDictionary-f5ae2e6a-9b10-46f3-8441-30aada36aa1b` also demonstrates an XFAIL case where a key-value *pair* is repeated.
+* `kb:ImproperDictionary-7fa3ea45-6426-4ad3-bb5f-7559e07adeb4` also demonstrates a PASS case where `repeatsKey`'s value is not in the supplied dictionary.
+* `kb:Dictionary-5bc55661-4808-48e6-9e02-80a153eee5d3` demonstrates an XFAIL case where the disjoint `Dictionary` subtypes are both asserted.
+
+
## Design of the Relationship tests
The `Relationship` objects in the `relationship_*.json` files include a numbering scheme in their identifiers, (object class)-(lexical value)-(datatype). These track the following matrix of test cases:
diff --git a/tests/examples/dictionary_PASS.json b/tests/examples/dictionary_PASS.json
index ae34187..31e1aab 100644
--- a/tests/examples/dictionary_PASS.json
+++ b/tests/examples/dictionary_PASS.json
@@ -6,8 +6,26 @@
},
"@graph": [
{
- "@id": "kb:Dictionary-eaded28e-0bf8-4df1-aee8-84d22c09702c",
+ "@id": "kb:Dictionary-3bb38b3e-d47a-43c8-8a77-afc0e6655ce1",
"@type": "types:Dictionary",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-b8a01d49-53c1-440f-a2d5-618b58801d37",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-6cac6c2c-5d4e-45f5-b784-c029c9f9fb6d",
+ "@type": "types:DictionaryEntry",
+ "types:key": "y",
+ "types:value": "2"
+ }
+ ]
+ },
+ {
+ "@id": "kb:ProperDictionary-eaded28e-0bf8-4df1-aee8-84d22c09702c",
+ "@type": "types:ProperDictionary",
"types:entry": [
{
"@id": "kb:DictionaryEntry-314212eb-39c4-4bf3-be3a-f07c38f0eae8",
@@ -24,9 +42,9 @@
]
},
{
- "@id": "kb:Dictionary-a8e5e8e1-b3de-4ac4-99dd-e36f96beea4d",
- "@type": "types:Dictionary",
- "rdfs:comment": "This dictionary will trigger a warning from having two entries keyed with value 'x'.",
+ "@id": "kb:ImproperDictionary-a8e5e8e1-b3de-4ac4-99dd-e36f96beea4d",
+ "@type": "types:ImproperDictionary",
+ "types:repeatsKey": "x",
"types:entry": [
{
"@id": "kb:DictionaryEntry-55786f64-534d-4e8c-8a64-616f708ea4d3",
@@ -44,9 +62,8 @@
},
{
"@id": "kb:Dictionary-e9adf6c1-0287-4290-95a9-c94a128d7ff6",
-
"@type": "types:Dictionary",
- "rdfs:comment": "This dictionary will trigger a warning from having two entries keyed with value 'x'.",
+ "rdfs:comment": "This dictionary, not being typed as a ProperDictionary, will not trigger a warning from having two entries keyed with value 'x'.",
"types:entry": [
{
"@id": "kb:DictionaryEntry-20431f00-64a3-4c0f-94a4-1eb09f8a6b6a",
@@ -61,6 +78,48 @@
"types:value": "1"
}
]
+ },
+ {
+ "@id": "kb:ImproperDictionary-7fa3ea45-6426-4ad3-bb5f-7559e07adeb4",
+ "@type": "types:ImproperDictionary",
+ "repeatsKey": "z"
+ },
+ {
+ "@id": "kb:ImproperDictionary-14e28425-00c1-4f11-b2ed-21390fc0749a",
+ "@type": "types:ImproperDictionary",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-09f23642-389b-4553-b5be-283a6160f534",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-7a84a0d6-d1cd-4291-afb4-c834d611898d",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "2"
+ }
+ ]
+ },
+ {
+ "@id": "kb:ImproperDictionary-cbc1c80d-1bad-4947-8459-c53ff61e8bfa",
+ "@type": "types:ImproperDictionary",
+ "rdfs:comment": "This improper dictionary has no repeated key or assertion of a repeated key. This should not trigger a data error, because the information in the graph could merely be incomplete.",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-ca1910ab-fa26-402a-86bb-229f490dd89a",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-2a13e674-5e95-4a7a-9fac-c90417dcd97c",
+ "@type": "types:DictionaryEntry",
+ "types:key": "y",
+ "types:value": "2"
+ }
+ ]
}
]
}
diff --git a/tests/examples/dictionary_PASS_validation.ttl b/tests/examples/dictionary_PASS_validation.ttl
index b00c4ad..f563abc 100644
--- a/tests/examples/dictionary_PASS_validation.ttl
+++ b/tests/examples/dictionary_PASS_validation.ttl
@@ -8,95 +8,39 @@
[]
a sh:ValidationReport ;
sh:conforms "true"^^xsd:boolean ;
- sh:result
- [
- a sh:ValidationResult ;
- sh:focusNode ;
- sh:resultMessage "A key in a dictionary can appear no more than once." ;
- sh:resultSeverity sh:Warning ;
- sh:sourceConstraint [
- a sh:SPARQLConstraint ;
- sh:message "A key in a dictionary can appear no more than once."@en ;
- sh:select """
- PREFIX types:
- SELECT $this ?value
- WHERE {
+ sh:result [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property." ;
+ sh:resultSeverity sh:Warning ;
+ sh:sourceConstraint [
+ a sh:SPARQLConstraint ;
+ sh:message "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property."@en ;
+ sh:select """
+ PREFIX types:
+ SELECT $this ?value
+ WHERE {
+ $this
+ types:entry/types:key ?value ;
+ .
+ FILTER NOT EXISTS {
$this
- types:entry/types:key ?value ;
+ a types:ImproperDictionary ;
.
}
- GROUP BY ?value
- HAVING (COUNT(?value) > 1)
- """ ;
- ] ;
- sh:sourceConstraintComponent sh:SPARQLConstraintComponent ;
- sh:sourceShape [
- a sh:NodeShape ;
- rdfs:comment "This anonymous shape is attached to types:Dictionary with rdfs:seeAlso in order to associate a warning-severity SPARQL-based shape, that will only be necessary as an independent shape until UCO 2.0.0."@en ;
- sh:severity sh:Warning ;
- sh:sparql [
- a sh:SPARQLConstraint ;
- sh:message "A key in a dictionary can appear no more than once."@en ;
- sh:select """
- PREFIX types:
- SELECT $this ?value
- WHERE {
- $this
- types:entry/types:key ?value ;
- .
- }
- GROUP BY ?value
- HAVING (COUNT(?value) > 1)
- """ ;
- ] ;
- sh:targetClass types:Dictionary ;
- ] ;
- sh:value "x" ;
- ] ,
- [
- a sh:ValidationResult ;
- sh:focusNode ;
- sh:resultMessage "A key in a dictionary can appear no more than once." ;
- sh:resultSeverity sh:Warning ;
- sh:sourceConstraint [
- a sh:SPARQLConstraint ;
- sh:message "A key in a dictionary can appear no more than once."@en ;
- sh:select """
- PREFIX types:
- SELECT $this ?value
- WHERE {
- $this
- types:entry/types:key ?value ;
- .
- }
- GROUP BY ?value
- HAVING (COUNT(?value) > 1)
- """ ;
- ] ;
- sh:sourceConstraintComponent sh:SPARQLConstraintComponent ;
- sh:sourceShape [
- a sh:NodeShape ;
- rdfs:comment "This anonymous shape is attached to types:Dictionary with rdfs:seeAlso in order to associate a warning-severity SPARQL-based shape, that will only be necessary as an independent shape until UCO 2.0.0."@en ;
- sh:severity sh:Warning ;
- sh:sparql [
- a sh:SPARQLConstraint ;
- sh:message "A key in a dictionary can appear no more than once."@en ;
- sh:select """
- PREFIX types:
- SELECT $this ?value
- WHERE {
+ FILTER NOT EXISTS {
$this
- types:entry/types:key ?value ;
+ a types:ProperDictionary ;
.
}
- GROUP BY ?value
- HAVING (COUNT(?value) > 1)
+ }
+ GROUP BY ?value
+ HAVING (COUNT(?value) > 1)
""" ;
- ] ;
- sh:targetClass types:Dictionary ;
- ] ;
- sh:value "x" ;
- ]
- ;
+ ] ;
+ sh:sourceConstraintComponent sh:SPARQLConstraintComponent ;
+ sh:sourceShape types:Dictionary-keyUniqueness-shape ;
+ sh:value "x" ;
+ ] ;
.
diff --git a/tests/examples/dictionary_XFAIL.json b/tests/examples/dictionary_XFAIL.json
new file mode 100644
index 0000000..dd2a456
--- /dev/null
+++ b/tests/examples/dictionary_XFAIL.json
@@ -0,0 +1,122 @@
+{
+ "@context": {
+ "kb": "http://example.org/kb/",
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+ "types": "https://ontology.unifiedcyberontology.org/uco/types/"
+ },
+ "@graph": [
+ {
+ "@id": "kb:Dictionary-5bc55661-4808-48e6-9e02-80a153eee5d3",
+ "@type": [
+ "types:ImproperDictionary",
+ "types:ProperDictionary"
+ ],
+ "rdfs:comment": "This dictionary will trigger an error from being typed as both disjoint subclasses of types:Dictionary.",
+ "types:entry": {
+ "@id": "kb:DictionaryEntry-fa139d6e-2b4d-49e5-8c7d-3cfc635d56e0",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ }
+ },
+ {
+ "@id": "kb:Dictionary-34ac0c49-1042-49c0-8fd6-c42a810e58da",
+ "@type": "types:Dictionary",
+ "rdfs:comment": "This dictionary will trigger an error from using repeatsKey while not also typing itself as a types:ImproperDictionary.",
+ "types:repeatsKey": "x",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-322b718b-3869-48a3-a7bf-d97d5463563b",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-e51c7808-7fcb-423a-95e6-dcb431a3bade",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "2"
+ }
+ ]
+ },
+ {
+ "@id": "kb:ProperDictionary-b2baf8af-3d5d-4c4e-b442-49befefd147e",
+ "@type": "types:ProperDictionary",
+ "rdfs:comment": "This dictionary will trigger an error from having two entries keyed with value 'x'.",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-203a8596-1439-4065-a99f-daf4d530bed7",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-40b9d75d-6a11-4a8f-9951-e96e2c1fe683",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "2"
+ }
+ ]
+ },
+ {
+ "@id": "kb:ProperDictionary-8114819f-d3c8-4e29-9e31-295d771f9db2",
+ "@type": "types:ProperDictionary",
+ "rdfs:comment": "This proper dictionary will trigger an error from using repeatsKey while not being an ImproperDictionary.",
+ "types:repeatsKey": "x",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-1311a664-fce3-4174-ace1-539ac6d54a5f",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-8b149881-5adc-4020-b46f-2be1c60bab83",
+ "@type": "types:DictionaryEntry",
+ "types:key": "y",
+ "types:value": "2"
+ }
+ ]
+ },
+ {
+ "@id": "kb:ProperDictionary-f5ae2e6a-9b10-46f3-8441-30aada36aa1b",
+ "@type": "types:ProperDictionary",
+ "rdfs:comment": "This dictionary will trigger an error from having two entries keyed with value 'x'.",
+ "types:repeatsKey": "x",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-0274c19c-89b9-42b6-a87e-f671cbd2c731",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-de108ec2-8ddd-4201-8267-5a04035ba88e",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ }
+ ]
+ },
+ {
+ "@id": "kb:Dictionary-e6dc9c2e-25bc-422f-8ae8-8457e29f5fde",
+ "@type": "types:Dictionary",
+ "rdfs:comment": "This dictionary will trigger an error from using repeatsKey while not also typing itself as a types:ImproperDictionary.",
+ "types:repeatsKey": "x",
+ "types:entry": [
+ {
+ "@id": "kb:DictionaryEntry-02edb446-1ad5-41ef-8877-fbee912189e7",
+ "@type": "types:DictionaryEntry",
+ "types:key": "x",
+ "types:value": "1"
+ },
+ {
+ "@id": "kb:DictionaryEntry-147908bb-ebba-42e8-854d-72352dc903a1",
+ "@type": "types:DictionaryEntry",
+ "types:key": "y",
+ "types:value": "2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/examples/dictionary_XFAIL_validation.ttl b/tests/examples/dictionary_XFAIL_validation.ttl
new file mode 100644
index 0000000..f15a758
--- /dev/null
+++ b/tests/examples/dictionary_XFAIL_validation.ttl
@@ -0,0 +1,141 @@
+@prefix owl: .
+@prefix rdf: .
+@prefix rdfs: .
+@prefix sh: .
+@prefix types: .
+@prefix xsd: .
+
+[]
+ a sh:ValidationReport ;
+ sh:conforms "false"^^xsd:boolean ;
+ sh:result
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property." ;
+ sh:resultSeverity sh:Warning ;
+ sh:sourceConstraint [
+ a sh:SPARQLConstraint ;
+ sh:message "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property."@en ;
+ sh:select """
+ PREFIX types:
+ SELECT $this ?value
+ WHERE {
+ $this
+ types:entry/types:key ?value ;
+ .
+ FILTER NOT EXISTS {
+ $this
+ a types:ImproperDictionary ;
+ .
+ }
+ FILTER NOT EXISTS {
+ $this
+ a types:ProperDictionary ;
+ .
+ }
+ }
+ GROUP BY ?value
+ HAVING (COUNT(?value) > 1)
+ """ ;
+ ] ;
+ sh:sourceConstraintComponent sh:SPARQLConstraintComponent ;
+ sh:sourceShape types:Dictionary-keyUniqueness-shape ;
+ sh:value "x" ;
+ ] ,
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "Value does not have class types:ImproperDictionary" ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraintComponent sh:ClassConstraintComponent ;
+ sh:sourceShape types:repeatsKey-subjects-shape ;
+ sh:value ;
+ ] ,
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "types:ImproperDictionary and types:ProperDictionary are disjoint classes."@en ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraintComponent sh:NotConstraintComponent ;
+ sh:sourceShape types:ImproperDictionary-disjointWith-ProperDictionary-shape ;
+ sh:value ;
+ ] ,
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "Value does not have class types:ImproperDictionary" ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraintComponent sh:ClassConstraintComponent ;
+ sh:sourceShape types:repeatsKey-subjects-shape ;
+ sh:value ;
+ ] ,
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "Value does not have class types:ImproperDictionary" ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraintComponent sh:ClassConstraintComponent ;
+ sh:sourceShape types:repeatsKey-subjects-shape ;
+ sh:value ;
+ ] ,
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "A key in a proper dictionary can appear no more than once." ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraint [
+ a sh:SPARQLConstraint ;
+ sh:message "A key in a proper dictionary can appear no more than once."@en ;
+ sh:select """
+ PREFIX types:
+ SELECT $this ?value
+ WHERE {
+ $this
+ types:entry/types:key ?value ;
+ .
+ }
+ GROUP BY ?value
+ HAVING (COUNT(?value) > 1)
+ """ ;
+ ] ;
+ sh:sourceConstraintComponent sh:SPARQLConstraintComponent ;
+ sh:sourceShape types:ProperDictionary ;
+ sh:value "x" ;
+ ] ,
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "A key in a proper dictionary can appear no more than once." ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraint [
+ a sh:SPARQLConstraint ;
+ sh:message "A key in a proper dictionary can appear no more than once."@en ;
+ sh:select """
+ PREFIX types:
+ SELECT $this ?value
+ WHERE {
+ $this
+ types:entry/types:key ?value ;
+ .
+ }
+ GROUP BY ?value
+ HAVING (COUNT(?value) > 1)
+ """ ;
+ ] ;
+ sh:sourceConstraintComponent sh:SPARQLConstraintComponent ;
+ sh:sourceShape types:ProperDictionary ;
+ sh:value "x" ;
+ ] ,
+ [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage "Value does not have class types:ImproperDictionary" ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraintComponent sh:ClassConstraintComponent ;
+ sh:sourceShape types:repeatsKey-subjects-shape ;
+ sh:value ;
+ ]
+ ;
+ .
+
diff --git a/tests/examples/test_validation.py b/tests/examples/test_validation.py
index fb3c099..5320ba9 100644
--- a/tests/examples/test_validation.py
+++ b/tests/examples/test_validation.py
@@ -223,8 +223,22 @@ def test_dictionary_PASS() -> None:
"dictionary_PASS_validation.ttl",
True,
expected_focus_node_severities={
- ("http://example.org/kb/Dictionary-a8e5e8e1-b3de-4ac4-99dd-e36f96beea4d", str(NS_SH.Warning)),
- ('http://example.org/kb/Dictionary-e9adf6c1-0287-4290-95a9-c94a128d7ff6', str(NS_SH.Warning)),
+ ("http://example.org/kb/Dictionary-e9adf6c1-0287-4290-95a9-c94a128d7ff6", str(NS_SH.Warning)),
+ }
+ )
+
+def test_dictionary_XFAIL() -> None:
+ confirm_validation_results(
+ "dictionary_XFAIL_validation.ttl",
+ False,
+ expected_focus_node_severities={
+ ("http://example.org/kb/Dictionary-5bc55661-4808-48e6-9e02-80a153eee5d3", str(NS_SH.Violation)),
+ ("http://example.org/kb/Dictionary-e6dc9c2e-25bc-422f-8ae8-8457e29f5fde", str(NS_SH.Violation)),
+ ("http://example.org/kb/Dictionary-34ac0c49-1042-49c0-8fd6-c42a810e58da", str(NS_SH.Warning)),
+ ("http://example.org/kb/Dictionary-34ac0c49-1042-49c0-8fd6-c42a810e58da", str(NS_SH.Violation)),
+ ("http://example.org/kb/ProperDictionary-8114819f-d3c8-4e29-9e31-295d771f9db2", str(NS_SH.Violation)),
+ ("http://example.org/kb/ProperDictionary-b2baf8af-3d5d-4c4e-b442-49befefd147e", str(NS_SH.Violation)),
+ ("http://example.org/kb/ProperDictionary-f5ae2e6a-9b10-46f3-8441-30aada36aa1b", str(NS_SH.Violation)),
}
)