Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ private List<MethodBodyStatement> 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);
}
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,5 +477,29 @@ 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);

var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider(
serializationProvider!,
name => name == "DeserializeModel" || name == "JsonModelWriteCore"));
var file = writer.Write();
Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// <auto-generated/>

#nullable disable

using System;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Text.Json;
using Sample.Models;

namespace Sample
{
public partial class Model
{
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<global::Sample.Models.Model>)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
}
}
}

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<string> prop2 = default;
global::System.Collections.Generic.IDictionary<string, global::System.BinaryData> additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary<string, global::System.BinaryData>();
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<string>() : new global::System.Collections.Generic.List<string>(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<string>()), additionalBinaryDataProperties);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<string> Prop2 { get; internal set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public PropertyWireInformation(SerializationFormat serializationFormat, bool isR
/// </summary>
/// <param name="inputProperty">The input model property.</param>
internal PropertyWireInformation(InputProperty inputProperty)
: base(CodeModelGenerator.Instance.TypeFactory.GetSerializationFormat(inputProperty.Type), 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -335,21 +335,5 @@ public void Update(
BuildDocs();
}
}

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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,25 @@ or InputPrimitiveTypeKind.Int64 or InputPrimitiveTypeKind.UInt8 or InputPrimitiv
_ => SerializationFormat.Default
};

/// <summary>
/// Retrieves the serialization format for a given input property. For array-typed properties
/// this checks the property-level <see cref="InputModelProperty.Encode"/> before falling
/// back to <see cref="GetSerializationFormat(InputType)"/>.
/// </summary>
/// <param name="inputProperty">The <see cref="InputProperty"/> to retrieve the serialization format for.</param>
/// <returns>The <see cref="SerializationFormat"/> for the input property.</returns>
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);
}

/// <summary>
/// The initialization type of list properties. This type should implement both <see cref="IList{T}"/> and <see cref="IReadOnlyList{T}"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand Down
Loading