Summary
Generated deserializers for properties whose model types come from another assembly hardcode ModelSerializationExtensions.WireOptions instead of forwarding the caller's ModelReaderWriterOptions. This silently drops unknown nested JSON properties during a JSON-persistence round-trip.
Found while reviewing Azure/azure-sdk-for-net#62269: Azure/azure-sdk-for-net#62269 (comment).
Generator evidence
In the unbranded C# generator, MrwSerializationTypeDefinition.DeserializeJsonValueCore accepts mrwOptions, but its fallback at lines 2518-2521 emits:
return ModelReaderWriterSnippets.Read(
valueType,
data,
ModelSerializationExtensionsSnippets.Wire);
The Azure generator delegates deserialization of these external model types to the base generator; this is not an SDK-specific customization issue.
For example, after externalizing AzureAISearchToolOptions, the generated AzureAISearchToolboxTool deserializer changed from:
azureAiSearch = AzureAISearchToolOptions.DeserializeAzureAISearchToolOptions(prop.Value, options);
to:
azureAiSearch = ModelReaderWriter.Read<AzureAISearchToolOptions>(
prop.Value.GetUtf8Bytes(),
ModelSerializationExtensions.WireOptions,
AzureAIProjectsAgentsContext.Default);
The same pattern occurs in nested collection/dictionary model reads, including ToolConfig in this serializer.
Reproduction
Reproduced against Azure/azure-sdk-for-net commit 6e80fb90609cf3f60b4a602c0337f17a81729982, targeting .NET 10, with Azure.AI.Extensions.OpenAI 3.0.0-alpha.20260908.3, OpenAI 2.12.0, and System.ClientModel 1.15.0.
using System;
using System.ClientModel.Primitives;
using Azure.AI.Projects.Agents;
var input = BinaryData.FromString(
"""{"type":"azure_ai_search","name":"search","azure_ai_search":{"indexes":[],"new_option":true}}""");
var model = ModelReaderWriter.Read<AzureAISearchToolboxTool>(
input, ModelReaderWriterOptions.Json, AzureAIProjectsAgentsContext.Default);
var saved = ModelReaderWriter.Write(
model, ModelReaderWriterOptions.Json, AzureAIProjectsAgentsContext.Default);
Console.WriteLine(saved.ToString());
Actual output:
{"type":"azure_ai_search","name":"search","azure_ai_search":{"indexes":[]}}
new_option is lost even though both outer operations use ModelReaderWriterOptions.Json. The nested model preserves unknown properties only outside wire mode, so forcing W prevents it from retaining that data.
Expected behavior
Forward the enclosing deserializer's options/mrwOptions into nested ModelReaderWriter.Read calls. JSON persistence should preserve unknown nested properties; wire-mode callers should retain their existing wire behavior.
Please cover external models in scalar properties, lists, and dictionaries, including unknown nested properties under JSON mode and unchanged behavior under wire mode.
Related but distinct: #9800 covered forwarding options to custom deserialization hooks, not external-model ModelReaderWriter.Read calls.
Summary
Generated deserializers for properties whose model types come from another assembly hardcode
ModelSerializationExtensions.WireOptionsinstead of forwarding the caller'sModelReaderWriterOptions. This silently drops unknown nested JSON properties during a JSON-persistence round-trip.Found while reviewing Azure/azure-sdk-for-net#62269: Azure/azure-sdk-for-net#62269 (comment).
Generator evidence
In the unbranded C# generator,
MrwSerializationTypeDefinition.DeserializeJsonValueCoreacceptsmrwOptions, but its fallback at lines 2518-2521 emits:The Azure generator delegates deserialization of these external model types to the base generator; this is not an SDK-specific customization issue.
For example, after externalizing
AzureAISearchToolOptions, the generatedAzureAISearchToolboxTooldeserializer changed from:to:
The same pattern occurs in nested collection/dictionary model reads, including
ToolConfigin this serializer.Reproduction
Reproduced against Azure/azure-sdk-for-net commit
6e80fb90609cf3f60b4a602c0337f17a81729982, targeting .NET 10, withAzure.AI.Extensions.OpenAI3.0.0-alpha.20260908.3,OpenAI2.12.0, andSystem.ClientModel1.15.0.Actual output:
{"type":"azure_ai_search","name":"search","azure_ai_search":{"indexes":[]}}new_optionis lost even though both outer operations useModelReaderWriterOptions.Json. The nested model preserves unknown properties only outside wire mode, so forcingWprevents it from retaining that data.Expected behavior
Forward the enclosing deserializer's
options/mrwOptionsinto nestedModelReaderWriter.Readcalls. JSON persistence should preserve unknown nested properties; wire-mode callers should retain their existing wire behavior.Please cover external models in scalar properties, lists, and dictionaries, including unknown nested properties under JSON mode and unchanged behavior under wire mode.
Related but distinct: #9800 covered forwarding options to custom deserialization hooks, not external-model
ModelReaderWriter.Readcalls.