Skip to content

Commit 37f084c

Browse files
refactor: add json serialization options (#9621)
Adds json serialization options to PropertyWireInformation and updates references to SerializedName to use the serialization options instead. fixes: #5861
1 parent 9be6023 commit 37f084c

File tree

10 files changed

+130
-61
lines changed

10 files changed

+130
-61
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.TypeSpec.Generator.Input;
5+
6+
namespace Microsoft.TypeSpec.Generator.ClientModel.Primitives
7+
{
8+
/// <summary>
9+
/// Represents JSON serialization options for a property or model.
10+
/// </summary>
11+
public class JsonSerialization
12+
{
13+
public JsonSerialization(InputJsonSerializationOptions options)
14+
{
15+
Name = options.Name;
16+
}
17+
18+
/// <summary>
19+
/// Gets or sets the serialized name for JSON format.
20+
/// </summary>
21+
public string Name { get; internal set; }
22+
}
23+
}

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmSerializationOptions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ public class ScmSerializationOptions : SerializationOptions
1010
{
1111
public ScmSerializationOptions(InputSerializationOptions inputSerializationOptions) : base()
1212
{
13+
Json = inputSerializationOptions.Json != null
14+
? new(inputSerializationOptions.Json)
15+
: null;
1316
Xml = inputSerializationOptions.Xml != null
1417
? new(inputSerializationOptions.Xml)
1518
: null;
1619
}
1720

18-
public XmlSerialization? Xml { get; }
21+
public JsonSerialization? Json { get; }
1922

20-
// TO-DO: Add remaining SCM serialization options https://github.com/microsoft/typespec/issues/5861.
23+
public XmlSerialization? Xml { get; }
2124
}
2225
}

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/XmlSerialization.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
namespace Microsoft.TypeSpec.Generator.ClientModel.Primitives
77
{
8+
/// <summary>
9+
/// Represents XML serialization options for a property or model.
10+
/// </summary>
811
public class XmlSerialization
912
{
1013
public XmlSerialization(InputXmlSerializationOptions xmlOptions)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Dynamic.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -309,15 +309,15 @@ private MethodBodyStatement[] BuildDynamicPropertyIfStatements(
309309

310310
// Add direct dynamic properties
311311
var dynamicProperties = properties.Where(p =>
312-
p.WireInfo?.SerializedName != null &&
312+
p.WireInfo != null &&
313313
ScmCodeModelGenerator.Instance.TypeFactory.CSharpTypeMap.TryGetValue(p.Type, out var provider) &&
314314
provider is ScmModelProvider { JsonPatchProperty: not null });
315315
allDynamicProperties.AddRange(dynamicProperties);
316316

317317
// Add dynamic collection properties
318318
var dynamicCollectionProperties = properties
319319
.Where(p => p.Type.IsCollection &&
320-
p.WireInfo?.SerializedName != null &&
320+
p.WireInfo != null &&
321321
ScmCodeModelGenerator.Instance.TypeFactory.CSharpTypeMap.TryGetValue(
322322
p.Type.GetNestedElementType(),
323323
out var provider) &&
@@ -333,16 +333,17 @@ private MethodBodyStatement[] BuildDynamicPropertyIfStatements(
333333
foreach (var property in allDynamicProperties)
334334
{
335335
var patchProperty = ((MemberExpression)property).Property("Patch").As<JsonPatch>();
336+
var jsonSerializedName = GetJsonSerializedName(property.WireInfo!);
336337
statements.Add(
337-
new IfStatement(localVariable.Invoke("StartsWith", LiteralU8(property.WireInfo!.SerializedName)))
338+
new IfStatement(localVariable.Invoke("StartsWith", LiteralU8(jsonSerializedName)))
338339
{
339340
propagateGet
340341
? Return(patchProperty.TryGetEncodedValue(
341342
IndexerExpression.FromCollection(
342343
Spread(LiteralU8("$")),
343344
Spread(ReadOnlySpanSnippets.Slice(
344345
localVariable,
345-
LiteralU8(property.WireInfo!.SerializedName).Property("Length")))),
346+
LiteralU8(jsonSerializedName).Property("Length")))),
346347
valueParameter))
347348
: new MethodBodyStatement[]
348349
{
@@ -351,7 +352,7 @@ private MethodBodyStatement[] BuildDynamicPropertyIfStatements(
351352
Spread(LiteralU8("$")),
352353
Spread(ReadOnlySpanSnippets.Slice(
353354
localVariable,
354-
LiteralU8(property.WireInfo!.SerializedName).Property("Length")))),
355+
LiteralU8(jsonSerializedName).Property("Length")))),
355356
valueParameter),
356357
Return(True)
357358
}
@@ -363,7 +364,7 @@ private MethodBodyStatement[] BuildDynamicPropertyIfStatements(
363364
var indexableProperty = new IndexableExpression(property);
364365
statements.Add(
365366
new IfStatement(
366-
localVariable.Invoke("StartsWith", LiteralU8(property.WireInfo!.SerializedName)))
367+
localVariable.Invoke("StartsWith", LiteralU8(GetJsonSerializedName(property.WireInfo!))))
367368
{
368369
BuildCollectionIfStatements(
369370
property,
@@ -393,7 +394,7 @@ private static MethodBodyStatement[] BuildCollectionIfStatements(
393394
ValueExpression? remainderSlice = null;
394395

395396
statements.Add(Declare("propertyLength", typeof(int),
396-
LiteralU8(property.WireInfo!.SerializedName).Property("Length"),
397+
LiteralU8(GetJsonSerializedName(property.WireInfo!)).Property("Length"),
397398
out var propertyLength));
398399

399400
statements.Add(Declare("currentSlice", typeof(ReadOnlySpan<byte>),

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Text;
1212
using System.Text.Json;
1313
using System.Xml.Linq;
14+
using Microsoft.TypeSpec.Generator.ClientModel.Primitives;
1415
using Microsoft.TypeSpec.Generator.ClientModel.Snippets;
1516
using Microsoft.TypeSpec.Generator.ClientModel.Utilities;
1617
using Microsoft.TypeSpec.Generator.EmitterRpc;
@@ -595,10 +596,10 @@ private static MethodBodyStatement BuildDiscriminatedModelsCondition(
595596
bool onlyContainsUnknownDerivedModel,
596597
ScopedApi<JsonElement> jsonElementParameterSnippet)
597598
{
598-
if (!onlyContainsUnknownDerivedModel && discriminatorProperty?.WireInfo?.SerializedName != null)
599+
if (!onlyContainsUnknownDerivedModel && discriminatorProperty?.WireInfo != null)
599600
{
600601
return new IfStatement(jsonElementParameterSnippet.TryGetProperty(
601-
discriminatorProperty.WireInfo.SerializedName,
602+
GetJsonSerializedName(discriminatorProperty.WireInfo),
602603
out var discriminator))
603604
{
604605
new SwitchStatement(discriminator.GetString(), abstractSwitchCases)
@@ -1001,7 +1002,7 @@ private List<MethodBodyStatement> BuildDeserializePropertiesStatements(ScopedApi
10011002
{
10021003
continue;
10031004
}
1004-
var propertySerializationName = wireInfo.SerializedName;
1005+
var propertySerializationName = GetJsonSerializedName(wireInfo);
10051006
var propertyName = parameter.Property?.Name ?? parameter.Field?.Name;
10061007
var propertyType = parameter.Property?.Type ?? parameter.Field?.Type;
10071008
var propertyExpression = parameter.Property?.AsVariableExpression ?? parameter.Field?.AsVariableExpression;
@@ -1647,6 +1648,7 @@ private MethodBodyStatement[] CreateWritePropertiesStatements(bool isDynamicMode
16471648
{
16481649
continue;
16491650
}
1651+
16501652
propertyStatements.Add(CreateWritePropertyStatement(property.WireInfo, property.Type, property.Name, property, property.WireInfo?.SerializationFormat));
16511653
}
16521654

@@ -1656,6 +1658,7 @@ private MethodBodyStatement[] CreateWritePropertiesStatements(bool isDynamicMode
16561658
{
16571659
continue;
16581660
}
1661+
16591662
propertyStatements.Add(CreateWritePropertyStatement(field.WireInfo, field.Type, field.Name, field, field.WireInfo?.SerializationFormat));
16601663
}
16611664

@@ -1695,7 +1698,7 @@ private MethodBodyStatement CreateWritePropertyStatement(
16951698
MemberExpression propertyExpression,
16961699
SerializationFormat? serializationFormat)
16971700
{
1698-
var propertySerializationName = wireInfo.SerializedName;
1701+
var propertySerializationName = GetJsonSerializedName(wireInfo);
16991702
var propertySerializationFormat = wireInfo.SerializationFormat;
17001703
var propertyIsReadOnly = wireInfo.IsReadOnly;
17011704
var propertyIsRequired = wireInfo.IsRequired;
@@ -1750,7 +1753,7 @@ private MethodBodyStatement CreateWritePropertyStatement(
17501753
var wrapInIsDefinedStatement = WrapInIsDefined(
17511754
propertyExpression,
17521755
propertyType,
1753-
wireInfo,
1756+
propertySerializationName,
17541757
propertyIsRequired,
17551758
propertyIsReadOnly,
17561759
propertyIsNullable,
@@ -1762,15 +1765,15 @@ private MethodBodyStatement CreateWritePropertyStatement(
17621765
private MethodBodyStatement WrapInIsDefined(
17631766
MemberExpression propertyExpression,
17641767
CSharpType propertyType,
1765-
PropertyWireInformation wireInfo,
1768+
string jsonSerializedName,
17661769
bool propertyIsRequired,
17671770
bool propertyIsReadOnly,
17681771
bool propertyIsNullable,
17691772
MethodBodyStatement writePropertySerializationStatement)
17701773
{
17711774
#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
17721775
ScopedApi<bool>? patchCheck = _jsonPatchProperty != null
1773-
? Not(_jsonPatchProperty.As<JsonPatch>().Contains(LiteralU8($"$.{wireInfo.SerializedName}")))
1776+
? Not(_jsonPatchProperty.As<JsonPatch>().Contains(LiteralU8($"$.{jsonSerializedName}")))
17741777
: null;
17751778
#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
17761779

@@ -1783,7 +1786,7 @@ private MethodBodyStatement WrapInIsDefined(
17831786
return writePropertySerializationStatement;
17841787

17851788
return (propertyType.IsList || propertyType.IsArray)
1786-
? CreateConditionalPatchSerializationStatement(wireInfo.SerializedName, null, writePropertySerializationStatement, writePropertySerializationStatement)
1789+
? CreateConditionalPatchSerializationStatement(jsonSerializedName, null, writePropertySerializationStatement, writePropertySerializationStatement)
17871790
: new IfStatement(patchCheck) { writePropertySerializationStatement };
17881791
}
17891792

@@ -1794,7 +1797,7 @@ private MethodBodyStatement WrapInIsDefined(
17941797
propertyIsReadOnly,
17951798
propertyIsNullable,
17961799
propertyIsRequired,
1797-
wireInfo.SerializedName,
1800+
jsonSerializedName,
17981801
patchCheck,
17991802
writePropertySerializationStatement);
18001803
}
@@ -2504,6 +2507,11 @@ private static bool TypeRequiresNullCheckInSerialization(CSharpType type)
25042507
return false;
25052508
}
25062509

2510+
private static string GetJsonSerializedName(PropertyWireInformation wireInfo)
2511+
{
2512+
return (wireInfo.SerializationOptions as ScmSerializationOptions)?.Json?.Name ?? wireInfo.SerializedName;
2513+
}
2514+
25072515
internal static ValueExpression GetDeserializationMethodInvocationForType(
25082516
CSharpType modelType,
25092517
ScopedApi element,

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmModelProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,15 +308,15 @@ private bool BuildHasDynamicProperties()
308308
{
309309
var propertiesWithWireInfo = CanonicalView.Properties;
310310
if (propertiesWithWireInfo.Any(p =>
311-
p.WireInfo?.SerializedName != null &&
311+
p.WireInfo != null &&
312312
ScmCodeModelGenerator.Instance.TypeFactory.CSharpTypeMap.TryGetValue(p.Type, out var provider) &&
313313
provider is ScmModelProvider { IsDynamicModel: true }))
314314
{
315315
return true;
316316
}
317317

318318
return propertiesWithWireInfo
319-
.Where(p => p.Type.IsCollection && p.WireInfo?.SerializedName != null)
319+
.Where(p => p.Type.IsCollection && p.WireInfo != null)
320320
.Any(p => ScmCodeModelGenerator.Instance.TypeFactory.CSharpTypeMap.TryGetValue(
321321
p.Type.GetNestedElementType(),
322322
out var provider) &&

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/DynamicModelSerializationTests.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -962,14 +962,17 @@ public void WriteDerivedModel()
962962
[Test]
963963
public void DeserializeMultiplePrimitiveProperties()
964964
{
965+
var fooSerializationOptions = InputFactory.Serialization.Json("foo");
966+
var catSerializationOptions = InputFactory.Serialization.Json("x-cat");
967+
var barSerializationOptions = InputFactory.Serialization.Json("bar");
965968
var inputModel = InputFactory.Model(
966969
"dynamicModel",
967970
isDynamicModel: true,
968971
properties:
969972
[
970-
InputFactory.Property("foo", InputPrimitiveType.String, isRequired: true),
971-
InputFactory.Property("cat", InputPrimitiveType.String, serializedName: "x-cat", isRequired: true),
972-
InputFactory.Property("bar", InputPrimitiveType.Int32, isRequired: false)
973+
InputFactory.Property("foo", InputPrimitiveType.String, isRequired: true, serializationOptions: InputFactory.Serialization.Options(fooSerializationOptions)),
974+
InputFactory.Property("cat", InputPrimitiveType.String, isRequired: true, serializationOptions: InputFactory.Serialization.Options(catSerializationOptions)),
975+
InputFactory.Property("bar", InputPrimitiveType.Int32, isRequired: false, serializationOptions: InputFactory.Serialization.Options(barSerializationOptions))
973976
]);
974977

975978
MockHelpers.LoadMockGenerator(inputModels: () => [inputModel]);

0 commit comments

Comments
 (0)