@@ -16,21 +16,23 @@ use super::{
16
16
ComputedFields , Extra , PydanticSerializer , SchemaFilter , SerializeInfer , TypeSerializer ,
17
17
} ;
18
18
19
+ /// representation of a field for serialization, used by `TypedDictSerializer` and `ModelFieldsSerializer`
19
20
#[ derive( Debug , Clone ) ]
20
- pub ( super ) struct TypedDictField {
21
+ pub ( super ) struct FieldSerializer {
21
22
key_py : Py < PyString > ,
22
23
alias : Option < String > ,
23
24
alias_py : Option < Py < PyString > > ,
24
- serializer : CombinedSerializer ,
25
+ // None serializer means exclude
26
+ serializer : Option < CombinedSerializer > ,
25
27
required : bool ,
26
28
}
27
29
28
- impl TypedDictField {
30
+ impl FieldSerializer {
29
31
pub ( super ) fn new (
30
32
py : Python ,
31
33
key_py : Py < PyString > ,
32
34
alias : Option < String > ,
33
- serializer : CombinedSerializer ,
35
+ serializer : Option < CombinedSerializer > ,
34
36
required : bool ,
35
37
) -> Self {
36
38
let alias_py = alias. as_ref ( ) . map ( |alias| PyString :: new ( py, alias. as_str ( ) ) . into ( ) ) ;
@@ -64,10 +66,10 @@ impl TypedDictField {
64
66
65
67
#[ derive( Debug , Clone ) ]
66
68
pub struct TypedDictSerializer {
67
- fields : AHashMap < String , TypedDictField > ,
69
+ fields : AHashMap < String , FieldSerializer > ,
68
70
computed_fields : Option < ComputedFields > ,
69
71
include_extra : bool ,
70
- // isize because we look up include exclude via `.hash()` which returns an isize
72
+ // isize because we look up filter via `.hash()` which returns an isize
71
73
filter : SchemaFilter < isize > ,
72
74
}
73
75
@@ -90,56 +92,44 @@ impl BuildSerializer for TypedDictSerializer {
90
92
) ;
91
93
92
94
let fields_dict: & PyDict = schema. get_as_req ( intern ! ( py, "fields" ) ) ?;
93
- let mut fields: AHashMap < String , TypedDictField > = AHashMap :: with_capacity ( fields_dict. len ( ) ) ;
94
- let mut exclude: Vec < Py < PyString > > = Vec :: with_capacity ( fields_dict. len ( ) ) ;
95
+ let mut fields: AHashMap < String , FieldSerializer > = AHashMap :: with_capacity ( fields_dict. len ( ) ) ;
95
96
96
97
for ( key, value) in fields_dict. iter ( ) {
97
98
let key_py: & PyString = key. downcast ( ) ?;
98
99
let key: String = key_py. extract ( ) ?;
99
100
let field_info: & PyDict = value. downcast ( ) ?;
100
101
101
102
let key_py: Py < PyString > = key_py. into_py ( py) ;
103
+ let required = field_info. get_as ( intern ! ( py, "required" ) ) ?. unwrap_or ( total) ;
102
104
103
105
if field_info. get_as ( intern ! ( py, "serialization_exclude" ) ) ? == Some ( true ) {
104
- exclude . push ( key_py . clone_ref ( py) ) ;
106
+ fields . insert ( key , FieldSerializer :: new ( py, key_py , None , None , required ) ) ;
105
107
} else {
106
108
let alias: Option < String > = field_info. get_as ( intern ! ( py, "serialization_alias" ) ) ?;
107
109
108
110
let schema = field_info. get_as_req ( intern ! ( py, "schema" ) ) ?;
109
111
let serializer = CombinedSerializer :: build ( schema, config, definitions)
110
112
. map_err ( |e| py_error_type ! ( "Field `{}`:\n {}" , key, e) ) ?;
111
-
112
- fields. insert (
113
- key,
114
- TypedDictField :: new (
115
- py,
116
- key_py,
117
- alias,
118
- serializer,
119
- field_info. get_as ( intern ! ( py, "required" ) ) ?. unwrap_or ( total) ,
120
- ) ,
121
- ) ;
113
+ fields. insert ( key, FieldSerializer :: new ( py, key_py, alias, Some ( serializer) , required) ) ;
122
114
}
123
115
}
124
116
125
- let filter = SchemaFilter :: from_vec_hash ( py, exclude) ?;
126
117
let computed_fields = ComputedFields :: new ( schema) ?;
127
118
128
- Ok ( Self :: new ( fields, include_extra, filter , computed_fields) . into ( ) )
119
+ Ok ( Self :: new ( fields, include_extra, computed_fields) . into ( ) )
129
120
}
130
121
}
131
122
132
123
impl TypedDictSerializer {
133
124
pub ( super ) fn new (
134
- fields : AHashMap < String , TypedDictField > ,
125
+ fields : AHashMap < String , FieldSerializer > ,
135
126
include_extra : bool ,
136
- filter : SchemaFilter < isize > ,
137
127
computed_fields : Option < ComputedFields > ,
138
128
) -> Self {
139
129
Self {
140
130
fields,
141
131
include_extra,
142
- filter,
132
+ filter : SchemaFilter :: default ( ) ,
143
133
computed_fields,
144
134
}
145
135
}
@@ -151,9 +141,9 @@ impl TypedDictSerializer {
151
141
}
152
142
}
153
143
154
- fn exclude_default ( & self , value : & PyAny , extra : & Extra , field : & TypedDictField ) -> PyResult < bool > {
144
+ fn exclude_default ( & self , value : & PyAny , extra : & Extra , serializer : & CombinedSerializer ) -> PyResult < bool > {
155
145
if extra. exclude_defaults {
156
- if let Some ( default) = field . serializer . get_default ( value. py ( ) ) ? {
146
+ if let Some ( default) = serializer. get_default ( value. py ( ) ) ? {
157
147
if value. eq ( default) ? {
158
148
return Ok ( true ) ;
159
149
}
@@ -190,21 +180,25 @@ impl TypeSerializer for TypedDictSerializer {
190
180
} ;
191
181
192
182
for ( key, value) in py_dict {
193
- let extra = Extra {
194
- field_name : Some ( key. extract ( ) ?) ,
195
- ..td_extra
196
- } ;
197
183
if extra. exclude_none && value. is_none ( ) {
198
184
continue ;
199
185
}
200
186
if let Some ( ( next_include, next_exclude) ) = self . filter . key_filter ( key, include, exclude) ? {
187
+ let extra = Extra {
188
+ field_name : Some ( key. extract ( ) ?) ,
189
+ ..td_extra
190
+ } ;
201
191
if let Ok ( key_py_str) = key. downcast :: < PyString > ( ) {
202
192
let key_str = key_py_str. to_str ( ) ?;
203
193
if let Some ( field) = self . fields . get ( key_str) {
204
- if self . exclude_default ( value, & extra, field) ? {
194
+ let serializer = match field. serializer {
195
+ Some ( ref serializer) => serializer,
196
+ None => continue ,
197
+ } ;
198
+ if self . exclude_default ( value, & extra, serializer) ? {
205
199
continue ;
206
200
}
207
- let value = field . serializer . to_python ( value, next_include, next_exclude, & extra) ?;
201
+ let value = serializer. to_python ( value, next_include, next_exclude, & extra) ?;
208
202
let output_key = field. get_key_py ( py, & extra) ;
209
203
output_dict. set_item ( output_key, value) ?;
210
204
@@ -289,17 +283,15 @@ impl TypeSerializer for TypedDictSerializer {
289
283
if let Ok ( key_py_str) = key. downcast :: < PyString > ( ) {
290
284
let key_str = key_py_str. to_str ( ) . map_err ( py_err_se_err) ?;
291
285
if let Some ( field) = self . fields . get ( key_str) {
292
- if self . exclude_default ( value, & extra, field) . map_err ( py_err_se_err) ? {
286
+ let serializer = match field. serializer {
287
+ Some ( ref serializer) => serializer,
288
+ None => continue ,
289
+ } ;
290
+ if self . exclude_default ( value, & extra, serializer) . map_err ( py_err_se_err) ? {
293
291
continue ;
294
292
}
295
293
let output_key = field. get_key_json ( key_str, & extra) ;
296
- let s = PydanticSerializer :: new (
297
- value,
298
- & field. serializer ,
299
- next_include,
300
- next_exclude,
301
- & extra,
302
- ) ;
294
+ let s = PydanticSerializer :: new ( value, serializer, next_include, next_exclude, & extra) ;
303
295
map. serialize_entry ( & output_key, & s) ?;
304
296
continue ;
305
297
}
0 commit comments