From c56484d922fbc9a3b6ba936d76a10529902e8f50 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:46:41 +0000 Subject: [PATCH 1/6] Initial plan From 10cebc34c0aab5bb50760930d442fac97291674c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:59:33 +0000 Subject: [PATCH 2/6] fix: preserve serialization format for customized properties with array encoding Fix PropertyWireInformation to handle array encoding metadata when constructing from InputProperty, matching PropertyProvider behavior. Also update MrwSerializationTypeDefinition to use WireInfo serialization format consistently for both serialization and deserialization paths. Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/f951d06e-92e6-49f6-9690-c013979802ff --- .../MrwSerializationTypeDefinition.cs | 4 +-- .../SerializationCustomizationTests.cs | 31 +++++++++++++++++++ .../Model.cs | 13 ++++++++ .../src/Primitives/PropertyWireInformation.cs | 15 ++++++++- .../test/common/InputFactory.cs | 6 ++-- 5 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty/Model.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index d67edf8103d..4159758ce6a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -1074,7 +1074,7 @@ private List BuildDeserializePropertiesStatements(ScopedApi var propertyExpression = parameter.Property?.AsVariableExpression ?? parameter.Field?.AsVariableExpression; var checkIfJsonPropEqualsName = new IfStatement(jsonProperty.NameEquals(propertySerializationName)) { - DeserializeProperty(propertyName!, propertyType!, wireInfo, propertyExpression!, jsonProperty, serializationAttributes, parameter.Property?.SerializationFormat) + DeserializeProperty(propertyName!, propertyType!, wireInfo, propertyExpression!, jsonProperty, serializationAttributes, wireInfo.SerializationFormat) }; propertyDeserializationStatements.Add(checkIfJsonPropEqualsName); } @@ -1754,7 +1754,7 @@ private MethodBodyStatement[] CreateWritePropertiesStatements(bool isDynamicMode continue; } - propertyStatements.Add(CreateWritePropertyStatement(property.WireInfo, property.Type, property.Name, property, property.SerializationFormat)); + propertyStatements.Add(CreateWritePropertyStatement(property.WireInfo, property.Type, property.Name, property, property.WireInfo.SerializationFormat)); } foreach (var field in _model.CanonicalView.Fields) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs index bc61fe5f334..738a50c5aa5 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs @@ -477,5 +477,36 @@ public async Task CanChangePropertyNameAndRedefineOriginal() var file = writer.Write(); Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); } + + [Test] + public async Task CanPreserveArrayEncodingForCustomizedProperty() + { + var props = new[] + { + InputFactory.Property("Prop1", InputFactory.Array(InputPrimitiveType.String), encode: ArrayKnownEncoding.CommaDelimited) + }; + + var inputModel = InputFactory.Model("Model", properties: props, usage: InputModelTypeUsage.Json); + var mockGenerator = await MockHelpers.LoadMockGeneratorAsync( + inputModels: () => [inputModel], + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var modelProvider = mockGenerator.Object.OutputLibrary.TypeProviders.Single(t => t is ModelProvider); + var serializationProvider = modelProvider.SerializationProviders.Single(t => t is MrwSerializationTypeDefinition); + Assert.IsNotNull(serializationProvider); + + // validate the custom property has the correct wire info serialization format + var customCodeView = modelProvider.CustomCodeView; + Assert.IsNotNull(customCodeView); + Assert.AreEqual(1, customCodeView!.Properties.Count); + Assert.AreEqual(SerializationFormat.Array_CommaDelimited, customCodeView.Properties[0].WireInfo?.SerializationFormat); + + // validate the serialization output uses the encoded array delimiter + var mrwSerialization = (MrwSerializationTypeDefinition)serializationProvider!; + var writeMethod = mrwSerialization.BuildJsonModelWriteCoreMethod(); + var methodBody = writeMethod.BodyStatements!.ToDisplayString(); + Assert.IsTrue(methodBody.Contains("string.Join(\",\", Prop2)"), + $"Expected serialization to use string.Join with comma delimiter for customized property, but got: {methodBody}"); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty/Model.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty/Model.cs new file mode 100644 index 00000000000..23bc9276356 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty/Model.cs @@ -0,0 +1,13 @@ + +using SampleTypeSpec; +using System.Collections.Generic; +using Microsoft.TypeSpec.Generator.Customizations; + +namespace Sample.Models +{ + public partial class Model + { + [CodeGenMember("Prop1")] + public IList Prop2 { get; internal set; } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs index b7a2e85b87a..78c45e0ee15 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs @@ -3,6 +3,7 @@ using System; using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Input.Extensions; using Microsoft.TypeSpec.Generator.Utilities; namespace Microsoft.TypeSpec.Generator.Primitives @@ -34,7 +35,7 @@ public PropertyWireInformation(SerializationFormat serializationFormat, bool isR /// /// The input model property. internal PropertyWireInformation(InputProperty inputProperty) - : base(CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty.Type), inputProperty.SerializedName) + : base(GetSerializationFormat(inputProperty), inputProperty.SerializedName) // TODO -- this is only temporary because we do not support other type of serialization, improvement tracking https://github.com/microsoft/typespec/issues/5861 { InputModelProperty? modelProperty = inputProperty as InputModelProperty; @@ -49,5 +50,17 @@ internal PropertyWireInformation(InputProperty inputProperty) ? CodeModelGenerator.Instance.TypeFactory.CreateSerializationOptions(modelProperty.SerializationOptions) : null; } + + private static SerializationFormat GetSerializationFormat(InputProperty inputProperty) + { + if (inputProperty is InputModelProperty modelProperty && + inputProperty.Type is InputArrayType && + modelProperty.Encode.HasValue) + { + return modelProperty.Encode.Value.ToSerializationFormat(); + } + + return CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty.Type); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs index 570585e3abe..2881aceeaa6 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs @@ -278,7 +278,8 @@ public static InputModelProperty Property( string? summary = null, string? serializedName = null, string? doc = null, - InputSerializationOptions? serializationOptions = null) + InputSerializationOptions? serializationOptions = null, + ArrayKnownEncoding? encode = null) { serializationOptions ??= new InputSerializationOptions(); return new InputModelProperty( @@ -294,7 +295,8 @@ public static InputModelProperty Property( access: null, isDiscriminator: isDiscriminator, serializedName: serializedName ?? wireName ?? name.ToVariableName(), - serializationOptions: serializationOptions); + serializationOptions: serializationOptions, + encode: encode); } public static InputHeaderParameter HeaderParameter( From 0b24f44f09f5a8a577ed649809dd80e38dbf8cb9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:14:01 +0000 Subject: [PATCH 3/6] refactor: use TestData file for CanPreserveArrayEncodingForCustomizedProperty assertions Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/4406d355-b4f4-43e9-8a9c-d89f13693c48 --- .../SerializationCustomizationTests.cs | 16 +-- ...serveArrayEncodingForCustomizedProperty.cs | 125 ++++++++++++++++++ 2 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs index 738a50c5aa5..04698677a33 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs @@ -494,19 +494,9 @@ public async Task CanPreserveArrayEncodingForCustomizedProperty() var modelProvider = mockGenerator.Object.OutputLibrary.TypeProviders.Single(t => t is ModelProvider); var serializationProvider = modelProvider.SerializationProviders.Single(t => t is MrwSerializationTypeDefinition); Assert.IsNotNull(serializationProvider); - - // validate the custom property has the correct wire info serialization format - var customCodeView = modelProvider.CustomCodeView; - Assert.IsNotNull(customCodeView); - Assert.AreEqual(1, customCodeView!.Properties.Count); - Assert.AreEqual(SerializationFormat.Array_CommaDelimited, customCodeView.Properties[0].WireInfo?.SerializationFormat); - - // validate the serialization output uses the encoded array delimiter - var mrwSerialization = (MrwSerializationTypeDefinition)serializationProvider!; - var writeMethod = mrwSerialization.BuildJsonModelWriteCoreMethod(); - var methodBody = writeMethod.BodyStatements!.ToDisplayString(); - Assert.IsTrue(methodBody.Contains("string.Join(\",\", Prop2)"), - $"Expected serialization to use string.Join with comma delimiter for customized property, but got: {methodBody}"); + var writer = new TypeProviderWriter(serializationProvider); + var file = writer.Write(); + Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs new file mode 100644 index 00000000000..d2553487aa1 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs @@ -0,0 +1,125 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; +using Sample; + +namespace Sample.Models +{ + public partial class Model : global::System.ClientModel.Primitives.IJsonModel + { + protected virtual global::Sample.Models.Model PersistableModelCreateCore(global::System.BinaryData data, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(data, global::Sample.ModelSerializationExtensions.JsonDocumentOptions)) + { + return global::Sample.Models.Model.DeserializeModel(document.RootElement, options); + } + default: + throw new global::System.FormatException($"The model {nameof(global::Sample.Models.Model)} does not support reading '{options.Format}' format."); + } + } + + protected virtual global::System.BinaryData PersistableModelWriteCore(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return global::System.ClientModel.Primitives.ModelReaderWriter.Write(this, options, global::Sample.SampleContext.Default); + default: + throw new global::System.FormatException($"The model {nameof(global::Sample.Models.Model)} does not support writing '{options.Format}' format."); + } + } + + global::System.BinaryData global::System.ClientModel.Primitives.IPersistableModel.Write(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => this.PersistableModelWriteCore(options); + + global::Sample.Models.Model global::System.ClientModel.Primitives.IPersistableModel.Create(global::System.BinaryData data, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => this.PersistableModelCreateCore(data, options); + + string global::System.ClientModel.Primitives.IPersistableModel.GetFormatFromOptions(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => "J"; + + void global::System.ClientModel.Primitives.IJsonModel.Write(global::System.Text.Json.Utf8JsonWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + this.JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if ((format != "J")) + { + throw new global::System.FormatException($"The model {nameof(global::Sample.Models.Model)} does not support writing '{format}' format."); + } + if (global::Sample.Optional.IsCollectionDefined(Prop2)) + { + writer.WritePropertyName("prop1"u8); + writer.WriteStringValue(string.Join(",", Prop2)); + } + if (((options.Format != "W") && (_additionalBinaryDataProperties != null))) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(item.Value)) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + global::Sample.Models.Model global::System.ClientModel.Primitives.IJsonModel.Create(ref global::System.Text.Json.Utf8JsonReader reader, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => this.JsonModelCreateCore(ref reader, options); + + protected virtual global::Sample.Models.Model JsonModelCreateCore(ref global::System.Text.Json.Utf8JsonReader reader, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if ((format != "J")) + { + throw new global::System.FormatException($"The model {nameof(global::Sample.Models.Model)} does not support reading '{format}' format."); + } + using global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.ParseValue(ref reader); + return global::Sample.Models.Model.DeserializeModel(document.RootElement, options); + } + + internal static global::Sample.Models.Model DeserializeModel(global::System.Text.Json.JsonElement element, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + if ((element.ValueKind == global::System.Text.Json.JsonValueKind.Null)) + { + return null; + } + global::System.Collections.Generic.IList prop2 = default; + global::System.Collections.Generic.IDictionary additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("prop1"u8)) + { + if ((prop.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null)) + { + continue; + } + string stringValue = prop.Value.GetString(); + prop2 = string.IsNullOrEmpty(stringValue) ? new global::System.Collections.Generic.List() : new global::System.Collections.Generic.List(stringValue.Split(',')); + continue; + } + if ((options.Format != "W")) + { + additionalBinaryDataProperties.Add(prop.Name, global::System.BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new global::Sample.Models.Model((prop2 ?? new global::Sample.ChangeTrackingList()), additionalBinaryDataProperties); + } + } +} From 20f833a8e82d160b029b8c94af4d641fd8a501eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:21:14 +0000 Subject: [PATCH 4/6] refactor: move GetSerializationFormat(InputProperty) to TypeFactory as internal method Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/c7ba895c-8352-4294-8e3f-a0bc7242c583 --- .../src/Primitives/PropertyWireInformation.cs | 15 +-------------- .../src/Providers/PropertyProvider.cs | 17 ++--------------- .../src/TypeFactory.cs | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs index 78c45e0ee15..603686c1c73 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/PropertyWireInformation.cs @@ -3,7 +3,6 @@ using System; using Microsoft.TypeSpec.Generator.Input; -using Microsoft.TypeSpec.Generator.Input.Extensions; using Microsoft.TypeSpec.Generator.Utilities; namespace Microsoft.TypeSpec.Generator.Primitives @@ -35,7 +34,7 @@ public PropertyWireInformation(SerializationFormat serializationFormat, bool isR /// /// The input model property. internal PropertyWireInformation(InputProperty inputProperty) - : base(GetSerializationFormat(inputProperty), inputProperty.SerializedName) + : base(CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty), inputProperty.SerializedName) // TODO -- this is only temporary because we do not support other type of serialization, improvement tracking https://github.com/microsoft/typespec/issues/5861 { InputModelProperty? modelProperty = inputProperty as InputModelProperty; @@ -50,17 +49,5 @@ internal PropertyWireInformation(InputProperty inputProperty) ? CodeModelGenerator.Instance.TypeFactory.CreateSerializationOptions(modelProperty.SerializationOptions) : null; } - - private static SerializationFormat GetSerializationFormat(InputProperty inputProperty) - { - if (inputProperty is InputModelProperty modelProperty && - inputProperty.Type is InputArrayType && - modelProperty.Encode.HasValue) - { - return modelProperty.Encode.Value.ToSerializationFormat(); - } - - return CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty.Type); - } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs index d6ffb0fa6ed..4e96d72f9dd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs @@ -336,20 +336,7 @@ public void Update( } } - private SerializationFormat GetSerializationFormat(InputProperty inputProperty) - { - // Handle array encoding from InputModelProperty - if (inputProperty is InputModelProperty modelProperty && - inputProperty.Type is InputArrayType) - { - var arrayEncoding = modelProperty.Encode; - if (arrayEncoding.HasValue) - { - return arrayEncoding.Value.ToSerializationFormat(); - } - } - - return CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty.Type); - } + private static SerializationFormat GetSerializationFormat(InputProperty inputProperty) + => CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty); } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs index 12c7fea1699..c5f46a399d5 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs @@ -385,6 +385,25 @@ or InputPrimitiveTypeKind.Int64 or InputPrimitiveTypeKind.UInt8 or InputPrimitiv _ => SerializationFormat.Default }; + /// + /// Retrieves the serialization format for a given input property. For array-typed properties + /// this checks the property-level before falling + /// back to . + /// + /// The to retrieve the serialization format for. + /// The for the input property. + internal SerializationFormat GetSerializationFormat(InputProperty inputProperty) + { + if (inputProperty is InputModelProperty modelProperty && + inputProperty.Type is InputArrayType && + modelProperty.Encode.HasValue) + { + return modelProperty.Encode.Value.ToSerializationFormat(); + } + + return GetSerializationFormat(inputProperty.Type); + } + /// /// The initialization type of list properties. This type should implement both and . /// From e000600e2676f095c4740b50cf655509f5733131 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:22:45 +0000 Subject: [PATCH 5/6] refactor: inline PropertyProvider.GetSerializationFormat wrapper Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/c7ba895c-8352-4294-8e3f-a0bc7242c583 --- .../src/Providers/PropertyProvider.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs index 4e96d72f9dd..6638e48c05a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PropertyProvider.cs @@ -92,7 +92,7 @@ private PropertyProvider(InputProperty inputProperty, CSharpType propertyType, T } EnclosingType = enclosingType; - _serializationFormat = GetSerializationFormat(inputProperty); + _serializationFormat = CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty); _isRequiredNonNullableConstant = inputProperty.IsRequired && propertyType is { IsLiteral: true, IsNullable: false }; var propHasSetter = PropertyHasSetter(propertyType, inputProperty); MethodSignatureModifiers setterModifier = propHasSetter ? MethodSignatureModifiers.Public : MethodSignatureModifiers.None; @@ -335,8 +335,5 @@ public void Update( BuildDocs(); } } - - private static SerializationFormat GetSerializationFormat(InputProperty inputProperty) - => CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty); } } From e388ae66a2c7d0b0c04c64d21b063bbd6b8ea179 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:32:27 +0000 Subject: [PATCH 6/6] test: filter to only validate DeserializeModel and JsonModelWriteCore methods Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/caf19890-4278-4815-b032-aa61e5630356 --- .../SerializationCustomizationTests.cs | 5 +- ...serveArrayEncodingForCustomizedProperty.cs | 59 +------------------ 2 files changed, 7 insertions(+), 57 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs index 04698677a33..4c28452d689 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs @@ -494,7 +494,10 @@ public async Task CanPreserveArrayEncodingForCustomizedProperty() var modelProvider = mockGenerator.Object.OutputLibrary.TypeProviders.Single(t => t is ModelProvider); var serializationProvider = modelProvider.SerializationProviders.Single(t => t is MrwSerializationTypeDefinition); Assert.IsNotNull(serializationProvider); - var writer = new TypeProviderWriter(serializationProvider); + + var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( + serializationProvider!, + name => name == "DeserializeModel" || name == "JsonModelWriteCore")); var file = writer.Write(); Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs index d2553487aa1..2d18b87d28f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanPreserveArrayEncodingForCustomizedProperty.cs @@ -6,52 +6,12 @@ using System.ClientModel.Primitives; using System.Collections.Generic; using System.Text.Json; -using Sample; +using Sample.Models; -namespace Sample.Models +namespace Sample { - public partial class Model : global::System.ClientModel.Primitives.IJsonModel + public partial class Model { - protected virtual global::Sample.Models.Model PersistableModelCreateCore(global::System.BinaryData data, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) - { - string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - switch (format) - { - case "J": - using (global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(data, global::Sample.ModelSerializationExtensions.JsonDocumentOptions)) - { - return global::Sample.Models.Model.DeserializeModel(document.RootElement, options); - } - default: - throw new global::System.FormatException($"The model {nameof(global::Sample.Models.Model)} does not support reading '{options.Format}' format."); - } - } - - protected virtual global::System.BinaryData PersistableModelWriteCore(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) - { - string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - switch (format) - { - case "J": - return global::System.ClientModel.Primitives.ModelReaderWriter.Write(this, options, global::Sample.SampleContext.Default); - default: - throw new global::System.FormatException($"The model {nameof(global::Sample.Models.Model)} does not support writing '{options.Format}' format."); - } - } - - global::System.BinaryData global::System.ClientModel.Primitives.IPersistableModel.Write(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => this.PersistableModelWriteCore(options); - - global::Sample.Models.Model global::System.ClientModel.Primitives.IPersistableModel.Create(global::System.BinaryData data, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => this.PersistableModelCreateCore(data, options); - - string global::System.ClientModel.Primitives.IPersistableModel.GetFormatFromOptions(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => "J"; - - void global::System.ClientModel.Primitives.IJsonModel.Write(global::System.Text.Json.Utf8JsonWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) - { - writer.WriteStartObject(); - this.JsonModelWriteCore(writer, options); - writer.WriteEndObject(); - } - protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) { string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; @@ -81,19 +41,6 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite } } - global::Sample.Models.Model global::System.ClientModel.Primitives.IJsonModel.Create(ref global::System.Text.Json.Utf8JsonReader reader, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => this.JsonModelCreateCore(ref reader, options); - - protected virtual global::Sample.Models.Model JsonModelCreateCore(ref global::System.Text.Json.Utf8JsonReader reader, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) - { - string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - if ((format != "J")) - { - throw new global::System.FormatException($"The model {nameof(global::Sample.Models.Model)} does not support reading '{format}' format."); - } - using global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.ParseValue(ref reader); - return global::Sample.Models.Model.DeserializeModel(document.RootElement, options); - } - internal static global::Sample.Models.Model DeserializeModel(global::System.Text.Json.JsonElement element, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) { if ((element.ValueKind == global::System.Text.Json.JsonValueKind.Null))