@@ -11,6 +11,8 @@ use smallvec::SmallVec;
1111
1212use crate :: common:: missing_sentinel:: get_missing_sentinel_object;
1313use crate :: serializers:: extra:: SerCheck ;
14+ use crate :: serializers:: type_serializers:: any:: AnySerializer ;
15+ use crate :: serializers:: type_serializers:: function:: { FunctionPlainSerializer , FunctionWrapSerializer } ;
1416use crate :: PydanticSerializationUnexpectedValue ;
1517
1618use super :: computed_fields:: ComputedFields ;
@@ -190,31 +192,26 @@ impl GeneralFieldsSerializer {
190192 ..extra
191193 } ;
192194 if let Some ( ( next_include, next_exclude) ) = self . filter . key_filter ( & key, include, exclude) ? {
193- if let Some ( field) = op_field {
194- if let Some ( ref serializer) = field. serializer {
195- if exclude_default ( & value, & field_extra, serializer) ? {
196- continue ;
197- }
198- if serialization_exclude_if ( field. serialization_exclude_if . as_ref ( ) , & value) ? {
199- continue ;
200- }
201- let value =
202- serializer. to_python ( & value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?;
203- let output_key = field. get_key_py ( output_dict. py ( ) , & field_extra) ;
204- output_dict. set_item ( output_key, value) ?;
205- }
195+ let ( key, serializer) = if let Some ( field) = op_field {
196+ let serializer = Self :: prepare_value ( & value, field, & field_extra) ?;
206197
207198 if field. required {
208199 used_req_fields += 1 ;
209200 }
210- } else if self . mode == FieldsMode :: TypedDictAllow {
211- let value = match & self . extra_serializer {
212- Some ( serializer) => {
213- serializer. to_python ( & value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?
214- }
215- _ => infer_to_python ( & value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ?,
201+
202+ let Some ( serializer) = serializer else {
203+ continue ;
216204 } ;
217- output_dict. set_item ( key, value) ?;
205+
206+ ( field. get_key_py ( output_dict. py ( ) , & field_extra) , serializer)
207+ } else if self . mode == FieldsMode :: TypedDictAllow {
208+ let serializer = self
209+ . extra_serializer
210+ . as_ref ( )
211+ // If using `serialize_as_any`, extras are always inferred
212+ . filter ( |_| !extra. serialize_as_any )
213+ . unwrap_or_else ( || AnySerializer :: get ( ) ) ;
214+ ( & key, serializer)
218215 } else if field_extra. check == SerCheck :: Strict {
219216 return Err ( PydanticSerializationUnexpectedValue :: new (
220217 Some ( format ! ( "Unexpected field `{key}`" ) ) ,
@@ -223,7 +220,18 @@ impl GeneralFieldsSerializer {
223220 None ,
224221 )
225222 . to_py_err ( ) ) ;
226- }
223+ } else {
224+ continue ;
225+ } ;
226+
227+ // Use `no_infer` here because the `serialize_as_any` logic has been handled in `prepare_value`
228+ let value = serializer. to_python_no_infer (
229+ & value,
230+ next_include. as_ref ( ) ,
231+ next_exclude. as_ref ( ) ,
232+ & field_extra,
233+ ) ?;
234+ output_dict. set_item ( key, value) ?;
227235 }
228236 }
229237
@@ -257,7 +265,7 @@ impl GeneralFieldsSerializer {
257265 extra : Extra ,
258266 ) -> Result < S :: SerializeMap , S :: Error > {
259267 // NOTE! As above, we maintain the order of the input dict assuming that's right
260- // we don't both with `used_fields ` here because on unions, `to_python(..., mode='json')` is used
268+ // we don't both with `used_req_fields ` here because on unions, `to_python(..., mode='json')` is used
261269 let mut map = serializer. serialize_map ( Some ( expected_len) ) ?;
262270
263271 for result in main_iter {
@@ -278,26 +286,23 @@ impl GeneralFieldsSerializer {
278286 let filter = self . filter . key_filter ( & key, include, exclude) . map_err ( py_err_se_err) ?;
279287 if let Some ( ( next_include, next_exclude) ) = filter {
280288 if let Some ( field) = self . fields . get ( key_str) {
281- if let Some ( ref serializer) = field. serializer {
282- if exclude_default ( & value, & field_extra, serializer) . map_err ( py_err_se_err) ? {
283- continue ;
284- }
285- if serialization_exclude_if ( field. serialization_exclude_if . as_ref ( ) , & value)
286- . map_err ( py_err_se_err) ?
287- {
288- continue ;
289- }
290- let s = PydanticSerializer :: new (
291- & value,
292- serializer,
293- next_include. as_ref ( ) ,
294- next_exclude. as_ref ( ) ,
295- & field_extra,
296- ) ;
297- let output_key = field. get_key_json ( key_str, & field_extra) ;
298- map. serialize_entry ( & output_key, & s) ?;
299- }
289+ let Some ( serializer) = Self :: prepare_value ( & value, field, & field_extra) . map_err ( py_err_se_err) ?
290+ else {
291+ continue ;
292+ } ;
293+
294+ // Use `no_infer` here because the `serialize_as_any` logic has been handled in `prepare_value`
295+ let s = PydanticSerializer :: new_no_infer (
296+ & value,
297+ serializer,
298+ next_include. as_ref ( ) ,
299+ next_exclude. as_ref ( ) ,
300+ & field_extra,
301+ ) ;
302+ let output_key = field. get_key_json ( key_str, & field_extra) ;
303+ map. serialize_entry ( & output_key, & s) ?;
300304 } else if self . mode == FieldsMode :: TypedDictAllow {
305+ // FIXME: why is `extra_serializer` not used here when `serialize_as_any` is not set?
301306 let output_key = infer_json_key ( & key, & field_extra) . map_err ( py_err_se_err) ?;
302307 let s = SerializeInfer :: new ( & value, next_include. as_ref ( ) , next_exclude. as_ref ( ) , & field_extra) ;
303308 map. serialize_entry ( & output_key, & s) ?;
@@ -308,6 +313,49 @@ impl GeneralFieldsSerializer {
308313 Ok ( map)
309314 }
310315
316+ /// Gets the serializer to use for a field, applying `serialize_as_any` logic and applying any
317+ /// field-level exclusionsg
318+ fn prepare_value < ' s > (
319+ value : & Bound < ' _ , PyAny > ,
320+ field : & ' s SerField ,
321+ field_extra : & Extra ,
322+ ) -> PyResult < Option < & ' s Arc < CombinedSerializer > > > {
323+ let Some ( serializer) = field. serializer . as_ref ( ) else {
324+ // field excluded at schema level
325+ return Ok ( None ) ;
326+ } ;
327+
328+ if exclude_default ( value, field_extra, serializer) ? {
329+ return Ok ( None ) ;
330+ }
331+
332+ // FIXME: should `exclude_if` be applied to extra fields too?
333+ if serialization_exclude_if ( field. serialization_exclude_if . as_ref ( ) , value) ? {
334+ return Ok ( None ) ;
335+ }
336+
337+ Ok ( Some (
338+ if field_extra. serialize_as_any &&
339+ // if serialize_as_any is set, we ensure that field serializers are
340+ // still used, because this would match the `SerializeAsAny` annotation
341+ // on a field
342+ !matches ! (
343+ serializer. as_ref( ) ,
344+ CombinedSerializer :: Function ( FunctionPlainSerializer {
345+ is_field_serializer: true ,
346+ ..
347+ } ) | CombinedSerializer :: FunctionWrap ( FunctionWrapSerializer {
348+ is_field_serializer: true ,
349+ ..
350+ } )
351+ ) {
352+ AnySerializer :: get ( )
353+ } else {
354+ serializer
355+ } ,
356+ ) )
357+ }
358+
311359 pub ( crate ) fn add_computed_fields_python (
312360 & self ,
313361 model : Option < & Bound < ' _ , PyAny > > ,
@@ -425,7 +473,7 @@ impl TypeSerializer for GeneralFieldsSerializer {
425473 _ => self . fields . len ( ) + option_length ! ( extra_dict) + self . computed_field_count ( ) ,
426474 } ;
427475 // NOTE! As above, we maintain the order of the input dict assuming that's right
428- // we don't both with `used_fields ` here because on unions, `to_python(..., mode='json')` is used
476+ // we don't both with `used_req_fields ` here because on unions, `to_python(..., mode='json')` is used
429477 let mut map = self . main_serde_serialize (
430478 dict_items ( & main_dict) ,
431479 expected_len,
0 commit comments