chore: archive tools/ui_inspector snapshot before migration removal.
Keep the last in-template copy on main so history can restore it; product continues in jiangchang-ui-recorder. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
64
tools/ui_inspector/models/serialize.py
Normal file
64
tools/ui_inspector/models/serialize.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Stable JSON serialization helpers for v2 models."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import asdict, is_dataclass
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def _normalize(obj: Any) -> Any:
|
||||
if obj is None or isinstance(obj, (str, int, float, bool)):
|
||||
return obj
|
||||
if isinstance(obj, datetime):
|
||||
return obj.isoformat()
|
||||
if isinstance(obj, Path):
|
||||
return str(obj).replace("\\", "/")
|
||||
if is_dataclass(obj) and not isinstance(obj, type):
|
||||
return {k: _normalize(v) for k, v in asdict(obj).items()}
|
||||
if isinstance(obj, dict):
|
||||
return {str(k): _normalize(v) for k, v in obj.items()}
|
||||
if isinstance(obj, (list, tuple)):
|
||||
return [_normalize(x) for x in obj]
|
||||
return str(obj)
|
||||
|
||||
|
||||
def to_dict(obj: Any) -> dict[str, Any]:
|
||||
data = _normalize(obj)
|
||||
if not isinstance(data, dict):
|
||||
raise TypeError(f"expected dataclass/dict, got {type(obj)!r}")
|
||||
return data
|
||||
|
||||
|
||||
def from_dict(cls: type[T], data: dict[str, Any]) -> T:
|
||||
"""Best-effort dataclass reconstruction (ignores unknown keys)."""
|
||||
if not is_dataclass(cls):
|
||||
raise TypeError(f"{cls!r} is not a dataclass")
|
||||
import dataclasses
|
||||
|
||||
fields = {f.name for f in dataclasses.fields(cls)}
|
||||
kwargs = {k: v for k, v in (data or {}).items() if k in fields}
|
||||
return cls(**kwargs) # type: ignore[call-arg]
|
||||
|
||||
|
||||
def dumps_json(obj: Any, *, indent: int = 2) -> str:
|
||||
return json.dumps(
|
||||
to_dict(obj) if is_dataclass(obj) and not isinstance(obj, type) else _normalize(obj),
|
||||
ensure_ascii=False,
|
||||
indent=indent,
|
||||
sort_keys=False,
|
||||
) + "\n"
|
||||
|
||||
|
||||
def write_model_json(path: Path, obj: Any) -> None:
|
||||
path = Path(path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(dumps_json(obj), encoding="utf-8")
|
||||
|
||||
|
||||
def read_json(path: Path) -> Any:
|
||||
return json.loads(Path(path).read_text(encoding="utf-8"))
|
||||
Reference in New Issue
Block a user