|
| 1 | +# :material-new-box: What's new in version 4 |
| 2 | + |
| 3 | +PyLD 4 moves the RDF datastructure to [:material-library: RDFLib](https://rdflib.readthedocs.io/) |
| 4 | +and adds RDF Dataset Canonicalization 1.0 support. The JSON-LD document APIs |
| 5 | +remain the same, but RDF-facing code should review return types, canonicalization |
| 6 | +defaults, and N-Quads behavior before upgrading. |
| 7 | + |
| 8 | +## RDFLib datasets are now the native RDF model |
| 9 | + |
| 10 | +`jsonld.to_rdf()` returns an `rdflib.Dataset` by default when `format` is not |
| 11 | +set. In PyLD 3.x and earlier, it returned a RDF.js-like nested `dict`. |
| 12 | + |
| 13 | +```python |
| 14 | +from rdflib import Dataset |
| 15 | + |
| 16 | +from pyld import jsonld |
| 17 | + |
| 18 | +dataset = jsonld.to_rdf(doc) |
| 19 | +assert isinstance(dataset, Dataset) |
| 20 | +``` |
| 21 | + |
| 22 | +Request N-Quads when you need a serialized string: |
| 23 | + |
| 24 | +```python |
| 25 | +nquads = jsonld.to_rdf(doc, {"format": "application/n-quads"}) |
| 26 | +``` |
| 27 | + |
| 28 | +Use `legacyMode` when existing code still expects the PyLD 3.x dataset `dict`: |
| 29 | + |
| 30 | +```python |
| 31 | +legacy_dataset = jsonld.to_rdf(doc, {"legacyMode": True}) |
| 32 | +``` |
| 33 | + |
| 34 | +`jsonld.from_rdf()` accepts an `rdflib.Dataset`, an N-Quads string, or the |
| 35 | +legacy dataset `dict`. New code should prefer `rdflib.Dataset` for in-memory RDF |
| 36 | +work and `application/n-quads` for process or storage boundaries. |
| 37 | + |
| 38 | +## `RDFC10` is available and is the normalization default |
| 39 | + |
| 40 | +`jsonld.normalize()` now defaults to `RDFC10`, the RDF Dataset Canonicalization |
| 41 | +1.0 algorithm. `URDNA2015` and `URGNA2012` remain available by setting |
| 42 | +`algorithm` explicitly. |
| 43 | + |
| 44 | +```python |
| 45 | +canonical_urdna2015 = jsonld.normalize( |
| 46 | + doc, |
| 47 | + {"algorithm": "URDNA2015", "format": "application/n-quads"}, |
| 48 | +) |
| 49 | +``` |
| 50 | + |
| 51 | +For RDFC 1.0 test vectors and integrations that need the canonical blank node |
| 52 | +identifier map, pass `outputMap`: |
| 53 | + |
| 54 | +```python |
| 55 | +identifier_map = jsonld.normalize(doc, {"algorithm": "RDFC10", "outputMap": True}) |
| 56 | +``` |
| 57 | + |
| 58 | +`RDFC10` also accepts `hashAlgorithm` for test suites and specialized |
| 59 | +integrations. Most applications should keep the default SHA-256 behavior. |
| 60 | + |
| 61 | +## N-Quads parsing and serialization delegates to RDFLib |
| 62 | + |
| 63 | +PyLD 4 removes the internal `pyld.nquads` parser and serializer module. Public |
| 64 | +JSON-LD APIs still accept and produce N-Quads through `format: "application/n-quads"`, |
| 65 | +but imports from `pyld.nquads` need to be removed. |
| 66 | + |
| 67 | +If you previously used `pyld.nquads` directly, replace it with one of these |
| 68 | +paths: |
| 69 | + |
| 70 | +- Use `jsonld.from_rdf(nquads, {"format": "application/n-quads"})` to convert |
| 71 | + N-Quads to JSON-LD. |
| 72 | +- Use `jsonld.to_rdf(doc, {"format": "application/n-quads"})` to serialize |
| 73 | + JSON-LD as N-Quads. |
| 74 | +- Use `jsonld.parse_nquads(doc, {"legacyMode": True})` to convert nquads |
| 75 | + to a RDF.js-like nested `dict` from PyLD 3.x and earlier. |
| 76 | + Omit `legacyMode` to return an `rdflib.Dataset`. |
| 77 | + This method preserves blank node identifiers from the input document. |
| 78 | +- Use `rdflib.Dataset().parse(data=nquads, format="nquads")` or |
| 79 | + `rdflib.plugins.parsers.nquads.NQuadsParser()` for direct RDFLib parsing. |
| 80 | + Note that, opposed to `jsonld.parse_nquads`, this does NOT preserve blank node |
| 81 | + identifiers by default. |
| 82 | + |
| 83 | +## Compatibility helpers |
| 84 | + |
| 85 | +PyLD 4 includes conversion helpers for applications that need to bridge between |
| 86 | +the old in-memory RDF.js-like nested `dict` and RDFLib: |
| 87 | + |
| 88 | +```python |
| 89 | +from pyld.util import from_legacy_dataset, to_legacy_dataset |
| 90 | + |
| 91 | +rdflib_dataset = from_legacy_dataset(legacy_dataset) |
| 92 | +legacy_dataset = to_legacy_dataset(rdflib_dataset) |
| 93 | +``` |
| 94 | + |
| 95 | +Treat these helpers as migration aids. Prefer RDFLib terms and datasets in new |
| 96 | +code so RDF processing is compatible with the rest of the Python RDF ecosystem. |
| 97 | + |
| 98 | +## Behavior fixes to expect |
| 99 | + |
| 100 | +The RDFLib migration also fixes several RDF conversion edge cases: |
| 101 | + |
| 102 | +- RDF literal lexical forms are preserved more carefully through RDFLib |
| 103 | + conversion, including canonical double output, large numeric values, and |
| 104 | + compound literals. |
| 105 | +- Invalid IRI and language values are skipped during `jsonld.to_rdf()` instead |
| 106 | + of producing invalid triples or crashing. |
| 107 | +- Query and fragment reconstruction in `iri_resolver.unresolve()` is corrected. |
| 108 | +- More W3C URDNA2015, URDNA2012, RDFC10, and JSON-LD `toRdf` tests run through |
| 109 | + the default test runner. |
| 110 | + |
| 111 | +## Upgrade checklist |
| 112 | + |
| 113 | +- Add `rdflib` to application constraints if dependencies are pinned outside |
| 114 | + PyLD's package metadata. |
| 115 | +- Audit `jsonld.to_rdf()` call sites that do not pass `format`. Update them to |
| 116 | + handle `rdflib.Dataset` or temporarily pass `legacyMode`. |
| 117 | +- Remove imports of `pyld.nquads`. |
| 118 | +- Check normalization call sites that relied on the old default algorithm. Pass |
| 119 | + `{"algorithm": "URDNA2015"}` explicitly if that output must remain stable. |
| 120 | +- Compare N-Quads as RDF data or sorted lines in tests unless the test requires |
| 121 | + exact serializer ordering. |
0 commit comments