Serialize deep object query parameters (#4523) (#4664)

(cherry picked from commit b623c23183)
This commit is contained in:
Jeremiah Lowin 2026-07-27 13:10:57 -04:00 committed by GitHub
commit 87ba18c898
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 6 deletions

View file

@ -313,13 +313,13 @@ class RequestDirector:
if not value:
continue
if explode:
# form,explode=true on objects: each property becomes
# a separate query parameter.
# e.g. {"R": 100, "G": 200} → R=100&G=200
for k, v in value.items():
serialized[_query_scalar_to_str(k)] = _query_scalar_to_str(
v
)
# deepObject keeps the parent parameter name;
# form style emits each property as a bare key.
property_name = _query_scalar_to_str(k)
if param_info.style == "deepObject":
property_name = f"{key}[{property_name}]"
serialized[property_name] = _query_scalar_to_str(v)
else:
style = param_info.style or "form"
delimiter = self._STYLE_DELIMITERS.get(style, ",")

View file

@ -890,6 +890,45 @@ class TestQueryParameterSerialization:
assert "myAttribute=true" in url
assert "data=" not in url
def test_deep_object_explode_true_uses_bracket_notation(self, director):
route = HTTPRoute(
path="/items",
method="GET",
operation_id="list_items",
parameters=[
ParameterInfo(
name="filter",
location="query",
required=True,
schema={
"type": "object",
"properties": {
"eq": {"type": "string"},
"display name": {"type": "string"},
},
},
explode=True,
style="deepObject",
)
],
parameter_map={
"filter": {"location": "query", "openapi_name": "filter"},
},
)
request = director.build(
route,
{"filter": {"eq": "foo/bar", "display name": "active & ready"}},
"https://example.com",
)
assert request.url.params["filter[eq]"] == "foo/bar"
assert request.url.params["filter[display name]"] == "active & ready"
assert "filter%5Beq%5D=foo%2Fbar" in str(request.url)
assert "filter%5Bdisplay+name%5D=active+%26+ready" in str(request.url)
assert "eq" not in request.url.params
assert "display name" not in request.url.params
def test_explode_default_dict_expands_to_separate_params(self, director):
"""Default explode (None → true) on objects expands properties."""
route = HTTPRoute(