From a Python dataclass¶
You already model data as Python dataclasses or pydantic models. OO-LD round-trips with them, so the schema and the code stay in sync:
- Schema to code: generate pydantic models from an OO-LD schema with datamodel-code-generator; the
@contextandx-oold-*keywords ride along injson_schema_extra. - Code to schema: annotate a model's
model_configwith@context/x-oold-iriand emit OO-LD via pydantic'smodel_json_schema().
Keep property names identifier-safe (^[A-Za-z_][A-Za-z0-9_]*$) and put the semantics in @context (alias name to schema:name), so you never need an IRI as a Python attribute name.
1. Plain pydantic¶
from pydantic import BaseModel, ConfigDict
class Person(BaseModel):
model_config = ConfigDict(json_schema_extra={
"@context": {"schema": "https://schema.org/", "name": "schema:name"},
"x-oold-iri": "schema:Person",
})
name: str | None = None
model_json_schema() emits a JSON Schema carrying that @context and x-oold-iri - a valid OO-LD schema, with no dependency beyond pydantic.
2. Switch to oold-python's LinkedBaseModel¶
The same model, with the base class swapped, becomes OO-LD-aware at runtime - resolving linked references by IRI, carrying @id identity, and serialising to JSON-LD:
from pydantic import ConfigDict
from oold.model import LinkedBaseModel
class Person(LinkedBaseModel):
model_config = ConfigDict(json_schema_extra={
"@context": {"schema": "https://schema.org/", "name": "schema:name"},
"x-oold-iri": "schema:Person",
})
name: str | None = None
See the oold library (PyPI oold) for the additional features - reference resolution, @id handling, controllers, and JSON-LD import/export.