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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions