Describe the bug
Split out from #10973 to track the C# (@typespec/http-client-csharp) portion specifically.
When a @query parameter uses @query(#{explode: true}) on a model type that contains complex/nested objects (e.g. arrays of objects), the generated C# client serializes the whole model via its .ToString() instead of expanding it into bracket-notation key/value pairs.
Reproduction
TypeSpec
import "@typespec/http";
using TypeSpec.Http;
model Condition {
field: string;
value: string;
}
model SearchFilter {
items?: Condition[];
}
model ItemCollection { data: unknown[]; }
@service
@route("/api/v1")
namespace Example {
@route("/items")
interface Items {
@get list(
@query(#{explode: true}) filter?: SearchFilter,
): ItemCollection;
}
}
Generated C#
The generated RestClient calls:
uri.AppendQuery("filter", TypeFormatters.ConvertToString(filter), true);
TypeFormatters.ConvertToString(filter) falls back to filter.ToString(), which returns the type name.
Actual behavior
GET /api/v1/items?filter=Example.SearchFilter
Expected behavior
Deep-object (OpenAPI style: deepObject, explode: true) serialization, e.g.:
GET /api/v1/items?filter[items][0][field]=status&filter[items][0][value]=active&filter[items][1][field]=type&filter[items][1][value]=admin
Root cause
The C# emitter does not implement "deep object" serialization for complex nested query-parameter types. It emits a single uri.AppendQuery(name, TypeFormatters.ConvertToString(value), ...) call, and TypeFormatters.ConvertToString has no branch for arbitrary models/collections of models, so it uses object.ToString().
Suggested next steps
Note: the JS emitter (@typespec/http-client-js) has the same underlying gap and remains tracked in #10973.
Describe the bug
Split out from #10973 to track the C# (
@typespec/http-client-csharp) portion specifically.When a
@queryparameter uses@query(#{explode: true})on a model type that contains complex/nested objects (e.g. arrays of objects), the generated C# client serializes the whole model via its.ToString()instead of expanding it into bracket-notation key/value pairs.Reproduction
TypeSpec
Generated C#
The generated
RestClientcalls:TypeFormatters.ConvertToString(filter)falls back tofilter.ToString(), which returns the type name.Actual behavior
Expected behavior
Deep-object (OpenAPI
style: deepObject, explode: true) serialization, e.g.:Root cause
The C# emitter does not implement "deep object" serialization for complex nested query-parameter types. It emits a single
uri.AppendQuery(name, TypeFormatters.ConvertToString(value), ...)call, andTypeFormatters.ConvertToStringhas no branch for arbitrary models/collections of models, so it usesobject.ToString().Suggested next steps
filter[items][0][field]=status).TypeFormatters.ConvertToString.explode: truein the C# emitter.Note: the JS emitter (
@typespec/http-client-js) has the same underlying gap and remains tracked in #10973.