Skip to content

fix(rdf): preserve string-derived anyURI literals across generators - #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

Problem and behavior

A custom type derived from string with uri: xsd:anyURI represents a URI reference held as literal data. Generating @id for a relative value such as ./data/file.png resolves it against the JSON-LD document base and changes that value into a different RDF term.

This change preserves such values as xsd:anyURI literals across JSON-LD contexts, SHACL, ShEx and the RDF dumper, matching OWL's datatype-property representation. LinkML's uri/uriorcurie family retains its existing IRI behavior in SHACL, ShEx and the dumper, and its existing opt-in IRI representation in JSON-LD/OWL.

Implementation and generality

  • Compare expanded datatype IRIs, not particular prefix spellings. Full IRIs and aliases such as xs:anyURI have the same behavior as xsd:anyURI.
  • Resolve type ancestry and the effective datatype together. Multi-level derived types work; an explicit override to xsd:string remains literal-valued even with URI ancestry.
  • Use the shared compiler predicate in OWL, JSON-LD, SHACL and ShEx. The independently released runtime uses the same short rule without introducing a new cross-package API dependency or narrowing the compiler's declared runtime compatibility.
  • Legacy generators inspect a copy of the already-resolved schema and prefix map. They neither reopen a potentially unavailable source_file nor change the provenance of the schema used for output.
  • ShEx emits literal-valued xsd:anyURI type constraints for string-derived types. Its existing enum and slot-expression behavior is unchanged.
  • SHACL resolves its IRI/non-literal node-kind vocabulary terms by expanded IRI, retaining the distinction between IRI-only and IRI-or-blank-node types.

No consumer class, property, filename, vocabulary list or URI pattern is special-cased. Identifier slots retain their distinct RDF-subject semantics.

Standards, modelling and options

XSD anyURI permits relative URI references as literal values. RDF literals and IRI nodes are distinct terms. JSON-LD type coercion with @id creates identifiers and resolves relative references; datatype coercion preserves the literal representation.

The existing --xsd-anyuri-as-iri option was introduced in upstream #3292. This PR introduces no flag and preserves its default. The explicit custom type already distinguishes literal-valued data, so retaining that data needs no further option. Broader defaults for the built-in URI types remain a separate, existing design discussion in #2495; the default generators are not claimed to agree on those types when the existing option is disabled.

The downstream example in OMB #106 is a UriReference type for file-location data. The regression schemas are generic and exercise both relative and absolute values, not OMB-specific vocabulary.

Cross-generator tests

For scalar ranges, real JSON-LD expansion and generated Python-to-RDF serialization produce isomorphic graphs. pySHACL (including meta-SHACL) and ShEx evaluation accept the resulting graphs and reject IRI/plain-literal substitutions for typed-literal values. Tests also check OWL property kinds in datatype mode, aliases, inherited types, explicit overrides, and source-file independence. Class/literal unions are tested through JSON-LD and SHACL. The runtime tests serialize and parse their RDF terms independently of the compiler package. No mocks are used.

Remaining cross-generator limitation: ShEx does not implement slot-level any_of; a class-or-literal union therefore does not have equivalent ShEx validation. This pre-existing gap requires separate work on branch constraints, collection semantics and class node kinds. An exploratory union implementation failed 33 full-suite cases, and negative consumer tests exposed literal acceptance by empty class shapes. None of that broader implementation is included or claimed as fixed here. Existing assertions were not weakened to make this URI correction pass.

This establishes the tested RDF representation contract for scalar ranges. It does not change JSON Schema/Python validation semantics for scalar values.

Measured effect

SHACL verdict for a valid value of each range, with data loaded through the generated JSON-LD context, the context with --xsd-anyuri-as-iri, and the runtime RDF dumper:

Range main this PR
typeof: string, uri: xsd:anyURI ✗ / ✓ / ✓ (data turned into IRIs) ✓ / ✓ / ✓ (^^xsd:anyURI literals)
typeof: uri ✗ / ✓ / ✗ (dumper wrote a plain literal) ✗ / ✓ / ✓
uri, uriorcurie ✗ / ✓ / ✓ unchanged

No cell gets worse. The remaining ✗ for the built-in types under the default context is the existing default discussed in linkml#2495. nodeidentifier values are written as ^^shex:nonLiteral literals on every data path and never satisfy SHACL's sh:BlankNodeOrIRI; that is a separate, pre-existing bug (fork issue #51).

Validation

  • Rebased onto main bd102062b. Full compiler suite: 12,420 passed; runtime suite: 1,836 passed.
  • CI: see the checks on the current head.

@jdsika jdsika self-assigned this Oct 2, 2026
@jdsika
jdsika marked this pull request as draft October 5, 2026 10:27
@jdsika jdsika changed the title fix(rdf): keep string-derived xsd:anyURI types as typed literals fix(rdf): preserve string-derived anyURI literals across generators Oct 5, 2026
@jdsika
jdsika force-pushed the fix/string-derived-anyuri-types-stay-literals branch from 75f9be6 to 3e1b4ef Compare October 5, 2026 10:42
@jdsika
jdsika marked this pull request as ready for review October 5, 2026 10:55
A string-derived type with uri: xsd:anyURI holds URI-reference data,
including relative paths. Preserve its typed literal in JSON-LD contexts,
SHACL, ShEx and runtime RDF serialization, matching OWL datatype mode.

Resolve type ancestry and the effective expanded datatype together so
inherited types, prefix aliases and explicit datatype overrides agree.
Legacy generators inspect a copy of their resolved schema instead of
reopening source files. Preserve the existing JSON-LD/OWL IRI option and
keep the runtime independent of a new compiler/runtime shared API.

Exercise actual JSON-LD expansion, Python-to-RDF serialization, SHACL and
ShEx validation, including negative RDF-term substitutions. Test mixed
class/literal JSON-LD and SHACL ranges; general ShEx any_of remains a
documented pre-existing limitation outside this correction.

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