Repository navigation
gen-shacl rules: translate equals_string on IRI- and typed-literal-valued ranges (follow-up to linkml/linkml#3989) #37
Copy link
Copy link
Open
Labels
enhancementNew feature or requestNew feature or request
Description
What
Since linkml#3989, and in linkml#3990, gen-shacl translates equals_string / equals_string_in in rules only on slots that hold strings or xsd:boolean. Strings means an enum, a type with datatype xsd:string, or no range. On any other range the rule is skipped with a warning (ShaclGenerator._value_terms), while gen-json-schema enforces it with const.
Reproduction (linkml#3990 head c89faac)
# schema.yaml
id: https://example.org/r
name: r
prefixes:
ex: https://example.org/r/
linkml: https://w3id.org/linkml/
default_prefix: ex
imports: [linkml:types]
classes:
Thing:
attributes:
guard: {range: string}
target: {range: uriorcurie}
rules:
- preconditions: {slot_conditions: {guard: {value_presence: PRESENT}}}
postconditions: {slot_conditions: {target: {equals_string: "ex:a"}}}# data.yaml
guard: g
target: ex:b# both.yaml
plugins:
JsonschemaValidationPlugin: {closed: true}
linkml.validator.plugins.shacl_validation_plugin.ShaclValidationPlugin: {}linkml-validate --config both.yaml -s schema.yaml -C Thing data.yaml
[ERROR] [data.yaml/0] 'ex:a' was expected in /target
Rule 1 of class 'Thing': skipped, because equals_string(_in) on slot 'target', whose range 'uriorcurie' is neither an enum nor a type with datatype xsd:string or xsd:boolean.
Other ranges behave the same way. The RDF term column shows rdflib_dumper / JSON-LD context / context with --xsd-anyuri-as-iri.
| Range | RDF term | JSON Schema |
|---|---|---|
uri, uriorcurie |
IRI / "V"^^xsd:anyURI (CURIE unexpanded) / IRI |
const, enforced |
typeof: string, uri: xsd:anyURI |
IRI / "V"^^xsd:anyURI / IRI |
enforced |
typeof: string, uri: xsd:token |
"V"^^xsd:token |
enforced |
| class with identifier | IRI | const on the id, enforced |
date, datetime, time |
typed literal (rdflib rewrites …Z to …+00:00) |
enforced |
integer, float, decimal |
typed literal | const: "3" next to type: integer/number: rejects every value |
Contract (decide first)
- Metamodel
equals_string: "the slot must have range string and the value of the slot must equal the specified value". - Code:
SchemaLoader._check_equals_stringand gen-shacl's per-slot check both raise for any slot-level range other than literallystring. That includes enums,curie,uriorcurieandtypeof: string. - Docs and tests use it on other ranges:
docs/howtos/model-property-graphs.md:421(uriorcurie);docs/faq/modeling.md:116,125(enum, rule preconditions);tests/linkml/test_compliance/test_boolean_slot_compliance.py:1054(class range).
array.yaml:equals_stringused on enum-ranged slot in array.yaml linkml/linkml#3453 (enum range inarray.yaml) is still open, although linkml-model replaced that use withifabsent(Fix equals_string on enum-ranged array_linearization_order linkml/linkml-model#251, in 1.11.0-rc2).- Other types: numeric ranges belong to
equals_number. For booleans, see question 4 on feat(gen-shacl): translate presence-implies-value rules to SHACL-SPARQL linkml/linkml#3989.
Proposed approach (if in scope)
- Render each value as the term its range produces. Derive it from the range's type
uri, not from a list of type names:- an expanded IRI for IRI-rendered ranges and class references;
"lex"^^datatypefor typed literals, rejecting ill-typed values at generation time.
- Compare with
_sparql_is_one_of(=), and decide which comparison the rule means.- SPARQL 1.1 §17.3 compares by value only numerics, strings,
xsd:booleanandxsd:dateTime. - For other datatypes
=is RDFterm-equal, which "produces a type error if the arguments are both literal but are not the same RDF term" (§17.4.1.7). - rdflib also compares
xsd:dateby value. JSON Schema compares the lexical string.
- SPARQL 1.1 §17.3 compares by value only numerics, strings,
- Settle the
xsd:anyURIrendering first. The dumper emits an IRI. The context emits a typed literal unless--xsd-anyuri-as-iriis set (feat(generators): add --xsd-anyuri-as-iri flag for cross-generator IRI consistency linkml/linkml#3292). Related: fix(rdf): give values of URI-like types one RDF term kind in every generator #32, Make it possible to map a URI to an RDF Node linkml/linkml#2495. - One helper covers every translation: presence-implies-value, exclusive-value and the composed translation (feat(gen-shacl): add a compositional fallback for rule-to-SPARQL conversion linkml/linkml#3990) all share
_value_terms.
Acceptance criteria
- For each newly supported range, SHACL and JSON Schema agree on the same instances. Extend
_json_schema_and_shacl_verdicts(test_shaclgen.py) to load data throughrdflib_dumperas well as through the JSON-LD context. - Ill-typed values fail at generation time, and a non-canonical lexical form in the data is tested.
- Numeric ranges stay skipped unless the contract changes.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
[-]gen-shacl rules: translate equals_string on IRI- and typed-literal-valued ranges (follow-up to #3989)[/-][+]gen-shacl rules: translate equals_string on IRI- and typed-literal-valued ranges (follow-up to linkml/linkml#3989)[/+]