@@ -80,39 +80,7 @@ impl TypeSerializer for UnionSerializer {
80
80
exclude : Option < & Bound < ' _ , PyAny > > ,
81
81
extra : & Extra ,
82
82
) -> PyResult < PyObject > {
83
- // try the serializers in left to right order with error_on fallback=true
84
- let mut new_extra = extra. clone ( ) ;
85
- new_extra. check = SerCheck :: Strict ;
86
- let mut errors: SmallVec < [ PyErr ; SMALL_UNION_THRESHOLD ] > = SmallVec :: new ( ) ;
87
-
88
- for comb_serializer in & self . choices {
89
- match comb_serializer. to_python ( value, include, exclude, & new_extra) {
90
- Ok ( v) => return Ok ( v) ,
91
- Err ( err) => match err. is_instance_of :: < PydanticSerializationUnexpectedValue > ( value. py ( ) ) {
92
- true => ( ) ,
93
- false => errors. push ( err) ,
94
- } ,
95
- }
96
- }
97
- if self . retry_with_lax_check ( ) {
98
- new_extra. check = SerCheck :: Lax ;
99
- for comb_serializer in & self . choices {
100
- match comb_serializer. to_python ( value, include, exclude, & new_extra) {
101
- Ok ( v) => return Ok ( v) ,
102
- Err ( err) => match err. is_instance_of :: < PydanticSerializationUnexpectedValue > ( value. py ( ) ) {
103
- true => ( ) ,
104
- false => errors. push ( err) ,
105
- } ,
106
- }
107
- }
108
- }
109
-
110
- for err in & errors {
111
- extra. warnings . custom_warning ( err. to_string ( ) ) ;
112
- }
113
-
114
- extra. warnings . on_fallback_py ( self . get_name ( ) , value, extra) ?;
115
- infer_to_python ( value, include, exclude, extra)
83
+ to_python ( value, include, exclude, extra, & self . choices , self . get_name ( ) )
116
84
}
117
85
118
86
fn json_key < ' a > ( & self , key : & ' a Bound < ' _ , PyAny > , extra : & Extra ) -> PyResult < Cow < ' a , str > > {
@@ -202,10 +170,55 @@ impl TypeSerializer for UnionSerializer {
202
170
}
203
171
}
204
172
205
- #[ derive( Debug , Clone ) ]
173
+ fn to_python (
174
+ value : & Bound < ' _ , PyAny > ,
175
+ include : Option < & Bound < ' _ , PyAny > > ,
176
+ exclude : Option < & Bound < ' _ , PyAny > > ,
177
+ extra : & Extra ,
178
+ choices : & [ CombinedSerializer ] ,
179
+ name : & str ,
180
+ ) -> PyResult < PyObject > {
181
+ // try the serializers in left to right order with error_on fallback=true
182
+ let mut new_extra = extra. clone ( ) ;
183
+ new_extra. check = SerCheck :: Strict ;
184
+ let mut errors: SmallVec < [ PyErr ; SMALL_UNION_THRESHOLD ] > = SmallVec :: new ( ) ;
185
+
186
+ for comb_serializer in choices. clone ( ) {
187
+ match comb_serializer. to_python ( value, include, exclude, & new_extra) {
188
+ Ok ( v) => return Ok ( v) ,
189
+ Err ( err) => match err. is_instance_of :: < PydanticSerializationUnexpectedValue > ( value. py ( ) ) {
190
+ true => ( ) ,
191
+ false => errors. push ( err) ,
192
+ } ,
193
+ }
194
+ }
195
+
196
+ let retry_with_lax_check = choices. clone ( ) . into_iter ( ) . any ( CombinedSerializer :: retry_with_lax_check) ;
197
+ if retry_with_lax_check {
198
+ new_extra. check = SerCheck :: Lax ;
199
+ for comb_serializer in choices {
200
+ match comb_serializer. to_python ( value, include, exclude, & new_extra) {
201
+ Ok ( v) => return Ok ( v) ,
202
+ Err ( err) => match err. is_instance_of :: < PydanticSerializationUnexpectedValue > ( value. py ( ) ) {
203
+ true => ( ) ,
204
+ false => errors. push ( err) ,
205
+ } ,
206
+ }
207
+ }
208
+ }
209
+
210
+ for err in & errors {
211
+ extra. warnings . custom_warning ( err. to_string ( ) ) ;
212
+ }
213
+
214
+ extra. warnings . on_fallback_py ( name, value, extra) ?;
215
+ infer_to_python ( value, include, exclude, extra)
216
+ }
217
+
218
+ #[ derive( Debug ) ]
206
219
pub struct TaggedUnionSerializer {
207
220
discriminator : Discriminator ,
208
- lookup : HashMap < String , CombinedSerializer > ,
221
+ lookup : HashMap < String , usize > ,
209
222
choices : Vec < CombinedSerializer > ,
210
223
name : String ,
211
224
}
@@ -221,14 +234,15 @@ impl BuildSerializer for TaggedUnionSerializer {
221
234
let py = schema. py ( ) ;
222
235
let discriminator = Discriminator :: new ( py, & schema. get_as_req ( intern ! ( py, "discriminator" ) ) ?) ?;
223
236
237
+ // TODO: guarantee at least 1 choice
224
238
let choices_map: Bound < PyDict > = schema. get_as_req ( intern ! ( py, "choices" ) ) ?;
225
- let mut lookup: HashMap < String , CombinedSerializer > = HashMap :: with_capacity ( choices_map. len ( ) ) ;
226
- let mut choices: Vec < CombinedSerializer > = Vec :: with_capacity ( choices_map. len ( ) ) ;
239
+ let mut lookup = HashMap :: with_capacity ( choices_map. len ( ) ) ;
240
+ let mut choices = Vec :: with_capacity ( choices_map. len ( ) ) ;
227
241
228
- for ( choice_key, choice_schema) in choices_map {
229
- let serializer = CombinedSerializer :: build ( choice_schema. downcast ( ) ?, config, definitions) . unwrap ( ) ;
230
- choices. push ( serializer. clone ( ) ) ;
231
- lookup. insert ( choice_key. to_string ( ) , serializer ) ;
242
+ for ( idx , ( choice_key, choice_schema) ) in choices_map. into_iter ( ) . enumerate ( ) {
243
+ let serializer = CombinedSerializer :: build ( choice_schema. downcast ( ) ?, config, definitions) ? ;
244
+ choices. push ( serializer) ;
245
+ lookup. insert ( choice_key. to_string ( ) , idx ) ;
232
246
}
233
247
234
248
let descr = choices
@@ -265,13 +279,13 @@ impl TypeSerializer for TaggedUnionSerializer {
265
279
if let Some ( tag) = self . get_discriminator_value ( value) {
266
280
let tag_str = tag. to_string ( ) ;
267
281
if let Some ( serializer) = self . lookup . get ( & tag_str) {
268
- match serializer. to_python ( value, include, exclude, & new_extra) {
282
+ match self . choices [ * serializer] . to_python ( value, include, exclude, & new_extra) {
269
283
Ok ( v) => return Ok ( v) ,
270
284
Err ( err) => match err. is_instance_of :: < PydanticSerializationUnexpectedValue > ( py) {
271
285
true => {
272
286
if self . retry_with_lax_check ( ) {
273
287
new_extra. check = SerCheck :: Lax ;
274
- return serializer. to_python ( value, include, exclude, & new_extra) ;
288
+ return self . choices [ * serializer] . to_python ( value, include, exclude, & new_extra) ;
275
289
}
276
290
}
277
291
false => return Err ( err) ,
@@ -280,13 +294,7 @@ impl TypeSerializer for TaggedUnionSerializer {
280
294
}
281
295
}
282
296
283
- let basic_union_ser = UnionSerializer :: from_choices ( self . choices . clone ( ) ) ;
284
- if let Ok ( s) = basic_union_ser {
285
- return s. to_python ( value, include, exclude, extra) ;
286
- }
287
-
288
- extra. warnings . on_fallback_py ( self . get_name ( ) , value, extra) ?;
289
- infer_to_python ( value, include, exclude, extra)
297
+ to_python ( value, include, exclude, extra, & self . choices , self . get_name ( ) )
290
298
}
291
299
292
300
fn json_key < ' a > ( & self , key : & ' a Bound < ' _ , PyAny > , extra : & Extra ) -> PyResult < Cow < ' a , str > > {
@@ -296,12 +304,12 @@ impl TypeSerializer for TaggedUnionSerializer {
296
304
if let Some ( tag) = self . get_discriminator_value ( key) {
297
305
let tag_str = tag. to_string ( ) ;
298
306
if let Some ( serializer) = self . lookup . get ( & tag_str) {
299
- match serializer. json_key ( key, & new_extra) {
307
+ match self . choices [ * serializer] . json_key ( key, & new_extra) {
300
308
Ok ( v) => return Ok ( v) ,
301
309
Err ( _) => {
302
310
if self . retry_with_lax_check ( ) {
303
311
new_extra. check = SerCheck :: Lax ;
304
- return serializer. json_key ( key, & new_extra) ;
312
+ return self . choices [ * serializer] . json_key ( key, & new_extra) ;
305
313
}
306
314
}
307
315
}
@@ -332,12 +340,12 @@ impl TypeSerializer for TaggedUnionSerializer {
332
340
if let Some ( tag) = self . get_discriminator_value ( value) {
333
341
let tag_str = tag. to_string ( ) ;
334
342
if let Some ( selected_serializer) = self . lookup . get ( & tag_str) {
335
- match selected_serializer. to_python ( value, include, exclude, & new_extra) {
343
+ match self . choices [ * selected_serializer] . to_python ( value, include, exclude, & new_extra) {
336
344
Ok ( v) => return infer_serialize ( v. bind ( py) , serializer, None , None , extra) ,
337
345
Err ( _) => {
338
346
if self . retry_with_lax_check ( ) {
339
347
new_extra. check = SerCheck :: Lax ;
340
- match selected_serializer. to_python ( value, include, exclude, & new_extra) {
348
+ match self . choices [ * selected_serializer] . to_python ( value, include, exclude, & new_extra) {
341
349
Ok ( v) => return infer_serialize ( v. bind ( py) , serializer, None , None , extra) ,
342
350
Err ( err) => return Err ( py_err_se_err ( err) ) ,
343
351
}
0 commit comments