Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
29bdd3c
Add stub for RDFC-1.0 and prepare tests.
mielvds Feb 5, 2026
b82d026
Add option to return the bnode map.
mielvds Apr 22, 2026
74abbd3
Add configurable hashAlgorithm
mielvds Apr 22, 2026
4791e49
Introduce rdflib and legacy mode. Move parse and serialize functions …
mielvds May 4, 2026
52a1265
Convert main algorithms to using rdflib
mielvds May 4, 2026
5eef168
Remove legacy nquads parser. Also add minor typing and legacy conversion
mielvds May 4, 2026
e977793
Remove whitespace and satisfy linter
mielvds May 4, 2026
588336e
Fix incomplete typing
mielvds May 4, 2026
dfbf86b
Adjust tests nquad output
mielvds May 21, 2026
f70dc67
Remove accidental NQuadsParser
mielvds Jun 5, 2026
4392e45
localize legacy mode
mielvds Jun 5, 2026
a5bb695
Fix compound literal tests
mielvds Jun 11, 2026
f297b61
Do not normalize literals in compound literals
mielvds Jun 11, 2026
baaa0b8
minor code improvements
mielvds Jun 17, 2026
40cf7aa
Fix regressions after complex merge
mielvds Jul 16, 2026
cd0ebc9
Migrate tests with legacy mode
mielvds Jul 16, 2026
e2ebe06
Fix rdflib datatypes not converted to string
mielvds Jul 16, 2026
ae9e25c
Fix regression with fractionals and adjust tests
mielvds Jul 16, 2026
1e4c151
Organize imports
mielvds Jul 16, 2026
3709285
Adjust changelog to rdflib migration
mielvds Aug 12, 2026
862ac59
Add legacyMode to options and document its use.
mielvds Aug 12, 2026
10a5c02
Sublass NQuadsParser to construct unnormalized literals.
mielvds Aug 13, 2026
74c15f0
Satisfy linter
mielvds Aug 13, 2026
e080800
Add tests for legacy mode
mielvds Aug 13, 2026
f16b3a6
satisfy linter
mielvds Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 42 additions & 26 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,50 @@

### Added

- migrate `canon.py` to `rdflib`
- added `rdflib` dependency in all relevant config and code files.
- added `util.py`:
- added the functions `from_legacy_dataset()` and `to_legacy_dataset()` to convert an `rdflib.Dataset` to an RDFJS-like `dict` and back wherever needed.
- added unittests in `tests/test_util.py` for these functions.
- implement RDFC1.0
- added new class `RDFC10` (subclass of `URDNA2015`) in `canon.py`
- added the RDFC1.0 test-suite in `tests/runtests.py`
- added support for testing blank-node identifier maps.
- added support for testing with different hashing algorithms
- Migrate `canon.py` to `rdflib`
- Added `rdflib` dependency in all relevant config and code files.
- Added `util.py`:
- Added the functions `from_legacy_dataset()` and `to_legacy_dataset()` to convert an `rdflib.Dataset` to an RDFJS-like `dict` and back wherever needed.
- Added unittests in `tests/test_util.py` for these functions.
- Implement RDFC1.0
- Added new class `RDFC10` (subclass of `URDNA2015`) in `canon.py`
- Added the RDFC1.0 test-suite in `tests/runtests.py`
- Added support for testing blank-node identifier maps.
- Added support for testing with different hashing algorithms

### Removed

- **BREAKING**: Removed the internal `pyld.nquads` parser/serializer module.

### Fixed

- Preserved RDF literal lexical forms when converting through RDFLib, including canonical double output, large numeric values, and compound literal handling.
- Fixed `iri_resolver.unresolve()` query/fragment reconstruction while cleaning up the resolver docstring.

### Changed
- migrate `canon.py` to `rdflib`:
- now use `rdflib` for RDF term type checking (e.g., checking is something is a bnode), looping over triples/quads, nquads serialization and constructing RDF terms (custom deepcopy is no longer needed)
- move nquads parsing from`JsonLdProcessor.normalize()` to `URDNA2015.main()` so all parsing and serialization is handled by the same class.
- move the main logic to `URDNA2015._canonicalize(self, dataset: Dataset)` while keeping input and output in `URDNA2015.main()`. The method `URDNA2015._canonicalize(self, dataset: Dataset)` accepts an `rdflib.Dataset` and returns a tuple with
- the canonicalized result as a nquads `str` and
- the blank node identifier map as `dict`.
- the method `URDNA2015 .main(self, dataset: str | dict | Dataset, options)` now
- accepts a `rdflib.Dataset` object in addition to an nquads `str` or the original RDFJS-like`dict`.
- returns
- a `str`: the serialized nquads result, or
- a `dict`: the result as RDFJS-like dataset or the blank node identifier map when the new parameter `outputMap` is `True`.
- the hashing algorithm is now an class attribute `URDNA2015.hash_algorithm` so it configurable (required for RDFC1.0)
- the `permutations()` function now uses `itertools.permutations` instead of a custom implementation.
- replacements for rdflib's `_nq_row` and `_quoteLiteral` (these should eventually move to a fix for rdflib's nquads serializer).
- (re-)enabled all skipped URDNA2015, URDNA2012 tests in `tests/runtests.py`
- if the result of a test is a dict and the expected value is a string, the expected value is now parsed as JSON (needed for testing blank-node identifier maps).
- **BREAKING**: Migrated `jsonld.to_rdf()`, `jsonld.from_rdf()`, and N-Quads parsing/serialization to use `rdflib.Dataset` and RDFLib terms directly.
- `jsonld.to_rdf()` now returns an `rdflib.Dataset` by default when no output `format` is requested.
- Added `legacyMode` to `jsonld.to_rdf()` to return the previous RDF.js-like dataset `dict`.
- `jsonld.from_rdf()` now accepts both `rdflib.Dataset` inputs and the previous legacy dataset `dict` shape.
- Updated RDF-related tests for RDFLib dataset and N-Quads serialization behavior.
- **BREAKING**: nquads input/output now delegates to RDFLib instead of PyLD’s custom parser and serializer.
- Migrate `canon.py` to `rdflib`:
- Now use `rdflib` for RDF term type checking (e.g., checking is something is a bnode), looping over triples/quads, nquads serialization and constructing RDF terms (custom deepcopy is no longer needed)
- Move nquads parsing from`JsonLdProcessor.normalize()` to `URDNA2015.main()` so all parsing and serialization is handled by the same class.
- Move the main logic to `URDNA2015._canonicalize(self, dataset: Dataset)` while keeping input and output in `URDNA2015.main()`. The method `URDNA2015._canonicalize(self, dataset: Dataset)` accepts an `rdflib.Dataset` and returns a tuple with
- the canonicalized result as a nquads `str` and
- the blank node identifier map as `dict`.
- The method `URDNA2015 .main(self, dataset: str | dict | Dataset, options)` now
- accepts a `rdflib.Dataset` object in addition to an nquads `str` or the original RDFJS-like`dict`.
- returns
- a `str`: the serialized nquads result, or
- a `dict`: the result as RDFJS-like dataset or the blank node identifier map when the new parameter `outputMap` is `True`.
- The hashing algorithm is now an class attribute `URDNA2015.hash_algorithm` so it configurable (required for RDFC1.0)
- The `permutations()` function now uses `itertools.permutations` instead of a custom implementation.
- Replacements for rdflib's `_nq_row` and `_quoteLiteral` (these should eventually move to a fix for rdflib's nquads serializer).
- Re-enabled all skipped URDNA2015, URDNA2012 tests in `tests/runtests.py`
- If the result of a test is a dict and the expected value is a string, the expected value is now parsed as JSON (needed for testing blank-node identifier maps).
- **BREAKING**: Removed small Python 2-era compatibility code and simplified a few internal collection checks.

## 3.3.0 - unreleased

Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ memory footprint in order to operate.
## Requirements

* Python (3.10 or later)
* rdflib (7.3 or later)
* [Requests](http://docs.python-requests.org/) (optional)
* [aiohttp](https://aiohttp.readthedocs.io/) (optional)

Expand Down Expand Up @@ -128,6 +129,15 @@ normalized = jsonld.normalize(
# that can be used for hashing, comparison, etc.
```

`jsonld.to_rdf()` returns an `rdflib.Dataset` when no output `format` is
requested. To keep the RDF.js-like dataset `dict` used by PyLD versions lower
than 4, pass
`{'legacyMode': True}`:

```python
dataset = jsonld.to_rdf(doc, {'legacyMode': True})
```

## Features & conformance

This library aims to conform with the following W3C Recommendations:
Expand Down Expand Up @@ -421,4 +431,4 @@ See [`LICENSE`](./LICENSE) file for details.
The PyLD library is maintained by [Miel Vander
Sande](https://github.com/mielvds), [Anatoly
Scherbakov](https://github.com/anatoly-scherbakov) and [Digital
Bazaar](https://github.com/digitalbazaar) (Original authors of `PyLD`).
Bazaar](https://github.com/digitalbazaar) (Original authors of `PyLD`).
4 changes: 4 additions & 0 deletions docs/reference/to_rdf.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
show_bases: false
heading_level: 3

When `format` is not set, `jsonld.to_rdf()` returns an
[`rdflib.Dataset`](https://rdflib.readthedocs.io/). Set `legacyMode` to `True`
to return the RDF.js-like dataset `dict` used by PyLD versions lower than 4.

## Example

{{ example('to_rdf.py') }}
14 changes: 7 additions & 7 deletions lib/pyld/iri_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ def remove_dot_segments_of_path(iri: str, colon_position: int) -> str:


def resolve(relative_iri: str, base_iri: str = None) -> str:
# """
# Resolves a given relative IRI to an absolute IRI.
"""
Resolves a given relative IRI to an absolute IRI.

# :param base_iri: the base IRI.
# :param relative_iri: the relative IRI.
:param base_iri: the base IRI.
:param relative_iri: the relative IRI.

# :return: the absolute IRI.
# """
:return: the absolute IRI.
"""
base_iri = base_iri or ''
base_fragment_pos = base_iri.find("#")

Expand Down Expand Up @@ -293,7 +293,7 @@ def unresolve(absolute_iri: str, base_iri: str = ""):
rval = './' + rval

# build relative IRI using urlunparse with empty scheme/netloc
return urlunparse(('', '', rval, '', rel.query or '', rel.fragment or '')) or './'
return urlunparse(('', '', rval, '', rel.query, rel.fragment)) or './'


def parse_authority(parsed_iri: ParseResult) -> str:
Expand Down
Loading
Loading