@@ -251,9 +251,12 @@ def is_local(self):
251251 pass
252252
253253
254- def serialize (resource : Any , drop_dict_null_values = True ) -> dict :
255- # TODO: make serialization pluggable
256- # TODO: add empty dict/array cleanup
254+ def serialize (resource : Any , remove_nulls = True ) -> dict :
255+ """
256+ * empty dicts/lists are always removed
257+ * nulls are removed only for dicts if `remove_nulls` is set
258+ * in lists empty dicts are transformed into nulls because nulls are used for alignment
259+ """
257260
258261 def convert_fn (item ):
259262 if isinstance (item , BaseResource ):
@@ -264,17 +267,40 @@ def convert_fn(item):
264267
265268 if _is_serializable_dict_like (item ):
266269 # Handle dict-serializable structures like pydantic Model
267- if drop_dict_null_values :
268- return _remove_dict_null_values (dict (item )), False
269- return dict (item ), False
270+ item = _remove_dict_empty_values (dict (item ))
271+
272+ if remove_nulls :
273+ return _remove_nulls (item ), False
274+ return item , False
275+
276+ if isinstance (item , list ):
277+ return _transform_list_empty_values_to_null (item ), False
270278
271279 return item , False
272280
273281 return convert_values (dict (resource ), convert_fn )
274282
275283
276- def _remove_dict_null_values (d : dict ):
277- return {key : value for key , value in d .items () if value is not None }
284+ def _remove_dict_empty_values (d : dict ):
285+ return {key : value for key , value in d .items () if not _is_empty (value )}
286+
287+
288+ def _transform_list_empty_values_to_null (d : list ):
289+ return [None if _is_empty (value ) else value for value in d ]
290+
291+
292+ def _remove_nulls (d : dict ):
293+ return {key : value for key , value in d .items () if not _is_null (value )}
294+
295+
296+ def _is_empty (d : Any ):
297+ if isinstance (d , (dict , list )):
298+ return not d
299+ return False
300+
301+
302+ def _is_null (d : Any ):
303+ return d is None
278304
279305
280306def _is_serializable_dict_like (item ):
0 commit comments