From 9d57d0843d18a933cdfedccc208fe7e1bcae8e2c Mon Sep 17 00:00:00 2001 From: haiyuan_zhang Date: Wed, 11 Mar 2026 02:04:06 +0800 Subject: [PATCH 1/2] Fix dictionary deserialization crash on $-prefixed keys Add TryReadComplexType overload for IReadOnlyDictionary that manually reads dictionary entries via Utf8JsonReader, bypassing the default System.Text.Json dictionary converter which rejects property names starting with '$' when ReferenceHandler is enabled. This fixes a crash when generating SDKs from specs with OData parameters (e.g. \) that appear as dictionary keys in example values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Serialization/Utf8JsonReaderExtensions.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs index f02980b787d..12e488d7c9c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs @@ -146,6 +146,37 @@ public static bool TryReadComplexType(this ref Utf8JsonReader reader, string return true; } + public static bool TryReadComplexType(this ref Utf8JsonReader reader, string propertyName, JsonSerializerOptions options, ref IReadOnlyDictionary? value) + { + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + if (reader.GetString() != propertyName) + { + return false; + } + + reader.Read(); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + reader.Read(); + var result = new Dictionary(); + while (reader.TokenType != JsonTokenType.EndObject) + { + var key = reader.GetString() ?? throw new JsonException("Dictionary key cannot be null"); + reader.Read(); + var item = reader.ReadWithConverter(options); + result[key] = item ?? throw new JsonException(); + } + reader.Read(); + value = result; + return true; + } + public static T? ReadWithConverter(this ref Utf8JsonReader reader, JsonSerializerOptions options) { var converter = (JsonConverter)options.GetConverter(typeof(T)); From 03ff4cf576bb0652fb8943de302d362961cee285 Mon Sep 17 00:00:00 2001 From: haiyuan_zhang Date: Wed, 11 Mar 2026 02:49:24 +0800 Subject: [PATCH 2/2] Add test for $-prefixed dictionary keys in example values Add a \ key to the existing PutStorageTask example test data and assert it deserializes correctly. Without the fix, this reproduces the JsonException: 'Properties that start with \$ are not allowed in types that support metadata.' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../LoadOperationExamples/tspCodeModel.json | 8 ++++++++ .../test/TypeSpecInputExampleConverterTests.cs | 1 + 2 files changed, 9 insertions(+) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputExampleConverterTests/LoadOperationExamples/tspCodeModel.json b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputExampleConverterTests/LoadOperationExamples/tspCodeModel.json index 8de71bb388c..034dc234913 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputExampleConverterTests/LoadOperationExamples/tspCodeModel.json +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputExampleConverterTests/LoadOperationExamples/tspCodeModel.json @@ -6080,6 +6080,14 @@ "$ref": "203" }, "value": { + "$filter": { + "$id": "2000", + "kind": "string", + "type": { + "$ref": "209" + }, + "value": "status eq 'Active'" + }, "description": { "$id": "471", "kind": "string", diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputExampleConverterTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputExampleConverterTests.cs index b7892554d22..d12d272cfcd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputExampleConverterTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputExampleConverterTests.cs @@ -103,6 +103,7 @@ static void AssertStorageTaskClientExample(InputClient client) { "location", "westus" }, { "properties.description", "My Storage task" }, { "properties.enabled", true }, + { "properties.$filter", "status eq 'Active'" }, { "properties.action.if.condition", "[[equals(AccessTier, 'Cool')]]" }, { "properties.action.if.operations[0].name", "SetBlobTier" }, { "properties.action.if.operations[0].onFailure", "break" },