Source code for pyfcstm.utils.json

"""
JSON/YAML serialization utilities for persistent storage.

This module provides a lightweight interface for serializing and deserializing
objects to and from JSON and YAML formats. It defines a base class,
:class:`IJsonOp`, that supplies file I/O helpers and a consistent contract for
converting objects to JSON-serializable structures.

The module contains the following main components:

* :class:`IJsonOp` - Base interface for JSON/YAML serialization operations

Example::

    >>> class MyData(IJsonOp):
    ...     def __init__(self, data):
    ...         self.data = data
    ...
    ...     def _to_json(self):
    ...         return {"data": self.data}
    ...
    ...     @classmethod
    ...     def _from_json(cls, data):
    ...         return cls(data["data"])
    ...
    >>> obj = MyData([1, 2, 3])
    >>> obj.to_json("example.json")
    >>> loaded = MyData.read_json("example.json")
    >>> loaded.data
    [1, 2, 3]

.. note::
   The serialization logic is defined by subclasses via :meth:`_to_json` and
   :meth:`_from_json`. The base class only handles file I/O and validation.

"""

import json
from pprint import pformat
from typing import Any, Dict, Type, TypeVar

import yaml

T = TypeVar("T", bound="IJsonOp")


[docs] class IJsonOp: """ An interface class that provides JSON serialization/deserialization capabilities. This class defines a common interface for objects that need to be serialized to and deserialized from JSON/YAML formats. Concrete classes must implement the :meth:`_to_json` and :meth:`_from_json` methods. Example:: >>> class MyData(IJsonOp): ... def __init__(self, data): ... self.data = data ... ... def _to_json(self): ... return {"data": self.data} ... ... @classmethod ... def _from_json(cls, data): ... return cls(data["data"]) """ def _to_json(self) -> Dict[str, Any]: """ Convert the object to a JSON-serializable format. :return: JSON-serializable representation of the object :rtype: dict :raises NotImplementedError: This method must be implemented by concrete classes. """ raise NotImplementedError @classmethod def _from_json(cls: Type[T], data: Dict[str, Any]) -> T: """ Create an instance of the class from JSON-formatted data. :param data: The JSON data to deserialize :type data: dict :return: An instance constructed from the provided JSON data :rtype: IJsonOp :raises NotImplementedError: This method must be implemented by concrete classes. """ raise NotImplementedError @property def json(self) -> Dict[str, Any]: """ Get the JSON representation of the object. :return: JSON-serializable representation of the object :rtype: dict """ return self._to_json()
[docs] def to_json(self, json_file: str) -> None: """ Save the object to a JSON file. :param json_file: Path to the output JSON file :type json_file: str """ data = self._to_json() with open(json_file, 'w') as f: json.dump(data, f, indent=4, ensure_ascii=False)
[docs] def to_yaml(self, yaml_file: str) -> None: """ Save the object to a YAML file. :param yaml_file: Path to the output YAML file :type yaml_file: str """ data = self._to_json() with open(yaml_file, 'w') as f: yaml.safe_dump(data, f)
[docs] @classmethod def from_json(cls: Type[T], data: Dict[str, Any]) -> T: """ Create an instance from JSON data. :param data: JSON-formatted data :type data: dict :return: An instance of the class :rtype: IJsonOp :raises TypeError: If the created object is not an instance of the class """ obj = cls._from_json(data) if not isinstance(obj, cls): raise TypeError(f'{cls!r} type expected, but {type(obj)!r} found in data:\n' f'{pformat(data)}') return obj
[docs] @classmethod def read_json(cls: Type[T], json_file: str) -> T: """ Create an instance by reading from a JSON file. :param json_file: Path to the input JSON file :type json_file: str :return: An instance of the class :rtype: IJsonOp """ with open(json_file, 'r') as f: return cls.from_json(json.load(f))
[docs] @classmethod def read_yaml(cls: Type[T], yaml_file: str) -> T: """ Create an instance by reading from a YAML file. :param yaml_file: Path to the input YAML file :type yaml_file: str :return: An instance of the class :rtype: IJsonOp """ with open(yaml_file, 'r') as f: return cls.from_json(yaml.safe_load(f))