Skip to content

Commit 3c25ca1

Browse files
committed
json: use source generation for JSON serialization
1 parent d25c50a commit 3c25ca1

File tree

9 files changed

+102
-46
lines changed

9 files changed

+102
-46
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace Atlassian.Bitbucket;
4+
5+
[JsonSourceGenerationOptions(
6+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
7+
PropertyNameCaseInsensitive = true
8+
)]
9+
[JsonSerializable(typeof(BitbucketTokenEndpointResponseJson))]
10+
[JsonSerializable(typeof(Cloud.UserInfo), TypeInfoPropertyName = "Cloud_UserInfo")]
11+
[JsonSerializable(typeof(DataCenter.UserInfo), TypeInfoPropertyName = "DataCenter_UserInfo")]
12+
[JsonSerializable(typeof(DataCenter.LoginOptions))]
13+
internal partial class BitbucketJsonSerializerContext : JsonSerializerContext
14+
{
15+
}

src/shared/Atlassian.Bitbucket/BitbucketOAuth2Client.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Net.Http;
4+
using System.Text.Json.Serialization;
45
using System.Threading;
56
using System.Threading.Tasks;
67
using GitCredentialManager;
@@ -42,7 +43,7 @@ protected override bool TryCreateTokenEndpointResult(string json, out OAuth2Toke
4243
// We override the token endpoint response parsing because the Bitbucket authority returns
4344
// the non-standard 'scopes' property for the list of scopes, rather than the (optional)
4445
// 'scope' (note the singular vs plural) property as outlined in the standard.
45-
if (TryDeserializeJson(json, out BitbucketTokenEndpointResponseJson jsonObj))
46+
if (TryDeserializeJson(json, BitbucketJsonSerializerContext.Default, out BitbucketTokenEndpointResponseJson jsonObj))
4647
{
4748
result = jsonObj.ToResult();
4849
return true;

src/shared/Atlassian.Bitbucket/Cloud/BitbucketRestApi.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,7 @@ public async Task<RestApiResult<IUserInfo>> GetUserInformationAsync(string userN
4343

4444
if (response.IsSuccessStatusCode)
4545
{
46-
var obj = JsonSerializer.Deserialize<UserInfo>(json,
47-
new JsonSerializerOptions
48-
{
49-
PropertyNameCaseInsensitive = true,
50-
});
46+
UserInfo obj = JsonSerializer.Deserialize(json, BitbucketJsonSerializerContext.Default.Cloud_UserInfo);
5147

5248
return new RestApiResult<IUserInfo>(response.StatusCode, obj);
5349
}

src/shared/Atlassian.Bitbucket/DataCenter/BitbucketRestApi.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,7 @@ public async Task<List<AuthenticationMethod>> GetAuthenticationMethodsAsync()
107107

108108
if (response.IsSuccessStatusCode)
109109
{
110-
var loginOptions = JsonSerializer.Deserialize<LoginOptions>(json,
111-
new JsonSerializerOptions
112-
{
113-
PropertyNameCaseInsensitive = true,
114-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
115-
});
110+
LoginOptions loginOptions = JsonSerializer.Deserialize(json, BitbucketJsonSerializerContext.Default.LoginOptions);
116111

117112
if (loginOptions.Results.Any(r => "LOGIN_FORM".Equals(r.Type)))
118113
{
@@ -151,4 +146,4 @@ private Uri ApiUri
151146
}
152147
}
153148
}
154-
}
149+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace GitCredentialManager.Authentication.OAuth.Json;
4+
5+
[JsonSourceGenerationOptions(
6+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
7+
PropertyNameCaseInsensitive = true
8+
)]
9+
[JsonSerializable(typeof(DeviceAuthorizationEndpointResponseJson))]
10+
[JsonSerializable(typeof(ErrorResponseJson))]
11+
[JsonSerializable(typeof(TokenEndpointResponseJson))]
12+
public partial class OAuthJsonSerializerContext : JsonSerializerContext
13+
{
14+
}

src/shared/Core/Authentication/OAuth/OAuth2Client.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Threading.Tasks;
77
using GitCredentialManager.Authentication.OAuth.Json;
88
using System.Text.Json;
9+
using System.Text.Json.Serialization;
910

1011
namespace GitCredentialManager.Authentication.OAuth
1112
{
@@ -213,7 +214,8 @@ public async Task<OAuth2DeviceCodeResult> GetDeviceCodeAsync(IEnumerable<string>
213214
{
214215
string json = await response.Content.ReadAsStringAsync();
215216

216-
if (response.IsSuccessStatusCode && TryDeserializeJson(json, out DeviceAuthorizationEndpointResponseJson jsonObj))
217+
if (response.IsSuccessStatusCode && TryDeserializeJson(json, OAuthJsonSerializerContext.Default,
218+
out DeviceAuthorizationEndpointResponseJson jsonObj))
217219
{
218220
return jsonObj.ToResult();
219221
}
@@ -321,12 +323,9 @@ public async Task<OAuth2TokenResult> GetTokenByDeviceCodeAsync(OAuth2DeviceCodeR
321323
return result;
322324
}
323325

324-
var error = JsonSerializer.Deserialize<ErrorResponseJson>(json, new JsonSerializerOptions
325-
{
326-
PropertyNameCaseInsensitive = true
327-
});
326+
TryDeserializeJson(json, OAuthJsonSerializerContext.Default, out ErrorResponseJson error);
328327

329-
switch (error.Error)
328+
switch (error?.Error)
330329
{
331330
case OAuth2Constants.DeviceAuthorization.Errors.AuthorizationPending:
332331
// Retry with the current polling interval value
@@ -358,7 +357,7 @@ public async Task<OAuth2TokenResult> GetTokenByDeviceCodeAsync(OAuth2DeviceCodeR
358357

359358
protected virtual bool TryCreateTokenEndpointResult(string json, out OAuth2TokenResult result)
360359
{
361-
if (TryDeserializeJson(json, out TokenEndpointResponseJson jsonObj))
360+
if (TryDeserializeJson(json, OAuthJsonSerializerContext.Default, out TokenEndpointResponseJson jsonObj))
362361
{
363362
result = jsonObj.ToResult();
364363
return true;
@@ -368,9 +367,9 @@ protected virtual bool TryCreateTokenEndpointResult(string json, out OAuth2Token
368367
return false;
369368
}
370369

371-
protected virtual bool TryCreateExceptionFromResponse(string json, out OAuth2Exception exception)
370+
private static bool TryCreateExceptionFromResponse(string json, out OAuth2Exception exception)
372371
{
373-
if (TryDeserializeJson(json, out ErrorResponseJson obj))
372+
if (TryDeserializeJson(json, OAuthJsonSerializerContext.Default, out ErrorResponseJson obj))
374373
{
375374
exception = obj.ToException();
376375
return true;
@@ -397,7 +396,7 @@ private HttpRequestMessage CreateRequestMessage(HttpMethod method, Uri requestUr
397396
return request;
398397
}
399398

400-
protected Exception CreateExceptionFromResponse(string json)
399+
private Exception CreateExceptionFromResponse(string json)
401400
{
402401
if (TryCreateExceptionFromResponse(json, out OAuth2Exception exception))
403402
{
@@ -410,11 +409,11 @@ protected Exception CreateExceptionFromResponse(string json)
410409
return new Trace2OAuth2Exception(_trace2, message, format);
411410
}
412411

413-
protected static bool TryDeserializeJson<T>(string json, out T obj)
412+
protected static bool TryDeserializeJson<T>(string json, JsonSerializerContext context, out T obj)
414413
{
415414
try
416415
{
417-
obj = JsonSerializer.Deserialize<T>(json);
416+
obj = (T)JsonSerializer.Deserialize(json, typeof(T), context);
418417
return true;
419418
}
420419
catch

src/shared/Core/Trace2Message.cs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,46 @@
66

77
namespace GitCredentialManager;
88

9+
public class Trace2EventEnumConverter : JsonStringEnumConverter<Trace2Event>
10+
{
11+
public Trace2EventEnumConverter()
12+
: base(JsonNamingPolicy.SnakeCaseLower, false) { }
13+
}
14+
15+
public class Trace2ProcessClassEnumConverter : JsonStringEnumConverter<Trace2ProcessClass>
16+
{
17+
public Trace2ProcessClassEnumConverter()
18+
: base(JsonNamingPolicy.SnakeCaseLower, false) { }
19+
}
20+
21+
[JsonSourceGenerationOptions(
22+
PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower,
23+
PropertyNameCaseInsensitive = true,
24+
Converters = new[]
25+
{
26+
typeof(Trace2EventEnumConverter),
27+
typeof(Trace2ProcessClassEnumConverter)
28+
}
29+
)]
30+
[JsonSerializable(typeof(VersionMessage))]
31+
[JsonSerializable(typeof(StartMessage))]
32+
[JsonSerializable(typeof(ExitMessage))]
33+
[JsonSerializable(typeof(ExitMessage))]
34+
[JsonSerializable(typeof(ChildStartMessage))]
35+
[JsonSerializable(typeof(ChildExitMessage))]
36+
[JsonSerializable(typeof(ErrorMessage))]
37+
[JsonSerializable(typeof(RegionEnterMessage))]
38+
[JsonSerializable(typeof(RegionLeaveMessage))]
39+
internal partial class Trace2JsonSerializerContext : JsonSerializerContext
40+
{
41+
}
42+
943
public abstract class Trace2Message
1044
{
1145
private const int SourceColumnMaxWidth = 23;
1246
private const string NormalPerfTimeFormat = "HH:mm:ss.ffffff";
1347

1448
protected const string EmptyPerformanceSpan = "| | | | ";
15-
protected static readonly JsonSerializerOptions JsonSerializerOptions = new()
16-
{
17-
PropertyNameCaseInsensitive = true,
18-
Converters = { new JsonStringEnumConverter(new SnakeCaseNamingPolicy()) }
19-
};
2049

2150
[JsonPropertyName("event")]
2251
[JsonPropertyOrder(1)]
@@ -194,7 +223,7 @@ public class VersionMessage : Trace2Message
194223

195224
public override string ToJson()
196225
{
197-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
226+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.VersionMessage);
198227
}
199228

200229
public override string ToNormalString()
@@ -230,7 +259,7 @@ public class StartMessage : Trace2Message
230259

231260
public override string ToJson()
232261
{
233-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
262+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.StartMessage);
234263
}
235264

236265
public override string ToNormalString()
@@ -266,7 +295,7 @@ public class ExitMessage : Trace2Message
266295

267296
public override string ToJson()
268297
{
269-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
298+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.ExitMessage);
270299
}
271300

272301
public override string ToNormalString()
@@ -314,7 +343,7 @@ public class ChildStartMessage : Trace2Message
314343

315344
public override string ToJson()
316345
{
317-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
346+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.ChildStartMessage);
318347
}
319348

320349
public override string ToNormalString()
@@ -371,7 +400,7 @@ public class ChildExitMessage : Trace2Message
371400

372401
public override string ToJson()
373402
{
374-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
403+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.ChildExitMessage);
375404
}
376405

377406
public override string ToNormalString()
@@ -415,7 +444,7 @@ public class ErrorMessage : Trace2Message
415444

416445
public override string ToJson()
417446
{
418-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
447+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.ErrorMessage);
419448
}
420449

421450
public override string ToNormalString()
@@ -473,7 +502,7 @@ public class RegionEnterMessage : RegionMessage
473502
{
474503
public override string ToJson()
475504
{
476-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
505+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.RegionEnterMessage);
477506
}
478507

479508
public override string ToNormalString()
@@ -504,7 +533,7 @@ public class RegionLeaveMessage : RegionMessage
504533

505534
public override string ToJson()
506535
{
507-
return JsonSerializer.Serialize(this, JsonSerializerOptions);
536+
return JsonSerializer.Serialize(this, Trace2JsonSerializerContext.Default.RegionLeaveMessage);
508537
}
509538

510539
public override string ToNormalString()
@@ -527,9 +556,3 @@ protected override string GetEventMessage(Trace2FormatTarget formatTarget)
527556
return Message;
528557
}
529558
}
530-
531-
public class SnakeCaseNamingPolicy : JsonNamingPolicy
532-
{
533-
public override string ConvertName(string name) =>
534-
name.ToSnakeCase();
535-
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace GitHub;
2+
3+
using System.Text.Json.Serialization;
4+
5+
[JsonSourceGenerationOptions(
6+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
7+
PropertyNameCaseInsensitive = true
8+
)]
9+
[JsonSerializable(typeof(GitHubUserInfo))]
10+
[JsonSerializable(typeof(GitHubMetaInfo))]
11+
internal partial class GitHubJsonSerializerContext : JsonSerializerContext
12+
{
13+
}

src/shared/GitHub/GitHubRestApi.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public async Task<GitHubUserInfo> GetUserInfoAsync(Uri targetUri, string accessT
106106

107107
string json = await response.Content.ReadAsStringAsync();
108108

109-
return JsonSerializer.Deserialize<GitHubUserInfo>(json);
109+
return JsonSerializer.Deserialize(json, GitHubJsonSerializerContext.Default.GitHubUserInfo);
110110
}
111111
}
112112
}
@@ -125,7 +125,7 @@ public async Task<GitHubMetaInfo> GetMetaInfoAsync(Uri targetUri)
125125

126126
string json = await response.Content.ReadAsStringAsync();
127127

128-
return JsonSerializer.Deserialize<GitHubMetaInfo>(json);
128+
return JsonSerializer.Deserialize(json, GitHubJsonSerializerContext.Default.GitHubMetaInfo);
129129
}
130130
}
131131

0 commit comments

Comments
 (0)