Source code for flask_jsonrpc.encoders

# Copyright (c) 2024-2025, Cenobit Technologies, Inc. http://cenobit.es/
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
# * Neither the name of the Cenobit Technologies nor the names of
#    its contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations

from enum import Enum
from types import GeneratorType
import typing as t
import inspect
from pathlib import PurePath
from collections import deque
import dataclasses

from typing_extensions import Buffer

from flask import typing as ft, jsonify as _jsonify

from pydantic.main import BaseModel


[docs] def serializable(obj: t.Any) -> t.Any: # noqa: ANN401, C901 """Serialize an object to a JSON-serializable format. Args: obj (typing.Any): The object to serialize. Returns: typing.Any: The JSON-serializable representation of the object. Examples: >>> serializable(b'hello') 'hello' >>> >>> serializable(bytearray(b'world')) 'world' >>> >>> from enum import Enum >>> class Color(Enum): ... RED = 'red' ... GREEN = 'green' >>> serializable(Color.RED) 'red' >>> >>> serializable({'key': b'value', 'number': 42}) {'key': 'value', 'number': 42} >>> >>> serializable([b'item1', b'item2']) ['item1', 'item2'] >>> >>> from dataclasses import dataclass >>> @dataclass ... class Person: ... name: str ... age: int >>> person = Person(name='Alice', age=30) >>> serializable(person) {'name': 'Alice', 'age': 30} >>> >>> from pydantic import BaseModel >>> class User(BaseModel): ... id: int ... username: str >>> user = User(id=1, username='bob') >>> serializable(user) {'id': 1, 'username': 'bob'} """ if isinstance(obj, bytes | bytearray): return obj.decode('utf-8') if isinstance(obj, Buffer): return bytes(obj).decode('utf-8') if isinstance(obj, Enum): return obj.value if isinstance(obj, PurePath): return str(obj) if isinstance(obj, dict): encoded_dict = {} for key, value in obj.items(): encoded_key = serializable(key) encoded_value = serializable(value) encoded_dict[encoded_key] = encoded_value return encoded_dict if isinstance(obj, list | set | frozenset | GeneratorType | tuple | deque): encoded_list = [] for item in obj: encoded_list.append(serializable(item)) return encoded_list if dataclasses.is_dataclass(obj): obj_dict = dataclasses.asdict(obj) # type: ignore return serializable(obj_dict) if isinstance(obj, BaseModel): return serializable(obj.model_dump(exclude_none=True, by_alias=True)) if not inspect.isbuiltin(obj) and getattr(obj, '__module__', '') != 'builtins' and hasattr(obj, '__dict__'): return obj.__dict__ return obj
[docs] def jsonify(obj: t.Any) -> ft.ResponseValue: # noqa: ANN401 """Convert an object to a JSON response. Args: obj (typing.Any): The object to convert. Returns: flask.typing.ResponseValue: The JSON response. See Also: :func:`flask_jsonrpc.encoders.serializable` """ return _jsonify(serializable(obj))