Skip to content

ID Properties

Basic IDs

Just like value objects, ID objects are supported via a custom class as well.

from pydantic_jsonld.compact import BaseModel, Id, Value


class DatasetModel(BaseModel):
    title: Value[str]
    measured_property: Value[str]
    instrument: Id
    license: Id


data = {
    "@context": {"@vocab": "http://www.example.org/"},
    "title": "Atmospheric CO2 Measurements 2025",
    "measuredProperty": "CO2 concentration",
    "instrument": {"@id": "http://www.example.org/instrument/mauna-loa-sensor"},
    "license": {"@id": "http://www.example.org/licenses/by/4.0/"},
}

model = DatasetModel.model_validate(data)

assert model.title.value == "Atmospheric CO2 Measurements 2025"
assert model.measured_property.value == "CO2 concentration"
assert model.instrument.id == "http://www.example.org/instrument/mauna-loa-sensor"
assert model.license.id == "http://www.example.org/licenses/by/4.0/"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data
from pydantic_jsonld.compact import BaseModel, Id, Value


class SoftwarePackageModel(BaseModel):
    name: Value[str]
    version: Value[str]
    homepage: Id
    dependency: Id


data = {
    "@context": {"@vocab": "http://www.example.org/"},
    "name": "jsonld-compact",
    "version": "2.3.0",
    "homepage": {"@id": "http://www.example.org/example/jsonld-compact"},
    "dependency": {"@id": "http://www.example.org/project/pydantic"},
}

model = SoftwarePackageModel.model_validate(data)

assert model.name.value == "jsonld-compact"
assert model.version.value == "2.3.0"
assert model.homepage.id == "http://www.example.org/example/jsonld-compact"
assert model.dependency.id == "http://www.example.org/project/pydantic"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data
from pydantic_jsonld.compact import BaseModel, Id, Value


class ExhibitModel(BaseModel):
    title: Value[str]
    creator: Id
    location_found: Id
    material: Value[str]


data = {
    "@context": {"@vocab": "http://www.example.org/"},
    "title": "Bronze Age Ritual Blade",
    "creator": {"@id": "http://www.example.org/culture/unknown-bronze-age-smith"},
    "locationFound": {"@id": "http://www.example.org/place/danube-valley"},
    "material": "Bronze",
}

model = ExhibitModel.model_validate(data)

assert model.title.value == "Bronze Age Ritual Blade"
assert model.creator.id == "http://www.example.org/culture/unknown-bronze-age-smith"
assert model.location_found.id == "http://www.example.org/place/danube-valley"
assert model.material.value == "Bronze"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data

Complex IDs

Additional information provided in ID objects is captured as well.

from pydantic_jsonld.compact import BaseModel, Id, Value


class DatasetModel(BaseModel):
    title: Value[str]
    measured_property: Value[str]
    instrument: Id
    license: Id


data = {
    "@context": {"@vocab": "http://www.example.org/"},
    "title": "Atmospheric CO2 Measurements 2025",
    "measuredProperty": "CO2 concentration",
    "instrument": {
        "@id": "http://www.example.org/instrument/mauna-loa-sensor",
        "@type": "http://www.example.org/Sensor",
    },
    "license": {
        "@id": "http://www.example.org/licenses/by/4.0/",
        "@type": "http://www.example.org/CreativeWorkLicense",
    },
}

model = DatasetModel.model_validate(data)

assert model.title.value == "Atmospheric CO2 Measurements 2025"
assert model.measured_property.value == "CO2 concentration"
assert model.instrument.id == "http://www.example.org/instrument/mauna-loa-sensor"
assert model.instrument.type == "http://www.example.org/Sensor"
assert model.license.id == "http://www.example.org/licenses/by/4.0/"
assert model.license.type == "http://www.example.org/CreativeWorkLicense"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data
from pydantic_jsonld.compact import BaseModel, Id, Value


class SoftwarePackageModel(BaseModel):
    name: Value[str]
    version: Value[str]
    homepage: Id
    dependency: Id


data = {
    "@context": {"@vocab": "http://www.example.org/"},
    "name": "jsonld-compact",
    "version": "2.3.0",
    "homepage": {
        "@id": "http://www.example.org/example/jsonld-compact",
        "@type": "http://www.example.org/WebPage",
    },
    "dependency": {
        "@id": "http://www.example.org/project/pydantic",
        "@type": "http://www.example.org/SoftwareDependency",
    },
}

model = SoftwarePackageModel.model_validate(data)

assert model.name.value == "jsonld-compact"
assert model.version.value == "2.3.0"
assert model.homepage.id == "http://www.example.org/example/jsonld-compact"
assert model.homepage.type == "http://www.example.org/WebPage"
assert model.dependency.id == "http://www.example.org/project/pydantic"
assert model.dependency.type == "http://www.example.org/SoftwareDependency"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data
from pydantic_jsonld.compact import BaseModel, Id, Value


class ExhibitModel(BaseModel):
    title: Value[str]
    creator: Id
    location_found: Id
    material: Value[str]


data = {
    "@context": {"@vocab": "http://www.example.org/"},
    "title": "Bronze Age Ritual Blade",
    "creator": {
        "@id": "http://www.example.org/culture/unknown-bronze-age-smith",
        "@type": "http://www.example.org/CulturalGroup",
    },
    "locationFound": {
        "@id": "http://www.example.org/place/danube-valley",
        "@type": "http://www.example.org/ArchaeologicalSite",
    },
    "material": "Bronze",
}

model = ExhibitModel.model_validate(data)

assert model.title.value == "Bronze Age Ritual Blade"
assert model.creator.id == "http://www.example.org/culture/unknown-bronze-age-smith"
assert model.creator.type == "http://www.example.org/CulturalGroup"
assert model.location_found.id == "http://www.example.org/place/danube-valley"
assert model.location_found.type == "http://www.example.org/ArchaeologicalSite"
assert model.material.value == "Bronze"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data

ID Keywords

The keywords captured are @type and @index.

Plain IDs

Conversely, if you know that some property will always have plain scalar IDs, you may also simplify the property's class declaration.

from pydantic_jsonld.compact import BaseModel, Id, Value


class DatasetModel(BaseModel):
    title: Value[str]
    measured_property: Value[str]
    instrument: str
    license: str


data = {
    "@context": {
        "@vocab": "http://www.example.org/",
        "instrument": {"@type": "@id"},
        "license": {"@type": "@id"},
    },
    "title": "Atmospheric CO2 Measurements 2025",
    "measuredProperty": "CO2 concentration",
    "instrument": "http://www.example.org/instrument/mauna-loa-sensor",
    "license": "http://www.example.org/licenses/by/4.0/",
}

model = DatasetModel.model_validate(data)

assert model.title.value == "Atmospheric CO2 Measurements 2025"
assert model.measured_property.value == "CO2 concentration"
assert model.instrument == "http://www.example.org/instrument/mauna-loa-sensor"
assert model.license == "http://www.example.org/licenses/by/4.0/"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data
from pydantic_jsonld.compact import BaseModel, Id, Value


class SoftwarePackageModel(BaseModel):
    name: Value[str]
    version: Value[str]
    homepage: str
    dependency: str


data = {
    "@context": {
        "@vocab": "http://www.example.org/",
        "homepage": {"@type": "@id"},
        "dependency": {"@type": "@id"},
    },
    "name": "jsonld-compact",
    "version": "2.3.0",
    "homepage": "http://www.example.org/example/jsonld-compact",
    "dependency": "http://www.example.org/project/pydantic",
}

model = SoftwarePackageModel.model_validate(data)

assert model.name.value == "jsonld-compact"
assert model.version.value == "2.3.0"
assert model.homepage == "http://www.example.org/example/jsonld-compact"
assert model.dependency == "http://www.example.org/project/pydantic"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data
from pydantic_jsonld.compact import BaseModel, Id, Value


class ExhibitModel(BaseModel):
    title: Value[str]
    creator: str
    location_found: str
    material: Value[str]


data = {
    "@context": {
        "@vocab": "http://www.example.org/",
        "creator": {"@type": "@id"},
        "locationFound": {"@type": "@id"},
    },
    "title": "Bronze Age Ritual Blade",
    "creator": "http://www.example.org/culture/unknown-bronze-age-smith",
    "locationFound": "http://www.example.org/place/danube-valley",
    "material": "Bronze",
}

model = ExhibitModel.model_validate(data)

assert model.title.value == "Bronze Age Ritual Blade"
assert model.creator == "http://www.example.org/culture/unknown-bronze-age-smith"
assert model.location_found == "http://www.example.org/place/danube-valley"
assert model.material.value == "Bronze"

dumped = model.model_dump(by_alias=True, exclude_none=True)
assert dumped == data

Validation Failure

If a particular data entry happens to have an ID object which is not a plain scalar value, the validation will fail for that instance.

Limitations

As the context is not parsed or resolved in any way, there is no way to safely determine whether any given ID object can be collapsed to a plain scalar.

This implementation does not collapse in such instances by default. However, when constructing a model from data, the initial format is remembered.

The difference is illustrated in the following examples

from pydantic_jsonld.compact import BaseModel, Id


class ExampleModel(BaseModel):
    id: Id

data = {
    "@context": {
        "@vocab": "http://www.example.org/",
        "id": {"@type": "@id"},
    },
    "id": "http://www.example.org/1",
}

model = ExampleModel.model_validate(data)
dumped = model.model_dump(by_alias=True, exclude_none=True)

# Keeps input format (correct compact form)
assert dumped["id"] == "http://www.example.org/1"
from pydantic_jsonld.compact import BaseModel, Id


class ExampleModel(BaseModel):
    id: Id

data = {
    "@context": {
        "@vocab": "http://www.example.org/",
        "id": {"@type": "@id"},
    },
    "id": {
        "@id": "http://www.example.org/1",
        "@type": "http://www.example.org/UniqueIdentifier",
    },
}

model = ExampleModel.model_validate(data)
model.id = Id(id="http://www.example.org/2")
dumped = model.model_dump(by_alias=True, exclude_none=True)

# Keeps input format (incorrect compact form)
# Should be just "http://www.example.org/2"
assert dumped["id"] == {"@id": "http://www.example.org/2"}

For such edge cases and also especially when manually creating instances instead of validating from data, you can manually change the behavior.

from pydantic_jsonld.compact import BaseModel, Id


class ExampleModel(BaseModel):
    id: Id

data = {
    "@context": {
        "@vocab": "http://www.example.org/",
        "id": {"@type": "@id"},
    },
    "id": {
        "@id": "http://www.example.org/1",
        "@type": "http://www.example.org/UniqueIdentifier",
    },
}

model = ExampleModel.model_validate(data)
model.id = Id(id="http://www.example.org/2")
model.id.compact = True
dumped = model.model_dump(by_alias=True, exclude_none=True)

assert dumped["id"] == "http://www.example.org/2"
from pydantic_jsonld.compact import BaseModel, Id


class ExampleModel(BaseModel):
    id: Id

data = {
    "@context": {
        "@vocab": "http://www.example.org/",
        "id": {"@type": "@id"},
    },
    "id": {
        "@id": "http://www.example.org/1",
        "@type": "http://www.example.org/UniqueIdentifier",
    },
}

model = ExampleModel.model_validate(data)
model.id = Id(id="http://www.example.org/2", compact=True)
dumped = model.model_dump(by_alias=True, exclude_none=True)

assert dumped["id"] == "http://www.example.org/2"