Skip to content
This repository was archived by the owner on May 3, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion examples/EmergencyManagement/Server/models_emergency.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from typing import Optional

from typing import Union, Optional

from reticulum_openapi.model import BaseModel
from sqlalchemy.orm import declarative_base
Expand Down Expand Up @@ -86,3 +87,29 @@ class Event(BaseModel):
detail: Optional[Detail] = None
point: Optional[Point] = None
__orm_model__ = EventORM


# --- Additional example models demonstrating allOf/oneOf/anyOf ---

@dataclass
class BaseVehicle(BaseModel):
manufacturer: str


@dataclass
class Car(BaseVehicle):
doors: int


@dataclass
class Bike(BaseModel):
handlebar: str


Vehicle = Union[Car, Bike]


@dataclass
class TransportRecord(BaseModel):
owner: str
vehicle: Vehicle
26 changes: 23 additions & 3 deletions reticulum_openapi/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from dataclasses import dataclass, asdict, is_dataclass, fields
import json
import zlib
from typing import Type, TypeVar
from typing import Type, TypeVar, get_origin, get_args, Union
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy import select

Expand Down Expand Up @@ -45,8 +45,28 @@ def dataclass_from_json(cls: Type[T], data: bytes) -> T:
json_bytes = data
json_str = json_bytes.decode('utf-8')
obj_dict = json.loads(json_str)
# Instantiate dataclass by unpacking dict (assumes keys match field names)
return cls(**obj_dict) # type: ignore

def _construct(tp, value):
origin = get_origin(tp)
if origin is Union:
for sub in get_args(tp):
try:
return _construct(sub, value)
except Exception:
continue
raise ValueError(f"No matching type for Union {tp}")
if is_dataclass(tp):
kwargs = {}
for f in fields(tp):
if isinstance(value, dict) and f.name in value:
kwargs[f.name] = _construct(f.type, value[f.name])
return tp(**kwargs) # type: ignore
if origin is list and isinstance(value, list):
item_type = get_args(tp)[0]
return [_construct(item_type, v) for v in value]
return value

return _construct(cls, obj_dict)


@dataclass
Expand Down
39 changes: 39 additions & 0 deletions tests/test_model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from typing import Union
from reticulum_openapi.model import dataclass_to_json, dataclass_from_json


Expand All @@ -13,3 +14,41 @@ def test_serialization_roundtrip():
data = dataclass_to_json(item)
obj = dataclass_from_json(Item, data)
assert obj == item


@dataclass
class BaseVehicle:
manufacturer: str


@dataclass
class Car(BaseVehicle):
doors: int


@dataclass
class Bike:
handlebar: str


Vehicle = Union[Car, Bike]


@dataclass
class TransportRecord:
owner: str
vehicle: Vehicle


def test_union_deserialization_root():
data = dataclass_to_json(Car(manufacturer="Acme", doors=2))
obj = dataclass_from_json(Vehicle, data)
assert isinstance(obj, Car)
assert obj.doors == 2


def test_union_deserialization_nested():
record = TransportRecord(owner="bob", vehicle=Bike(handlebar="drop"))
data = dataclass_to_json(record)
obj = dataclass_from_json(TransportRecord, data)
assert isinstance(obj.vehicle, Bike)