Skip to content

[Bug]: C# client emits wrong query string for @query(#{explode: true}) on nested object models #11123

Description

@JoshLove-msft

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

  • Decide on the target serialization format for nested query objects (bracket notation filter[items][0][field]=status).
  • Generate proper serialization code for complex/exploded query-parameter types instead of calling TypeFormatters.ConvertToString.
  • Add Spector/mock-api coverage for nested object models with explode: true in the C# emitter.

Note: the JS emitter (@typespec/http-client-js) has the same underlying gap and remains tracked in #10973.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingemitter:client:csharpIssue for the C# client emitter: @typespec/http-client-csharp

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions