File size: 2,820 Bytes
36f6eec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""
Fast JSON serialization/deserialization utilities using orjson.

This module provides drop-in replacements for standard json functions
with significant performance improvements.
"""

import orjson
from typing import Any, Union, Optional
from datetime import datetime, date
from decimal import Decimal
from uuid import UUID
from pydantic import BaseModel


def default(obj: Any) -> Any:
    """
    Custom serializer for orjson to handle special types.
    """
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    elif isinstance(obj, UUID):
        return str(obj)
    elif isinstance(obj, Decimal):
        return float(obj)
    elif isinstance(obj, BaseModel):
        return obj.model_dump()
    elif hasattr(obj, "__dict__"):
        return obj.__dict__
    raise TypeError(f"Type {type(obj)} not serializable")


def dumps(obj: Any, *, indent: bool = False) -> str:
    """
    Serialize obj to a JSON formatted string using orjson.
    
    Args:
        obj: Object to serialize
        indent: Whether to indent the output (slower but prettier)
    
    Returns:
        JSON string
    """
    options = orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY
    if indent:
        options |= orjson.OPT_INDENT_2
    
    return orjson.dumps(obj, default=default, option=options).decode("utf-8")


def loads(s: Union[str, bytes]) -> Any:
    """
    Deserialize s (a str or bytes containing JSON) to a Python object.
    
    Args:
        s: JSON string or bytes to deserialize
    
    Returns:
        Python object
    """
    if isinstance(s, str):
        s = s.encode("utf-8")
    return orjson.loads(s)


def dumps_bytes(obj: Any, *, indent: bool = False) -> bytes:
    """
    Serialize obj to JSON bytes (faster than dumps when you need bytes).
    
    Args:
        obj: Object to serialize
        indent: Whether to indent the output
    
    Returns:
        JSON bytes
    """
    options = orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY
    if indent:
        options |= orjson.OPT_INDENT_2
    
    return orjson.dumps(obj, default=default, option=options)


# FastAPI response helper
def jsonable_encoder(obj: Any, *, exclude_unset: bool = False, exclude_none: bool = False) -> Any:
    """
    Convert a Python object to a JSON-compatible format.
    
    Args:
        obj: Object to convert
        exclude_unset: Exclude unset fields from Pydantic models
        exclude_none: Exclude None values
    
    Returns:
        JSON-compatible Python object
    """
    if isinstance(obj, BaseModel):
        return obj.model_dump(exclude_unset=exclude_unset, exclude_none=exclude_none)
    
    # For other objects, serialize and deserialize to ensure compatibility
    return loads(dumps(obj))


# Aliases for drop-in replacement
encode = dumps
decode = loads