@@ -13,7 +13,7 @@ use crate::PydanticSerializationUnexpectedValue;
1313
1414use super :: computed_fields:: ComputedFields ;
1515use super :: errors:: py_err_se_err;
16- use super :: extra:: Extra ;
16+ use super :: extra:: { Extra , SortKeysMode } ;
1717use super :: filter:: SchemaFilter ;
1818use super :: infer:: { infer_json_key, infer_serialize, infer_to_python, SerializeInfer } ;
1919use super :: shared:: PydanticSerializer ;
@@ -156,7 +156,7 @@ impl GeneralFieldsSerializer {
156156 let output_dict = PyDict :: new ( py) ;
157157 let mut used_req_fields: usize = 0 ;
158158
159- if ! extra. sort_keys {
159+ if matches ! ( extra. sort_keys, SortKeysMode :: Unsorted ) {
160160 for result in main_iter {
161161 let ( key, value) = result?;
162162 if let Some ( is_required) =
@@ -201,6 +201,23 @@ impl GeneralFieldsSerializer {
201201 }
202202 }
203203
204+ fn sort_dict_recursive < ' py > ( py : Python < ' py > , value : & Bound < ' py , PyAny > ) -> PyResult < Bound < ' py , PyDict > > {
205+ let dict = value. downcast :: < PyDict > ( ) ?;
206+ let mut items = dict_items ( dict) . collect :: < PyResult < Vec < _ > > > ( ) ?;
207+ items. sort_by_cached_key ( |( key, _) | key_str ( key) . unwrap_or_default ( ) . to_string ( ) ) ;
208+
209+ let sorted_dict = PyDict :: new ( py) ;
210+ for ( k, v) in items {
211+ if v. downcast :: < PyDict > ( ) . is_ok ( ) {
212+ let sorted_v = Self :: sort_dict_recursive ( py, & v) ?;
213+ sorted_dict. set_item ( k, sorted_v) ?;
214+ } else {
215+ sorted_dict. set_item ( k, v) ?;
216+ }
217+ }
218+ Ok ( sorted_dict)
219+ }
220+
204221 fn process_field_entry_python < ' py > (
205222 & self ,
206223 key : & Bound < ' py , PyAny > ,
@@ -235,10 +252,21 @@ impl GeneralFieldsSerializer {
235252 if let Some ( field) = op_field {
236253 if let Some ( ref serializer) = field. serializer {
237254 if !exclude_default ( value, & field_extra, serializer) ? {
238- let value =
239- serializer. to_python ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?;
255+ let processed_value = if matches ! ( extra. sort_keys, SortKeysMode :: Recursive )
256+ && value. downcast :: < PyDict > ( ) . is_ok ( )
257+ {
258+ let sorted_dict = Self :: sort_dict_recursive ( value. py ( ) , value) ?;
259+ serializer. to_python (
260+ sorted_dict. as_ref ( ) ,
261+ next_include. as_ref ( ) ,
262+ next_exclude. as_ref ( ) ,
263+ & field_extra,
264+ ) ?
265+ } else {
266+ serializer. to_python ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?
267+ } ;
240268 let output_key = field. get_key_py ( output_dict. py ( ) , & field_extra) ;
241- output_dict. set_item ( output_key, value ) ?;
269+ output_dict. set_item ( output_key, processed_value ) ?;
242270 }
243271 }
244272
@@ -247,13 +275,33 @@ impl GeneralFieldsSerializer {
247275 }
248276 return Ok ( Some ( false ) ) ;
249277 } else if self . mode == FieldsMode :: TypedDictAllow {
250- let value = match & self . extra_serializer {
251- Some ( serializer) => {
252- serializer. to_python ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?
278+ let processed_value = if matches ! ( extra. sort_keys, SortKeysMode :: Recursive )
279+ && value. downcast :: < PyDict > ( ) . is_ok ( )
280+ {
281+ let sorted_dict = Self :: sort_dict_recursive ( value. py ( ) , value) ?;
282+ match & self . extra_serializer {
283+ Some ( serializer) => serializer. to_python (
284+ sorted_dict. as_ref ( ) ,
285+ next_include. as_ref ( ) ,
286+ next_exclude. as_ref ( ) ,
287+ & field_extra,
288+ ) ?,
289+ None => infer_to_python (
290+ sorted_dict. as_ref ( ) ,
291+ next_include. as_ref ( ) ,
292+ next_exclude. as_ref ( ) ,
293+ & field_extra,
294+ ) ?,
295+ }
296+ } else {
297+ match & self . extra_serializer {
298+ Some ( serializer) => {
299+ serializer. to_python ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?
300+ }
301+ None => infer_to_python ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?,
253302 }
254- None => infer_to_python ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?,
255303 } ;
256- output_dict. set_item ( key, value ) ?;
304+ output_dict. set_item ( key, processed_value ) ?;
257305 return Ok ( None ) ;
258306 } else if field_extra. check == SerCheck :: Strict {
259307 return Err ( PydanticSerializationUnexpectedValue :: new (
@@ -281,7 +329,7 @@ impl GeneralFieldsSerializer {
281329 // we don't both with `used_fields` here because on unions, `to_python(..., mode='json')` is used
282330 let mut map = serializer. serialize_map ( Some ( expected_len) ) ?;
283331
284- if ! extra. sort_keys {
332+ if matches ! ( extra. sort_keys, SortKeysMode :: Unsorted ) {
285333 for result in main_iter {
286334 let ( key, value) = result. map_err ( py_err_se_err) ?;
287335 self . process_field_entry :: < S > ( & key, & value, & mut map, include, exclude, & extra) ?;
@@ -308,32 +356,56 @@ impl GeneralFieldsSerializer {
308356 if extra. exclude_none && value. is_none ( ) {
309357 return Ok ( ( ) ) ;
310358 }
311- let key_str = key_str ( key) . map_err ( py_err_se_err) ?;
359+ let field_key_str = key_str ( key) . map_err ( py_err_se_err) ?;
312360 let field_extra = Extra {
313- field_name : Some ( key_str ) ,
361+ field_name : Some ( field_key_str ) ,
314362 ..* extra
315363 } ;
316364
317365 let filter = self . filter . key_filter ( key, include, exclude) . map_err ( py_err_se_err) ?;
318366 if let Some ( ( next_include, next_exclude) ) = filter {
319- if let Some ( field) = self . fields . get ( key_str ) {
367+ if let Some ( field) = self . fields . get ( field_key_str ) {
320368 if let Some ( ref serializer) = field. serializer {
321369 if !exclude_default ( value, & field_extra, serializer) . map_err ( py_err_se_err) ? {
322- let s = PydanticSerializer :: new (
323- value,
324- serializer,
325- next_include. as_ref ( ) ,
326- next_exclude. as_ref ( ) ,
327- & field_extra,
328- ) ;
329- let output_key = field. get_key_json ( key_str, & field_extra) ;
330- map. serialize_entry ( & output_key, & s) ?;
370+ if matches ! ( extra. sort_keys, SortKeysMode :: Recursive ) && value. downcast :: < PyDict > ( ) . is_ok ( ) {
371+ let sorted_dict = Self :: sort_dict_recursive ( value. py ( ) , value) . map_err ( py_err_se_err) ?;
372+ let s = PydanticSerializer :: new (
373+ sorted_dict. as_ref ( ) ,
374+ serializer,
375+ next_include. as_ref ( ) ,
376+ next_exclude. as_ref ( ) ,
377+ & field_extra,
378+ ) ;
379+ let output_key = field. get_key_json ( field_key_str, & field_extra) ;
380+ map. serialize_entry ( & output_key, & s) ?;
381+ } else {
382+ let s = PydanticSerializer :: new (
383+ value,
384+ serializer,
385+ next_include. as_ref ( ) ,
386+ next_exclude. as_ref ( ) ,
387+ & field_extra,
388+ ) ;
389+ let output_key = field. get_key_json ( field_key_str, & field_extra) ;
390+ map. serialize_entry ( & output_key, & s) ?;
391+ }
331392 }
332393 }
333394 } else if self . mode == FieldsMode :: TypedDictAllow {
334395 let output_key = infer_json_key ( key, & field_extra) . map_err ( py_err_se_err) ?;
335- let s = SerializeInfer :: new ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ;
336- map. serialize_entry ( & output_key, & s) ?;
396+ if matches ! ( extra. sort_keys, SortKeysMode :: Recursive ) && value. downcast :: < PyDict > ( ) . is_ok ( ) {
397+ let sorted_dict = Self :: sort_dict_recursive ( value. py ( ) , value) . map_err ( py_err_se_err) ?;
398+ let s = SerializeInfer :: new (
399+ sorted_dict. as_ref ( ) ,
400+ next_include. as_ref ( ) ,
401+ next_exclude. as_ref ( ) ,
402+ & field_extra,
403+ ) ;
404+ map. serialize_entry ( & output_key, & s) ?;
405+ } else {
406+ let s = SerializeInfer :: new ( value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ;
407+ map. serialize_entry ( & output_key, & s) ?;
408+ }
337409 }
338410 // no error case here since unions (which need the error case) use `to_python(..., mode='json')`
339411 }
0 commit comments