19
19
20
20
from openapi_core .extensions .models .factories import ModelClassImporter
21
21
from openapi_core .schema .schemas import get_all_properties
22
- from openapi_core .schema .schemas import get_all_properties_names
23
22
from openapi_core .spec import Spec
24
23
from openapi_core .unmarshalling .schemas .datatypes import FormattersDict
25
24
from openapi_core .unmarshalling .schemas .enums import UnmarshalContext
@@ -201,6 +200,15 @@ def object_class_factory(self) -> ModelClassImporter:
201
200
return ModelClassImporter ()
202
201
203
202
def unmarshal (self , value : Any ) -> Any :
203
+ properties = self .unmarshal_raw (value )
204
+
205
+ model = self .schema .getkey ("x-model" )
206
+ fields : Iterable [str ] = properties and properties .keys () or []
207
+ object_class = self .object_class_factory .create (fields , model = model )
208
+
209
+ return object_class (** properties )
210
+
211
+ def unmarshal_raw (self , value : Any ) -> Any :
204
212
try :
205
213
value = self .formatter .unmarshal (value )
206
214
except ValueError as exc :
@@ -209,65 +217,57 @@ def unmarshal(self, value: Any) -> Any:
209
217
else :
210
218
return self ._unmarshal_object (value )
211
219
220
+ def _clone (self , schema : Spec ) -> "ObjectUnmarshaller" :
221
+ return ObjectUnmarshaller (
222
+ schema ,
223
+ self .validator ,
224
+ self .formatter ,
225
+ self .unmarshallers_factory ,
226
+ self .context ,
227
+ )
228
+
212
229
def _unmarshal_object (self , value : Any ) -> Any :
230
+ properties = {}
231
+
213
232
if "oneOf" in self .schema :
214
- properties = None
233
+ one_of_properties = None
215
234
for one_of_schema in self .schema / "oneOf" :
216
235
try :
217
- unmarshalled = self ._unmarshal_properties (
218
- value , one_of_schema
236
+ unmarshalled = self ._clone ( one_of_schema ). unmarshal_raw (
237
+ value
219
238
)
220
239
except (UnmarshalError , ValueError ):
221
240
pass
222
241
else :
223
- if properties is not None :
242
+ if one_of_properties is not None :
224
243
log .warning ("multiple valid oneOf schemas found" )
225
244
continue
226
- properties = unmarshalled
245
+ one_of_properties = unmarshalled
227
246
228
- if properties is None :
247
+ if one_of_properties is None :
229
248
log .warning ("valid oneOf schema not found" )
249
+ else :
250
+ properties .update (one_of_properties )
230
251
231
- else :
232
- properties = self ._unmarshal_properties (value )
233
-
234
- model = self .schema .getkey ("x-model" )
235
- fields : Iterable [str ] = properties and properties .keys () or []
236
- object_class = self .object_class_factory .create (fields , model = model )
237
-
238
- return object_class (** properties )
239
-
240
- def _unmarshal_properties (
241
- self , value : Any , one_of_schema : Optional [Spec ] = None
242
- ) -> Dict [str , Any ]:
243
- all_props = get_all_properties (self .schema )
244
- all_props_names = get_all_properties_names (self .schema )
245
-
246
- if one_of_schema is not None :
247
- all_props .update (get_all_properties (one_of_schema ))
248
- all_props_names |= get_all_properties_names (one_of_schema )
249
-
250
- value_props_names = list (value .keys ())
251
- extra_props = set (value_props_names ) - set (all_props_names )
252
+ elif "anyOf" in self .schema :
253
+ any_of_properties = None
254
+ for any_of_schema in self .schema / "anyOf" :
255
+ try :
256
+ unmarshalled = self ._clone (any_of_schema ).unmarshal_raw (
257
+ value
258
+ )
259
+ except (UnmarshalError , ValueError ):
260
+ pass
261
+ else :
262
+ any_of_properties = unmarshalled
263
+ break
252
264
253
- properties : Dict [str , Any ] = {}
254
- additional_properties = self .schema .getkey (
255
- "additionalProperties" , True
256
- )
257
- if additional_properties is not False :
258
- # free-form object
259
- if additional_properties is True :
260
- additional_prop_schema = Spec .from_dict ({})
261
- # defined schema
265
+ if any_of_properties is None :
266
+ log .warning ("valid anyOf schema not found" )
262
267
else :
263
- additional_prop_schema = self .schema / "additionalProperties"
264
- for prop_name in extra_props :
265
- prop_value = value [prop_name ]
266
- properties [prop_name ] = self .unmarshallers_factory .create (
267
- additional_prop_schema
268
- )(prop_value )
268
+ properties .update (any_of_properties )
269
269
270
- for prop_name , prop in list ( all_props . items () ):
270
+ for prop_name , prop in get_all_properties ( self . schema ). items ():
271
271
read_only = prop .getkey ("readOnly" , False )
272
272
if self .context == UnmarshalContext .REQUEST and read_only :
273
273
continue
@@ -285,6 +285,24 @@ def _unmarshal_properties(
285
285
prop_value
286
286
)
287
287
288
+ additional_properties = self .schema .getkey (
289
+ "additionalProperties" , True
290
+ )
291
+ if additional_properties is not False :
292
+ # free-form object
293
+ if additional_properties is True :
294
+ additional_prop_schema = Spec .from_dict ({})
295
+ # defined schema
296
+ else :
297
+ additional_prop_schema = self .schema / "additionalProperties"
298
+ additional_prop_unmarshaler = self .unmarshallers_factory .create (
299
+ additional_prop_schema
300
+ )
301
+ for prop_name , prop_value in value .items ():
302
+ if prop_name in properties :
303
+ continue
304
+ properties [prop_name ] = additional_prop_unmarshaler (prop_value )
305
+
288
306
return properties
289
307
290
308
@@ -304,6 +322,10 @@ def unmarshal(self, value: Any) -> Any:
304
322
if one_of_schema :
305
323
return self .unmarshallers_factory .create (one_of_schema )(value )
306
324
325
+ any_of_schema = self ._get_any_of_schema (value )
326
+ if any_of_schema :
327
+ return self .unmarshallers_factory .create (any_of_schema )(value )
328
+
307
329
all_of_schema = self ._get_all_of_schema (value )
308
330
if all_of_schema :
309
331
return self .unmarshallers_factory .create (all_of_schema )(value )
@@ -338,6 +360,21 @@ def _get_one_of_schema(self, value: Any) -> Optional[Spec]:
338
360
return subschema
339
361
return None
340
362
363
+ def _get_any_of_schema (self , value : Any ) -> Optional [Spec ]:
364
+ if "anyOf" not in self .schema :
365
+ return None
366
+
367
+ any_of_schemas = self .schema / "anyOf"
368
+ for subschema in any_of_schemas :
369
+ unmarshaller = self .unmarshallers_factory .create (subschema )
370
+ try :
371
+ unmarshaller .validate (value )
372
+ except ValidateError :
373
+ continue
374
+ else :
375
+ return subschema
376
+ return None
377
+
341
378
def _get_all_of_schema (self , value : Any ) -> Optional [Spec ]:
342
379
if "allOf" not in self .schema :
343
380
return None
0 commit comments