diff --git a/openapi_core/deserializing/media_types/deserializers.py b/openapi_core/deserializing/media_types/deserializers.py index 0fc6b0ba..532417ec 100644 --- a/openapi_core/deserializing/media_types/deserializers.py +++ b/openapi_core/deserializing/media_types/deserializers.py @@ -125,6 +125,19 @@ def decode_property( location: Mapping[str, Any], ) -> Any: if self.encoding is None or prop_name not in self.encoding: + prop_schema_type = prop_schema.getkey("type", "") + if ( + self.mimetype == "application/x-www-form-urlencoded" + and prop_schema_type in ["array", "object"] + ): + # default serialization strategy for complex objects + # in the application/x-www-form-urlencoded + return self.decode_property_style( + prop_name, + prop_schema, + location, + SchemaPath.from_dict({"style": "form"}), + ) return self.decode_property_content_type( prop_name, prop_schema, location ) @@ -163,20 +176,24 @@ def decode_property_content_type( prop_name: str, prop_schema: SchemaPath, location: Mapping[str, Any], - prep_encoding: Optional[SchemaPath] = None, + prop_encoding: Optional[SchemaPath] = None, ) -> Any: - prop_content_type = get_content_type(prop_schema, prep_encoding) + prop_content_type = get_content_type(prop_schema, prop_encoding) prop_deserializer = self.evolve( prop_content_type, prop_schema, ) prop_schema_type = prop_schema.getkey("type", "") - if prop_schema_type == "array": + if ( + self.mimetype.startswith("multipart") + and prop_schema_type == "array" + ): if isinstance(location, SuportsGetAll): value = location.getall(prop_name) + return list(map(prop_deserializer.deserialize, value)) if isinstance(location, SuportsGetList): value = location.getlist(prop_name) - return list(map(prop_deserializer.deserialize, value)) - else: - value = location[prop_name] - return prop_deserializer.deserialize(value) + return list(map(prop_deserializer.deserialize, value)) + + value = location[prop_name] + return prop_deserializer.deserialize(value) diff --git a/openapi_core/deserializing/media_types/util.py b/openapi_core/deserializing/media_types/util.py index aa3c333c..1469bed1 100644 --- a/openapi_core/deserializing/media_types/util.py +++ b/openapi_core/deserializing/media_types/util.py @@ -41,7 +41,7 @@ def xml_loads(value: Union[str, bytes], **parameters: str) -> Element: def urlencoded_form_loads(value: Any, **parameters: str) -> Mapping[str, Any]: - return dict(parse_qsl(value)) + return ImmutableMultiDict(parse_qsl(value)) def data_form_loads( diff --git a/tests/unit/deserializing/test_media_types_deserializers.py b/tests/unit/deserializing/test_media_types_deserializers.py index 1d099a3d..56ccb17f 100644 --- a/tests/unit/deserializing/test_media_types_deserializers.py +++ b/tests/unit/deserializing/test_media_types_deserializers.py @@ -214,6 +214,60 @@ def test_urlencoded_form_simple(self, deserializer_factory): "name": "foo bar", } + def test_urlencoded_complex(self, deserializer_factory): + mimetype = "application/x-www-form-urlencoded" + schema_dict = { + "type": "object", + "properties": { + "prop": { + "type": "array", + "items": { + "type": "integer", + }, + }, + }, + } + schema = SchemaPath.from_dict(schema_dict) + deserializer = deserializer_factory(mimetype, schema=schema) + value = "prop=a&prop=b&prop=c" + + result = deserializer.deserialize(value) + + assert result == { + "prop": ["a", "b", "c"], + } + + def test_urlencoded_content_type(self, deserializer_factory): + mimetype = "application/x-www-form-urlencoded" + schema_dict = { + "type": "object", + "properties": { + "prop": { + "type": "array", + "items": { + "type": "integer", + }, + }, + }, + } + schema = SchemaPath.from_dict(schema_dict) + encoding_dict = { + "prop": { + "contentType": "application/json", + }, + } + encoding = SchemaPath.from_dict(encoding_dict) + deserializer = deserializer_factory( + mimetype, schema=schema, encoding=encoding + ) + value = 'prop=["a","b","c"]' + + result = deserializer.deserialize(value) + + assert result == { + "prop": ["a", "b", "c"], + } + def test_urlencoded_deepobject(self, deserializer_factory): mimetype = "application/x-www-form-urlencoded" schema_dict = {