fix: Turtle serializer abbreviates typed literals without validating lexical forms (#772) - #827
fix: Turtle serializer abbreviates typed literals without validating lexical forms (#772)#827jeswr wants to merge 2 commits into
Conversation
#772) The Turtle serializer normalized xsd:boolean literals by only checking for the lexical form "1", so literals with the (most common) lexical form "true" were silently serialized as "false", corrupting data on round-trip. Accept both canonical ("true"/"false") and numeric ("1"/"0") lexical forms of the xsd:boolean lexical space when normalizing, and add unit tests covering all four forms. Fixes #772 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses @jeswr's review on #827: the Turtle serializer abbreviated xsd:integer/decimal/double/boolean literals to native tokens based only on the datatype URI, without checking the lexical form. Invalid forms were mangled or silently lost: - lit('yes', xsd:boolean) serialized as false (value lost) - lit('abc', xsd:integer) serialized as bare abc (invalid Turtle) - lit('1.2.3', xsd:decimal) serialized as 1.2.3 (invalid Turtle) - lit('NaN'/'INF', xsd:double) serialized as NaN.0e0 (mangled) Now each case abbreviates only when the value is a valid lexical form that Turtle can express natively, and otherwise falls through to the verbose "value"^^datatype serialization, so round-trips are lossless. INF/-INF/NaN are valid xsd:double values but have no native Turtle DOUBLE token, so they also serialize verbosely. Trailing-dot decimals (e.g. "2.") are valid xsd:decimal and now normalize to a Turtle-valid token ("2.0") instead of emitting invalid syntax. Adds native/verbose serialization tests for all four datatypes plus serialize-parse round-trip tests asserting no data loss for invalid lexical forms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addressed the review comment by broadening the fix from booleans to the whole class of datatype-abbreviation bugs: in Concretely fixed:
Valid lexical forms serialize exactly as before. Tests: per-datatype native/verbose assertions plus serialize→parse round-trips asserting value+datatype preservation for every invalid form (30 new tests; unit suite 338 passing, full |
jeswr
left a comment
There was a problem hiding this comment.
LGTM.
This should be flagged on the linkeddata/rdflib, solidOs and discourse channels before merging in case this affects consumers of the library in unexpected ways.
May need to reserve for an mver.
Fixes #772. Part of the triage programme tracked in #824.
Problem
atomicTermToN3(src/serializer.js) abbreviatesxsd:integer/xsd:decimal/xsd:double/xsd:booleanliterals to native Turtle tokens based only on the datatype URI, without checking that the lexical form is valid for that datatype. Originally reported for booleans in #772, generalized to the whole class after review:"true"^^xsd:booleanfalse— value flipped (the #772 report)true"false"/"1"/"0"^^xsd:booleanfalse/true/false"yes"^^xsd:boolean(invalid)false— value lost"yes"^^xsd:boolean"abc"^^xsd:integer(invalid)abc— invalid Turtle, datatype lost"abc"^^xsd:integer"1.2.3"^^xsd:decimal(invalid)1.2.3— invalid Turtle"1.2.3"^^xsd:decimal"2."^^xsd:decimal(valid XSD; Turtle needs a digit after the dot)2.— invalid Turtle2.0"NaN"/"INF"/"-INF"^^xsd:double(valid XSD; no Turtle DOUBLE form)NaN.0e0etc. — mangled"NaN"^^xsd:doubleetc."abc"^^xsd:double(invalid)abc.0e0— mangled"abc"^^xsd:doubleFix
Each of the four abbreviation cases now abbreviates only when the lexical form is valid for the datatype and expressible as a Turtle token:
xsd:integer—/^[+-]?[0-9]+$/xsd:decimal—/^[+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)$/, then normalized so the token has a digit after the dot (5→5.0,2.→2.0)xsd:double—/^[+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/(excludesINF/-INF/NaN), then the existing./enormalizationxsd:boolean—true/1→true,false/0→falseAnything else falls through to the existing verbose
"value"^^datatypepath, so serialization is lossless and always emits valid Turtle. Diff:src/serializer.js+17/−1; everything else is tests.Ecosystem backward-compatibility assessment
tests/serialize/*-ref.*golden fixture changes; the full fixture corpus serializes identically (CItest:serializegreen on this head).true— e.g. minted by applications vialit('true', undefined, XSD.boolean), or imported from RDF/JS-ecosystem libraries (N3.js et al.) that keep the canonical lexical form. Booleans from rdflib's own Turtle parser orLiteral.fromBooleanare stored as'1'/'0'and always serialized correctly, so SolidOS/NSS round-trips through rdflib's own parser are unaffected.true/falseto'1'/'0'on parse) is untouched here — but both lexical conventions now serialize to correct tokens.release-patch(pure bug fix), thoughrelease-minoris defensible if any serializer-output delta is treated as behavioural.Tests
Extends the existing
tests/unit/serialize-test.js(no new file):true/false/1/0) unchanged; invalid forms (yes,TRUE,2,"",01) serialize verboselyCI is green on this head (Node 22.x / 24.x: unit + serialize golden fixtures + type tests + typedoc).
Relationship to #830
#830 (term-level serialization rework) supersedes this PR and subsumes this fix at its term layer. This PR stands as the minimal, self-contained alternative if the larger rework is not taken — whichever lands, the other should be closed or rebased accordingly.
@jeswr — over to you for review; leaving as draft.