Skip to content

Commit 56ab367

Browse files
committed
Add missing transformers
1 parent c7d1f99 commit 56ab367

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#!/usr/bin/env python3
2+
import dataclasses
3+
import functools
4+
import json
5+
from typing import Any
6+
from typing import Callable
7+
from typing import Dict
8+
from typing import Tuple
9+
from typing import Type
10+
from typing import TypeVar
11+
12+
from . import utils
13+
14+
T = TypeVar('T')
15+
16+
17+
def json_to_dict(cls: Type[T], json_value: str) -> Dict[str, Any]:
18+
"""
19+
Convert a JSON string to a dictionary.
20+
21+
Parameters
22+
----------
23+
json_value : str or dict
24+
The JSON string or dictionary representing the object.
25+
26+
Returns
27+
-------
28+
dict
29+
A dictionary with fields populated from the JSON.
30+
31+
"""
32+
return cls(json.loads(json_value)) # type: ignore
33+
34+
35+
def json_to_pydantic(cls: Type[T], json_value: str) -> T:
36+
"""
37+
Convert a JSON string to a Pydantic model instance.
38+
39+
Parameters
40+
----------
41+
cls : Type[T]
42+
The Pydantic model type to instantiate.
43+
json_value : str or dict
44+
The JSON string or dictionary representing the object.
45+
46+
Returns
47+
-------
48+
T
49+
An instance of the Pydantic model with fields populated from the JSON.
50+
51+
"""
52+
return cls(**json.loads(json_value))
53+
54+
55+
def json_to_namedtuple(cls: Type[T], json_value: str) -> T:
56+
"""
57+
Convert a JSON string to a namedtuple instance.
58+
59+
Parameters
60+
----------
61+
cls : Type[T]
62+
The namedtuple type to instantiate.
63+
json_value : str or dict
64+
The JSON string or dictionary representing the object.
65+
66+
Returns
67+
-------
68+
T
69+
An instance of the namedtuple with fields populated from the JSON.
70+
71+
"""
72+
data = json.loads(json_value)
73+
field_types = getattr(cls, '_field_types', getattr(cls, '__annotations__', {}))
74+
typed_data = {}
75+
for key, value in data.items():
76+
if key in field_types:
77+
typ = field_types[key]
78+
try:
79+
typed_data[key] = typ(value)
80+
except Exception:
81+
typed_data[key] = value # fallback if conversion fails
82+
else:
83+
typed_data[key] = value
84+
return cls(**typed_data)
85+
86+
87+
def json_to_typeddict(cls: Type[T], json_value: str) -> Dict[str, Any]:
88+
"""
89+
Convert a JSON string to a TypedDict instance.
90+
91+
Parameters
92+
----------
93+
cls : Type[T]
94+
The TypedDict type to instantiate.
95+
json_value : str or dict
96+
The JSON string or dictionary representing the object.
97+
98+
Returns
99+
-------
100+
T
101+
An instance of the TypedDict with fields populated from the JSON.
102+
103+
"""
104+
data = json.loads(json_value)
105+
field_types = getattr(cls, '__annotations__', {})
106+
typed_data = {}
107+
for key, value in data.items():
108+
if key in field_types:
109+
typ = field_types[key]
110+
try:
111+
typed_data[key] = typ(value)
112+
except Exception:
113+
typed_data[key] = value # fallback if conversion fails
114+
else:
115+
typed_data[key] = value
116+
return typed_data # TypedDicts are just dicts at runtime
117+
118+
119+
def json_to_dataclass(cls: Type[T], json_value: str) -> T:
120+
"""
121+
Convert a JSON string to a dataclass instance.
122+
123+
Parameters
124+
----------
125+
cls : Type[T]
126+
The dataclass type to instantiate.
127+
json_str : str
128+
The JSON string representing the object.
129+
130+
Returns
131+
-------
132+
T
133+
An instance of the dataclass with fields populated from the JSON.
134+
135+
"""
136+
data = json.loads(json_value)
137+
field_types = {f.name: f.type for f in dataclasses.fields(cls)} # type: ignore
138+
typed_data = {}
139+
for key, value in data.items():
140+
if key in field_types:
141+
typ = field_types[key]
142+
try:
143+
if callable(typ):
144+
typed_data[key] = typ(value)
145+
else:
146+
typed_data[key] = value
147+
except Exception:
148+
typed_data[key] = value # fallback if conversion fails
149+
else:
150+
typed_data[key] = value
151+
return cls(**typed_data)
152+
153+
154+
def json_to_pandas_dataframe(cls: Type[T], json_value: str) -> T:
155+
"""
156+
Convert a JSON string to a DataFrame instance.
157+
158+
Parameters
159+
----------
160+
cls : Type[T]
161+
The DataFrame type to instantiate.
162+
json_value : str or dict
163+
The JSON string or dictionary representing the object.
164+
165+
Returns
166+
-------
167+
T
168+
An instance of the DataFrame with fields populated from the JSON.
169+
170+
"""
171+
return cls(json.loads(json_value)) # type: ignore
172+
173+
174+
def dict_to_json(cls: Type[T], obj: Dict[str, Any]) -> str:
175+
"""
176+
Convert a dictionary to a JSON string.
177+
"""
178+
return json.dumps(obj)
179+
180+
181+
def pydantic_to_json(cls: Type[T], obj: Any) -> str:
182+
"""
183+
Convert a Pydantic model instance to a JSON string.
184+
"""
185+
return obj.model_dump_json()
186+
187+
188+
def namedtuple_to_json(cls: Type[T], obj: Any) -> str:
189+
"""
190+
Convert a namedtuple instance to a JSON string.
191+
"""
192+
return json.dumps(obj._asdict())
193+
194+
195+
def typeddict_to_json(cls: Type[T], obj: Dict[str, Any]) -> str:
196+
"""
197+
Convert a TypedDict instance (just a dict at runtime) to a JSON string.
198+
"""
199+
return json.dumps(obj)
200+
201+
202+
def dataclass_to_json(cls: Type[T], obj: Any) -> str:
203+
"""
204+
Convert a dataclass instance to a JSON string.
205+
"""
206+
return json.dumps(dataclasses.asdict(obj))
207+
208+
209+
def pandas_dataframe_to_json(cls: Type[T], obj: Any) -> str:
210+
"""
211+
Convert a pandas DataFrame to a JSON string (records orientation).
212+
"""
213+
return obj.to_json(orient='records')
214+
215+
216+
def create_json_transformers(
217+
cls: Type[T],
218+
) -> Tuple[Callable[[T], str], Callable[[str], T]]:
219+
"""
220+
Create transformers for arbitrary objects to JSON strings.
221+
222+
Parameters
223+
----------
224+
cls : Type[T]
225+
The class type to instantiate for the JSON conversion.
226+
227+
Returns
228+
-------
229+
Tuple[Callable[[T], str], Callable[[str], T]]
230+
A tuple containing two functions:
231+
- The first function converts an instance of `cls` to a JSON string.
232+
- The second function converts a JSON string back to an instance of `cls`.
233+
234+
"""
235+
if issubclass(cls, dict):
236+
return ( # type: ignore
237+
functools.partial(json_to_dict, cls),
238+
functools.partial(dict_to_json, cls),
239+
)
240+
elif utils.is_pydantic(cls):
241+
return ( # type: ignore
242+
functools.partial(json_to_pydantic, cls),
243+
functools.partial(pydantic_to_json, cls),
244+
)
245+
elif utils.is_namedtuple(cls):
246+
return ( # type: ignore
247+
functools.partial(json_to_namedtuple, cls),
248+
functools.partial(namedtuple_to_json, cls),
249+
)
250+
elif utils.is_typeddict(cls):
251+
return ( # type: ignore
252+
functools.partial(json_to_typeddict, cls),
253+
functools.partial(typeddict_to_json, cls),
254+
)
255+
elif utils.is_dataclass(cls):
256+
return ( # type: ignore
257+
functools.partial(json_to_dataclass, cls),
258+
functools.partial(dataclass_to_json, cls),
259+
)
260+
elif utils.is_dataframe(cls):
261+
return ( # type: ignore
262+
functools.partial(json_to_pandas_dataframe, cls),
263+
functools.partial(pandas_dataframe_to_json, cls),
264+
)
265+
raise TypeError(f'Unsupported type for JSON conversion: {type(cls).__name__}')

0 commit comments

Comments
 (0)