Skip to main content

A declarative Object-RDF Mapper for Python — map Python classes to RDF graphs using decorators.

Project description

rdfmapper

rdfmapper is a declarative Object-RDF Mapper for Python. It lets you map Python classes to RDF graphs using decorators, inspired by ORM frameworks such as JPA and SQLAlchemy, without requiring you to write SPARQL or manipulate triples manually.

Tests PyPI License: MIT Python


Features

  • Declarative mapping of Python classes to RDF types and predicates via decorators
  • Support for one-to-one and one-to-many relationships
  • Automatic serialization and deserialization between Python objects and RDF graphs
  • Dynamic query repository (find_by_*, count_by_*, group_by_count) powered by SPARQL
  • Automatic SHACL shape generation and validation from class metadata
  • Circular reference detection during serialization and deserialization
  • Type-aware literal conversion (int, float, bool, date, datetime)

Installation

pip install rdfmapper-py

Or install from source:

git clone https://github.com/lambdageo/rdfmapper.git
cd rdfmapper
pip install -e ".[dev]"

Quick start

from rdflib import Namespace
from rdfmapper import RDFMapper, RDFRepository

EX = Namespace("http://example.org/")
FOAF = Namespace("http://xmlns.com/foaf/0.1/")

mapper = RDFMapper()


@mapper.rdf_entity(EX.Person)
class Person:
    def __init__(self, uri, name: str, age: int = None):
        self.uri = uri
        self._name = name
        self._age = age

    @mapper.rdf_property(FOAF.name, minCount=1)
    def name(self): pass

    @mapper.rdf_property(FOAF.age)
    def age(self): pass


# Serialize to RDF
person = Person(uri=EX["person/1"], name="Felipe", age=25)
graph = mapper.to_rdf(person)
print(graph.serialize(format="turtle"))

# Deserialize back to Python
restored = mapper.from_rdf(graph, Person, str(EX["person/1"]))
print(restored.name)  # Felipe

# Query with repository
repo = RDFRepository(mapper, graph, Person)
results = repo.find_by_name(name="Felipe")
count = repo.count_by_name(name="Felipe")

Relationships

@mapper.rdf_entity(EX.Address)
class Address:
    def __init__(self, uri, city: str):
        self.uri = uri
        self._city = city

    @mapper.rdf_property(EX.city)
    def city(self): pass


@mapper.rdf_entity(EX.Person)
class Person:
    def __init__(self, uri, name: str, address=None, phones=None):
        self.uri = uri
        self._name = name
        self._address = address
        self._phones = phones or []

    @mapper.rdf_property(FOAF.name)
    def name(self): pass

    @mapper.rdf_one_to_one(EX.address, target_class=lambda: Address)
    def address(self): pass

    @mapper.rdf_one_to_many(EX.phone, target_class=lambda: Phone)
    def phones(self): pass

SHACL validation

# Auto-generate SHACL shape from class metadata
shacl_graph = mapper.to_shacl(Person)

# Validate an RDF graph
conforms, _, report = mapper.validate(graph, entity_class=Person)
print("Conforms:", conforms)
print(report)

Dynamic repository queries

repo = RDFRepository(mapper, graph, Person)

# Exact match
repo.find_by_name(name="Felipe")

# Regex match
repo.find_by_name_like(name="Fel")

# Compound filter
repo.find_by_name_and_age(name="Felipe", age=25)

# Pagination
repo.find_by_name(name="Felipe", limit=10, offset=0)

# Count
repo.count_by_name(name="Felipe")

# Aggregation
repo.group_by_count(Person, "name", order="DESC")

Examples

See the examples/ directory for complete runnable scripts:


Development

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Type checking
mypy src/

Citation

If you use rdfmapper in your research, please cite:

@software{goiabeira2025pyrdm,
  author    = {Goiabeira, Felipe dos Santos and Costa, Sergio Souza},
  title     = {rdfmapper: A Declarative Object-RDF Mapper for Python},
  year      = {2025},
  publisher = {GitHub},
  url       = {https://github.com/lambdageo/rdfmapper}
}

License

MIT — see LICENSE.


Acknowledgements

rdfmapper was originally developed as part of a Bachelor's thesis at the Federal University of Maranhão (UFMA) by Felipe dos Santos Goiabeira, advised by Prof. Dr. Sergio Souza Costa, LambdaGeo Research Group.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rdfmapper-0.2.0.tar.gz (13.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rdfmapper-0.2.0-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

File details

Details for the file rdfmapper-0.2.0.tar.gz.

File metadata

  • Download URL: rdfmapper-0.2.0.tar.gz
  • Upload date:
  • Size: 13.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for rdfmapper-0.2.0.tar.gz
Algorithm Hash digest
SHA256 65351019d8793735dcc26f6edf918022bbf536740a78ccb2481a50d344d62cb5
MD5 6ae90e1654039d6e768c33e7d0c0bedc
BLAKE2b-256 a4c026482b44f9dc87ec85c1b6aac49a991822f376b760721c63c82285af51b9

See more details on using hashes here.

File details

Details for the file rdfmapper-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: rdfmapper-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 9.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for rdfmapper-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f88bcf7791dd521cb6eac884d265b9d78f0c9862e144f81800ccfc923a793103
MD5 f5420bad5aaeae36d034e61bb84a8e04
BLAKE2b-256 bd1d778ba4822db7db8692389bb2465e9ed8686bec831b7e33e8d314fbd6c99a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page