"""Custom JSON encoding functionality.""" import re from datetime import date, datetime from flask.json.provider import DefaultJSONProvider from typing import Any class CustomJSONProvider(DefaultJSONProvider): def default(self, o): if isinstance(o, (date, datetime)): return o.isoformat() return super().default(o) def str_to_date(value: Any) -> Any: """Recursive str to date. Args: value (Any): Any value. Returns: Any: date or original value. """ if isinstance(value, list): return [str_to_date(i) for i in value] if isinstance(value, str) and re.match(r"^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\.*\d*$", value): return datetime.fromisoformat(value) if isinstance(value, str) and re.match(r"^\d{4}-\d{2}-\d{2}$", value): return datetime.strptime(value, "%Y-%m-%d").date() return value def date_hook(json_dict: dict) -> dict: """Deserialize dates for json loads. Args: json_dict (dict): Json keys and values. Returns: dict: Formatter json. """ for key, value in json_dict.items(): json_dict[key] = str_to_date(value) return json_dict def serialize_value(v, datefmt=None): """Convert arbitrary value to a JSON-serializable value. Conversion rules are: * Objects of types int, float, bool, str and None object returned unmodified. * Date and datetime objects are converted to strings according to format string passed with datefmt argument, by default ISO format is used. * If a list is passed the function is recursively called for each of its elements and a new list is returned. * If a dictionary is passed the function is recursively called for each of its keys and values, and a new dictionary is returned. Keys of the resulting dictionary converted to strings. * Objects of other types converted to their string representation. Args: v: Value of an arbitrary data type. datefmt (str): Optional format string for date and datetime objects. By default ISO 8601 is used. Returns: A JSON-serializable value. """ if isinstance(v, (int, float, bool, str, type(None))): return v if isinstance(v, (date, datetime)): if not datefmt: return v.isoformat() return v.strftime(datefmt) if isinstance(v, list): return [serialize_value(i, datefmt) for i in v] if isinstance(v, dict): return {str(serialize_value(key, datefmt)): serialize_value(value, datefmt) for key, value in v.items()} return str(v)