Repository navigation
Description
Summary
Generated serialization code reads and writes a nested model by calling that model's own implementation directly. So a ModelReaderWriter proxy registered for the nested type is never consulted, even when it is registered on the options passed in. For proxies to help libraries that substitute types nested inside another library's models, generated code has to route nested reads and writes through them. (System.ClientModel's proxy API is still a prototype: Azure/azure-sdk-for-net#59083.)
Current behavior
Generated code on main, from the Sample-TypeSpec test project:
-
Nested read: a static call to the child's deserializer (RoundTripModel.Serialization.cs#L411), emitted by
DeserializeJsonValueCore(MrwSerializationTypeDefinition.cs#L2655-L2683):requiredModel = Thing.DeserializeThing(prop.Value, options);
-
Nested write:
WriteObjectValue(RoundTripModel.Serialization.cs#L120, emitted at MrwSerializationTypeDefinition.cs#L2649), which calls the child's ownIJsonModel<T>.Write(ModelSerializationExtensions.cs#L245-L253):writer.WriteObjectValue(RequiredModel, options);
OpenAI's generated models follow the same pattern, for example StreamingResponseOutputItemAddedUpdate (read, write).
Scenario
Azure.AI.Extensions.OpenAI's ProjectResponsesClient derives from OpenAI's ResponsesClient. It converts Azure-specific response items and tools, which OpenAI's generated discriminator switch can't produce, into Azure types. Today that means overriding the response methods and post-processing their results (streaming override). For streaming, that requires either a hook in OpenAI (openai/openai-dotnet#1473) or Azure re-implementing event parsing.
If generated code honored nested proxies, Azure could register ResponseItem and ResponseTool proxies on its client options. OpenAI's own deserializer would then produce the Azure types in streaming updates and non-streaming results alike.
Request
When proxies are registered on the ModelReaderWriterOptions in use, generated code should resolve them for nested model properties and collection elements, on both the read and the write path. Today's static fast path should stay when no proxies are registered.
Related
- mrw proxy Azure/azure-sdk-for-net#59083: System.ClientModel proxy prototype (
ModelReaderWriterOptions.AddProxy<T>,ConditionalModelProxy<T>), closed pending API review. - Thread ModelReaderWriterOptions through generated clients #11390: draft that threads
ModelReaderWriterOptionsthrough generated clients and routes nested model reads through registered proxies. - [http-client-csharp] Preserve reader options when deserializing external models #11913: forwards the caller's options when deserializing models from another assembly (closed).
🤖 m-nash-copilot
Activity
github-actions commentedon Nov 3, 2026
🎯 Issue Triage
Generated C# serialization currently bypasses registered
ModelReaderWriterproxies for nested model properties and collection elements. This requests proxy-aware nested reads and writes while preserving the existing fast path when no proxies are registered.Category: Feature Request
Area:
emitter:client:csharp🔍 Analysis
The affected component is
MrwSerializationTypeDefinitionin the HTTP client C# generator, including nested scalar and collection serialization paths. Generated reads call the nested model's static deserializer directly, while writes call itsIJsonModel.Writeimplementation, so proxy resolution is skipped even though the enclosingModelReaderWriterOptionscarries registrations.#11390 is an open draft PR with substantial overlap: it threads options through generated clients and routes nested reads through proxies. #11913 fixed forwarding reader options for external models, but is related rather than the same request because it does not add nested proxy resolution or the write path.
The implementation depends on the provisional
System.ClientModelproxy API, so its final API shape may affect generator design.📝 Suggested next steps
System.ClientModelproxy API.