1919
2020from openapi_core .extensions .models .factories import ModelClassImporter
2121from openapi_core .schema .schemas import get_all_properties
22- from openapi_core .schema .schemas import get_all_properties_names
2322from openapi_core .spec import Spec
2423from openapi_core .unmarshalling .schemas .datatypes import FormattersDict
2524from openapi_core .unmarshalling .schemas .enums import UnmarshalContext
@@ -201,6 +200,15 @@ def object_class_factory(self) -> ModelClassImporter:
201200 return ModelClassImporter ()
202201
203202 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 :
204212 try :
205213 value = self .formatter .unmarshal (value )
206214 except ValueError as exc :
@@ -209,65 +217,57 @@ def unmarshal(self, value: Any) -> Any:
209217 else :
210218 return self ._unmarshal_object (value )
211219
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+
212229 def _unmarshal_object (self , value : Any ) -> Any :
230+ properties = {}
231+
213232 if "oneOf" in self .schema :
214- properties = None
233+ one_of_properties = None
215234 for one_of_schema in self .schema / "oneOf" :
216235 try :
217- unmarshalled = self ._unmarshal_properties (
218- value , one_of_schema
236+ unmarshalled = self ._clone ( one_of_schema ). unmarshal_raw (
237+ value
219238 )
220239 except (UnmarshalError , ValueError ):
221240 pass
222241 else :
223- if properties is not None :
242+ if one_of_properties is not None :
224243 log .warning ("multiple valid oneOf schemas found" )
225244 continue
226- properties = unmarshalled
245+ one_of_properties = unmarshalled
227246
228- if properties is None :
247+ if one_of_properties is None :
229248 log .warning ("valid oneOf schema not found" )
249+ else :
250+ properties .update (one_of_properties )
230251
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
252264
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" )
262267 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 )
269269
270- for prop_name , prop in list ( all_props . items () ):
270+ for prop_name , prop in get_all_properties ( self . schema ). items ():
271271 read_only = prop .getkey ("readOnly" , False )
272272 if self .context == UnmarshalContext .REQUEST and read_only :
273273 continue
@@ -285,6 +285,24 @@ def _unmarshal_properties(
285285 prop_value
286286 )
287287
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+
288306 return properties
289307
290308
@@ -304,6 +322,10 @@ def unmarshal(self, value: Any) -> Any:
304322 if one_of_schema :
305323 return self .unmarshallers_factory .create (one_of_schema )(value )
306324
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+
307329 all_of_schema = self ._get_all_of_schema (value )
308330 if all_of_schema :
309331 return self .unmarshallers_factory .create (all_of_schema )(value )
@@ -338,6 +360,21 @@ def _get_one_of_schema(self, value: Any) -> Optional[Spec]:
338360 return subschema
339361 return None
340362
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+
341378 def _get_all_of_schema (self , value : Any ) -> Optional [Spec ]:
342379 if "allOf" not in self .schema :
343380 return None
0 commit comments