orjson_wrapper.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. from decimal import Decimal
  2. from typing import Any, Optional, Tuple
  3. import numpy as np
  4. import orjson
  5. from fastapi import Response
  6. ORJSON_OPTS = orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NON_STR_KEYS
  7. def dumps(obj: Any, sort_keys: bool = False, separators: Optional[Tuple[str, str]] = None):
  8. """Serializes a Python object to a JSON-encoded string.
  9. By default, this function supports serializing numpy arrays,
  10. which Python's json module does not.
  11. Uses package `orjson` internally.
  12. """
  13. # note that parameters `sort_keys` and `separators` are required by AsyncServer's
  14. # internal calls, which match Python's default `json.dumps` API.
  15. assert separators is None or separators == (',', ':'), \
  16. f'NiceGUI JSON serializer only supports Python''s default ' +\
  17. f'JSON separators "," and ":", but got {separators} instead.'
  18. opts = ORJSON_OPTS
  19. # flag for sorting by object keys
  20. if sort_keys:
  21. opts |= orjson.OPT_SORT_KEYS
  22. return orjson.dumps(obj, option=opts, default=_orjson_converter).decode('utf-8')
  23. def loads(value: str) -> Any:
  24. """Deserialize a JSON-encoded string to a corresponding Python object/value.
  25. Uses package `orjson` internally.
  26. """
  27. return orjson.loads(value)
  28. def _orjson_converter(obj):
  29. """Custom serializer/converter, e.g. for numpy object arrays."""
  30. if isinstance(obj, np.ndarray) and obj.dtype == np.object_:
  31. return obj.tolist()
  32. if isinstance(obj, Decimal):
  33. return float(obj)
  34. class NiceGUIJSONResponse(Response):
  35. """FastAPI response class to support our custom json serializer implementation.
  36. Uses package `orjson` internally.
  37. """
  38. media_type = 'application/json'
  39. def render(self, content: Any) -> bytes:
  40. return orjson.dumps(content, option=ORJSON_OPTS, default=_orjson_converter)