Skip to content

to_jsonld() does not compose nested / scoped @context of LinkedBaseModel fields #94

Description

@simontaurus

Summary

to_jsonld() does not compose nested / scoped @contexts. When a model field is
itself a LinkedBaseModel (or a list of them), the field's own context (and the
context of its fields) is not folded into the exported document. As a result a
container model cannot inherit the value/unit/type mapping from the nested models
it holds, and the only way to get non-empty JSON-LD is to duplicate every nested
mapping, flat, on the top-level model.

Discovered while adding JSON-LD export to opensemantic.batteries: a
TabularData / Dataset whose rows are typed QuantityValue characteristics
(Voltage, ElectricCurrent, Time, ...). The intended output maps rows to
Property:HasRow and each row characteristic to Property:HasCharacteristic,
with value / unit / type coming from each QuantityValue's own context
(Property:HasValue / Property:HasUnit / Property:HasType).

Tested with oold==0.16.2 (current PyPI latest).

Behaviour

pydantic v1

export_jsonld reads the context directly from
model_instance.__class__.__config__.schema_extra["@context"] (static.py ~430)
and build_context's v1 branch is a no-op (static.py ~410-412: pass). So only
the top model's own @context is used; nested models' contexts are never
composed. If the top context maps rows but not the row fields, rows expands
to empty objects; if it maps the row fields but not value/unit/type, each
characteristic expands to {}.

A JSON-LD scoped context that references the nested schema, e.g.

{ "voltage": { "@id": "Property:HasCharacteristic",
               "@context": "/wiki/Category:OSW4082...?action=raw&slot=jsonschema" } }

fails in jsonld.expand with invalid scoped context -> the document loader
(static.py ~302-323) only resolves refs that are in the top model's base-class
schemas; a nested field type (QuantityValue) is not a base class, so the ref
falls through to requests_document_loader and errors with
URL could not be dereferenced; only "http" and "https" URLs are supported
on the schemeless Category:OSW... ref (offline and online).

pydantic v2

build_context does recurse, but:

  1. It raises KeyError when a nested-model field is not already present in the
    base @context (static.py ~397: if target_context[field_name] is None).
    Example: a Dataset subclass whose context maps rows but not the inherited
    label field crashes on KeyError: 'label'.
  2. When it does compose a nested model's context containing a relative
    /wiki/Category:OSW...?action=raw&slot=jsonschema ref, expansion hits the
    same loader limitation as above and fails to dereference the schemeless
    Category:OSW... ref.

Minimal repro

from typing import List, Optional
from opensemantic.v1 import OswBaseModel
from opensemantic.base.v1 import Dataset
from opensemantic.core.v1 import Label
from opensemantic.characteristics.quantitative.v1 import (
    ElectricCurrent, TabularData, Time, Voltage,
)

class Row(OswBaseModel):
    class Config:
        schema_extra = {"@context": {"voltage": "Property:HasCharacteristic"}}
    test_time: Time
    voltage: Voltage
    current: ElectricCurrent

class DS(Dataset, TabularData):
    class Config:
        schema_extra = {"@context": {
            "id": "@id",
            "rows": {"@id": "Property:HasRow", "@container": "@set"},
        }}
    label: Optional[List[Label]] = None
    rows: List[Row]

d = DS(rows=[Row(test_time=Time(value=0.0), voltage=Voltage(value=3.7),
                 current=ElectricCurrent(value=0.5))])
print(d.to_jsonld())
# -> {'@id': ..., 'Property:HasRow': [{}]}  (row context on Row not composed)

Request

Support composing nested / scoped @contexts in to_jsonld(), so a container
can inherit the mapping of the models it embeds instead of duplicating it:

  • Register the schemas of nested field types (not only base classes) with the
    JSON-LD document loader, so relative /wiki/Category:OSW... refs from nested
    models resolve locally.
  • Make build_context skip (not KeyError) fields that are absent from the base
    context.
  • Provide the same composition for the pydantic v1 export path.

The branch panelini-oold-graph already carries related work that seems to
address parts of this:

  • feat: add context building for LinkedBaseModel in JSON-LD export
  • fix: break on context contruction if definition is not found
  • fix: context resolving for OSW schemas with non-UUID IRI

Merging and releasing these (or the relevant subset) would unblock downstream
JSON-LD export.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions