Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Newtonsoft.Json with the new .Net JsonSerializer. #3414

Merged
merged 16 commits into from
Sep 16, 2020
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
6 changes: 3 additions & 3 deletions Microsoft.Toolkit.Services/Microsoft.Toolkit.Services.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
This .NET standard library enables access to different data sources such as Microsoft Graph, OneDrive, Twitter, Microsoft Translator, and LinkedIn. It is part of the Windows Community Toolkit.
</Description>
<PackageTags>UWP Community Toolkit Windows Microsoft Graph OneDrive Twitter Translator LinkedIn service login OAuth</PackageTags>

<LangVersion>8.0</LangVersion>
<NoWarn>CS8002;CS0618</NoWarn>
<DeterministicSourcePaths Condition="'$(EnableSourceLink)' == ''">false</DeterministicSourcePaths>
</PropertyGroup>
Expand All @@ -27,7 +27,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.Text.Json" Version="4.7.2" />
michael-hawker marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

Expand All @@ -50,4 +50,4 @@
<ItemGroup Condition="!('$(TargetFramework)'=='uap10.0.16299')">
<Compile Remove="PlatformSpecific\Uwp\**\*" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="Microsoft.Toolkit.Services">
<Namespace Name="System.Text.Json.Serialization.Converters" Dynamic="Required All"/>
</Library>
</Directives>
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
using System.Diagnostics;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
using Newtonsoft.Json.Linq;

#if WINRT
using Microsoft.Toolkit.Services.PlatformSpecific.Uwp;
Expand Down Expand Up @@ -81,7 +81,8 @@ public LinkedInDataProvider(LinkedInOAuthTokens tokens, LinkedInPermissions requ
throw new ArgumentException("Missing callback uri");
}

if (!Enum.IsDefined(typeof(LinkedInPermissions), requiredPermissions))
// Check if its a valid combination of LinkedInPermissions
if ((~(int)LinkedInPermissionsHelpers.AllPermissions & (int)requiredPermissions) != 0)
{
throw new ArgumentException("Error retrieving required permissions");
}
Expand Down Expand Up @@ -186,22 +187,18 @@ public async Task<IEnumerable<TSchema>> GetDataAsync<TSchema>(LinkedInDataConfig

var url = $"{_baseUrl}{config.Query}/~:({fields})?oauth2_access_token={Tokens.AccessToken}&format=json&count={maxRecords}&start={startRecord}";

using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(url)))
{
request.Headers.Connection.TryParseAdd("Keep-Alive");

using (var response = await client.SendAsync(request).ConfigureAwait(false))
{
var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(url));
request.Headers.Connection.TryParseAdd("Keep-Alive");

if (response.IsSuccessStatusCode && !string.IsNullOrEmpty(data))
{
return parser.Parse(data);
}
using var response = await client.SendAsync(request).ConfigureAwait(false);
var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

throw new RequestFailedException((System.Net.HttpStatusCode)response.StatusCode, data);
}
if (response.IsSuccessStatusCode && !string.IsNullOrEmpty(data))
{
return parser.Parse(data);
azchohfi marked this conversation as resolved.
Show resolved Hide resolved
}

throw new RequestFailedException((System.Net.HttpStatusCode)response.StatusCode, data);
}

/// <summary>
Expand All @@ -222,22 +219,18 @@ public async Task<U> ShareDataAsync<T, U>(T dataToShare)

var url = $"{_baseUrl}/people/~/shares?oauth2_access_token={Tokens.AccessToken}&format=json";

using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(url)))
{
request.Headers.Add("x-li-format", "json");
var stringContent = requestParser.Parse(shareRequest);
request.Content = new StringContent(stringContent, Encoding.UTF8, "application/json");
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(url));
request.Headers.Add("x-li-format", "json");
var stringContent = requestParser.Parse(shareRequest);
request.Content = new StringContent(stringContent, Encoding.UTF8, "application/json");

using (var response = await client.SendAsync(request).ConfigureAwait(false))
{
var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
using var response = await client.SendAsync(request).ConfigureAwait(false);
var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

var responseParser = new LinkedInParser<U>();
var responseParser = new LinkedInParser<U>();

var listResults = responseParser.Parse(data) as List<U>;
return listResults[0];
}
}
var listResults = responseParser.Parse(data) as List<U>;
return listResults[0];
}

return default(U);
Expand All @@ -263,15 +256,13 @@ private async Task<string> GetAccessTokenAsync(LinkedInOAuthTokens tokens, strin
+ "&client_id=" + tokens.ClientId
+ "&client_secret=" + tokens.ClientSecret;

using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri(url)))
{
using (var response = await client.SendAsync(request).ConfigureAwait(false))
{
var jsonString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var json = JObject.Parse(jsonString);
return json.GetValue("access_token").Value<string>();
}
}
using var request = new HttpRequestMessage(HttpMethod.Post, new Uri(url));
using var response = await client.SendAsync(request).ConfigureAwait(false);
using var jsonStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var jsonDoc = await JsonDocument.ParseAsync(jsonStream).ConfigureAwait(false);
azchohfi marked this conversation as resolved.
Show resolved Hide resolved

var value = jsonDoc.RootElement.GetProperty("access_token");
return value.GetString();
}

private async Task<string> GetAuthorizeCodeAsync(LinkedInOAuthTokens tokens, LinkedInPermissions permissions)
Expand Down
10 changes: 5 additions & 5 deletions Microsoft.Toolkit.Services/Services/LinkedIn/LinkedInParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text.Json;

namespace Microsoft.Toolkit.Services.LinkedIn
{
Expand All @@ -24,11 +24,11 @@ public IEnumerable<T> Parse(string data)

try
{
results = JsonConvert.DeserializeObject<List<T>>(data);
results = JsonSerializer.Deserialize<List<T>>(data);
}
catch (JsonSerializationException)
catch (JsonException)
{
T linkedInResult = JsonConvert.DeserializeObject<T>(data);
T linkedInResult = JsonSerializer.Deserialize<T>(data);
results = new List<T> { linkedInResult };
}

Expand All @@ -42,7 +42,7 @@ public IEnumerable<T> Parse(string data)
/// <returns>Returns string data.</returns>
public string Parse(T dataToShare)
{
return JsonConvert.SerializeObject(dataToShare, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
return JsonSerializer.Serialize(dataToShare, typeof(T), new JsonSerializerOptions { IgnoreNullValues = true });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,18 @@ public enum LinkedInPermissions
/// </summary>
WriteShare = 8
}

#pragma warning disable SA1649 // File name should match first type name
internal static class LinkedInPermissionsHelpers
{
/// <summary>
/// Internal AllPermissions for LinkedInPermissions, so we don't expose it. Keep it in sync with <see cref="LinkedInPermissions"/>
/// </summary>
internal const LinkedInPermissions AllPermissions =
LinkedInPermissions.ReadBasicProfile |
LinkedInPermissions.ReadEmailAddress |
LinkedInPermissions.ReadWriteCompanyAdmin |
LinkedInPermissions.WriteShare;
}
#pragma warning restore SA1649 // File name should match first type name
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
Expand All @@ -24,6 +24,9 @@ internal class AzureAuthToken
/// </summary>
private static readonly Uri ServiceUrl = new Uri("https://api.cognitive.microsoft.com/sts/v1.0/issueToken");

// TODO
// private static readonly Uri ServiceUrl = new Uri(THIS SHOULD BE A PARAMETER NOW);

/// <summary>
/// After obtaining a valid token, this class will cache it for this duration.
/// Use a duration of 8 minutes, which is less than the actual token lifetime of 10 minutes.
Expand Down Expand Up @@ -90,24 +93,22 @@ public async Task<string> GetAccessTokenAsync()
return _storedTokenValue;
}

using (var request = new HttpRequestMessage(HttpMethod.Post, ServiceUrl))
{
request.Headers.Add(OcpApimSubscriptionKeyHeader, SubscriptionKey);
using var request = new HttpRequestMessage(HttpMethod.Post, ServiceUrl);
request.Headers.Add(OcpApimSubscriptionKeyHeader, SubscriptionKey);

var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

if (!response.IsSuccessStatusCode)
{
var error = JsonConvert.DeserializeObject<ErrorResponse>(content);
throw new TranslatorServiceException(error.Message);
}
if (!response.IsSuccessStatusCode)
{
var error = JsonSerializer.Deserialize<ErrorResponse>(content);
throw new TranslatorServiceException(error?.Error?.Message);
}

_storedTokenTime = DateTime.Now;
_storedTokenValue = $"Bearer {content}";
_storedTokenTime = DateTime.Now;
_storedTokenValue = $"Bearer {content}";

return _storedTokenValue;
}
return _storedTokenValue;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text.Json.Serialization;

namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
#pragma warning disable SA1402 // File may only contain a single type
/// <summary>
/// Holds information about an error occurred while accessing Microsoft Translator Service.
/// </summary>
internal class ErrorResponse
{
[JsonPropertyName("error")]
public Error Error { get; set; }
}

internal class Error
{
/// <summary>
/// Gets or sets the error message.
/// </summary>
[JsonPropertyName("message")]
public string Message { get; set; }

/// <summary>
/// Gets or sets the HTTP status code.
/// </summary>
public int StatusCode { get; set; }
}
#pragma warning restore SA1402 // File may only contain a single type
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
Expand Down Expand Up @@ -30,7 +30,7 @@ public class ServiceLanguage
/// <summary>
/// Gets the directionality, which is rtl for right-to-left languages or ltr for left-to-right languages.
/// </summary>
[JsonProperty("dir")]
[JsonPropertyName("dir")]
public string Directionality { get; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
Expand Down Expand Up @@ -108,14 +107,13 @@ public async Task<IEnumerable<DetectedLanguageResponse>> DetectLanguagesWithResp
await CheckUpdateTokenAsync().ConfigureAwait(false);

var uriString = $"{BaseUrl}detect?{ApiVersion}";
using (var request = CreateHttpRequest(uriString, HttpMethod.Post, input.Select(t => new { Text = t.Substring(0, Math.Min(t.Length, _MaxTextLengthForDetection)) })))
{
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
using var request = CreateHttpRequest(uriString, HttpMethod.Post, input.Select(t => new { Text = t.Substring(0, Math.Min(t.Length, _MaxTextLengthForDetection)) }));

var responseContent = JsonConvert.DeserializeObject<IEnumerable<DetectedLanguageResponse>>(content);
return responseContent;
}
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

var responseContent = JsonSerializer.Deserialize<IEnumerable<DetectedLanguageResponse>>(content);
return responseContent;
}

/// <inheritdoc/>
Expand All @@ -132,24 +130,23 @@ public async Task<IEnumerable<ServiceLanguage>> GetLanguageNamesAsync(string lan
await CheckUpdateTokenAsync().ConfigureAwait(false);

var uriString = $"{BaseUrl}languages?scope=translation&{ApiVersion}";
using (var request = CreateHttpRequest(uriString))
using var request = CreateHttpRequest(uriString);

language = language ?? Language;
if (!string.IsNullOrWhiteSpace(language))
{
language = language ?? Language;
if (!string.IsNullOrWhiteSpace(language))
{
// If necessary, adds the Accept-Language header in order to get localized language names.
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
}
// If necessary, adds the Accept-Language header in order to get localized language names.
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
}

var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

var jsonContent = JToken.Parse(content)["translation"];
var responseContent = JsonConvert.DeserializeObject<Dictionary<string, ServiceLanguage>>(jsonContent.ToString()).ToList();
responseContent.ForEach(r => r.Value.Code = r.Key);
var jsonContent = JsonDocument.Parse(content).RootElement.GetProperty("translation");
var responseContent = JsonSerializer.Deserialize<Dictionary<string, ServiceLanguage>>(jsonContent.ToString()).ToList();
responseContent.ForEach(r => r.Value.Code = r.Key);

return responseContent.Select(r => r.Value).OrderBy(r => r.Name).ToList();
}
return responseContent.Select(r => r.Value).OrderBy(r => r.Name).ToList();
}

/// <inheritdoc/>
Expand Down Expand Up @@ -216,14 +213,13 @@ public async Task<IEnumerable<TranslationResponse>> TranslateWithResponseAsync(I

var toQueryString = string.Join("&", to.Select(t => $"to={t}"));
var uriString = (string.IsNullOrWhiteSpace(from) ? $"{BaseUrl}translate?{toQueryString}" : $"{BaseUrl}translate?from={from}&{toQueryString}") + $"&{ApiVersion}";
using (var request = CreateHttpRequest(uriString, HttpMethod.Post, input.Select(t => new { Text = t })))
{
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
using var request = CreateHttpRequest(uriString, HttpMethod.Post, input.Select(t => new { Text = t }));

var responseContent = JsonConvert.DeserializeObject<IEnumerable<TranslationResponse>>(content);
return responseContent;
}
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

var responseContent = JsonSerializer.Deserialize<IEnumerable<TranslationResponse>>(content);
return responseContent;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -254,7 +250,7 @@ private HttpRequestMessage CreateHttpRequest(string uriString, HttpMethod method

if (content != null)
{
var jsonRequest = JsonConvert.SerializeObject(content);
var jsonRequest = JsonSerializer.Serialize(content);
var requestContent = new StringContent(jsonRequest, System.Text.Encoding.UTF8, JsonMediaType);
request.Content = requestContent;
}
Expand Down
Loading