python-threatexchange/threatexchange/cli/dataclass_json.py (46 lines of code) (raw):

# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import json import pathlib import dacite import dataclasses from enum import Enum import typing as t T = t.TypeVar("T") def dataclass_dump_file(path: pathlib.Path, obj) -> None: with path.open("w") as fp: dataclass_dump(fp, obj) def _as_dict(obj: t.Any) -> t.Dict[str, t.Any]: json_dict = dataclasses.asdict(obj) # Sanity check - we want to make sure it will also come out the other end # And this will wrong-type error if it can't obj_sanity_check = dataclass_load_dict(json_dict, obj.__class__) assert obj == obj_sanity_check, "object changed during serialization?" return json_dict def dataclass_dump(fp: t.IO[str], obj) -> None: json_dict = _as_dict(obj) return json.dump(json_dict, fp, indent=2, default=_json_set_default) def dataclass_dumps(fp: t.IO[str], obj) -> str: json_dict = _as_dict(obj) return json.dumps(json_dict, indent=2, default=_json_set_default) def dataclass_load_file( path: pathlib.Path, cls: t.Type[T], *, default: t.Optional[T] = None ) -> T: if not path.is_file(): if default is not None: return default raise ValueError(f"cannot load dataclass: no such file {path}") with path.open("r") as fp: return dataclass_load(fp, cls) def dataclass_load(fp: t.IO[str], cls: t.Type[T]) -> T: json_dict = json.load(fp) return dataclass_load_dict(json_dict, cls) def dataclass_loads(s: str, cls: t.Type[T]) -> T: json_dict = json.loads(s) return dataclass_load_dict(json_dict, cls) def dataclass_load_dict(json_dict: t.Dict[str, t.Any], cls: t.Type[T]) -> T: return dacite.from_dict( data_class=cls, data=json_dict, config=dacite.Config(cast=[Enum, set]), ) def _json_set_default(obj): if isinstance(obj, set): return list(obj) raise TypeError