Skip to content

fix(rdf): keep string-derived xsd:anyURI types as typed literals - #32

Open
jdsika wants to merge 1 commit into
mainfrom
fix/string-derived-anyuri-types-stay-literals
Open

jdsika wants to merge 1 commit into
mainfrom
fix/string-derived-anyuri-types-stay-literals

Conversation

@jdsika

@jdsika jdsika commented Oct 2, 2026

Copy link
Copy Markdown

Summary

Fixes # — no issue covers this. Related: linkml#3221 (IRI nodes for uri in the dumper and gen-shacl), linkml#3292 (--xsd-anyuri-as-iri for gen-owl and gen-jsonld-context).

A schema can hold a URI either as a link to a resource or as data. A link is an IRI node. A URI reference held as data — a file path such as ./data/file.png, which may be relative — has to stay a literal: as an IRI, JSON-LD resolves it against the document base and it becomes a machine-specific file:///home/<user>/... IRI.

The way to declare the second is a type derived from string with uri: xsd:anyURI. gen-owl already decides by type ancestry (is_xsd_anyuri_range): only uri, uriorcurie and the types derived from them are promoted to IRIs by --xsd-anyuri-as-iri. The other three RDF components keyed on the xsd:anyURI datatype, so such a type came out differently in each:

before after
gen-owl owl:DatatypeProperty, range xsd:anyURI unchanged
gen-shacl sh:nodeKind sh:IRI sh:nodeKind sh:Literal, sh:datatype xsd:anyURI
gen-jsonld-context --xsd-anyuri-as-iri "@type": "@id" "@type": "xsd:anyURI"
RDF dumper URIRef Literal(..., datatype=xsd:anyURI)

All three now use the predicate gen-owl uses, for single ranges and any_of branches. uri, uriorcurie and their derived types behave exactly as before.

The dumper also resolved the type without inheritance (get_type), so a type derived from uri that declares no datatype of its own was dumped as a plain literal with a warning. It now uses induced_type, as the generators do, and that type is an IRI node.

How was this tested?

  • New tests/linkml/test_generators/test_anyuri_literal_types.py checks one schema with uri, a typeof: uri type and a typeof: string, uri: xsd:anyURI type across all four components: context with and without the flag, an any_of mixing a class with each, SHACL node kind and datatype, OWL property type with and without the flag, and the dumper's terms for an instance.
  • test_jsonldcontextgen.py, test_shaclgen.py, test_shaclgen_naming.py, shacl/, test_owlgen.py, test_pythongen.py — 294 passed, 3 skipped, 3 xfailed.
  • pytest tests/linkml_runtime — 1824 passed, 323 skipped, 2 xfailed, identical before and after the dumper change.
  • ruff check and ruff format --check (0.11.13) clean.

Areas of uncertainty

  • Behaviour change for string-derived xsd:anyURI types in gen-shacl and the dumper. A schema that relied on such a type being an IRI should derive it from uri instead, which states that intent. I found none in the test suite.
  • --xsd-anyuri-as-iri for gen-shacl. Not added: gen-shacl already emits IRIs for uri/uriorcurie, so the remaining divergence is only gen-owl's and the context's default, which the flag addresses.

Checklist

  • My code follows the contributor guidelines
  • I have added tests that prove my fix/feature works
  • Existing tests pass locally with my changes

AI Assistance

If you used AI tools while preparing this PR, you are still the author and responsible for understanding, verifying, and defending your submission. Please engage with reviewers personally rather than through your agent during feedback and revisions. See our AI Covenant for details.

A schema can hold a URI as a link to a resource or as data. A link is an IRI
node. A URI reference held as data - a file path such as ./data/file.png,
which may be relative - has to stay a literal: as an IRI, JSON-LD resolves it
against the document base and it becomes a machine-specific absolute IRI.

The way to declare the second is a type derived from string with
uri: xsd:anyURI. The OWL generator already decides by type ancestry
(is_xsd_anyuri_range): only uri, uriorcurie and the types derived from them
are promoted to IRIs by --xsd-anyuri-as-iri. The other three RDF components
keyed on the xsd:anyURI datatype instead, so such a type was an IRI in SHACL,
in the JSON-LD context (with the flag) and in the RDF dumper, and a literal
in OWL:

- gen-shacl emits sh:nodeKind sh:Literal and sh:datatype xsd:anyURI for it
- gen-jsonld-context keeps "@type": "xsd:anyURI" with --xsd-anyuri-as-iri,
  through the same predicate, for single ranges and any_of branches
- the RDF dumper emits a typed literal for it, and resolves the type with
  inheritance, so a type derived from uri without a datatype of its own is
  an IRI node instead of a plain literal

uri, uriorcurie and their derived types behave exactly as before.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant