Skip to content

Commit

Permalink
restsharp: Use System.Text.Json deserializer
Browse files Browse the repository at this point in the history
  • Loading branch information
chyyran committed Aug 18, 2021
1 parent f8a1961 commit d6d1fd4
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 18 deletions.
1 change: 0 additions & 1 deletion GiantBomb.Api.Tests/GiantBomb.Api.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
Expand Down
2 changes: 0 additions & 2 deletions GiantBomb.Api.Tests/Releases.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;

namespace GiantBomb.Api.Tests {
Expand Down
33 changes: 21 additions & 12 deletions GiantBomb.Api/Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using GiantBomb.Api.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using GiantBomb.Api.Serialization;
using RestSharp;
using RestSharp.Serializers.NewtonsoftJson;
using RestSharp.Serializers.SystemTextJson;

namespace GiantBomb.Api
{
Expand Down Expand Up @@ -49,14 +49,16 @@ public GiantBombRestClient(string apiToken, Uri baseUrl)
// API token is used on every request
_client.AddDefaultParameter("api_key", ApiKey);
_client.AddDefaultParameter("format", "json");
_client.UseNewtonsoftJson(new JsonSerializerSettings
var options = new System.Text.Json.JsonSerializerOptions()
{
DateFormatString = "yyyy-MM-dd HH:mm:ss",
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
});
PropertyNamingPolicy = SnakeCaseNamingPolicy.SnakeCase,
DictionaryKeyPolicy = SnakeCaseNamingPolicy.SnakeCase,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
};

options.Converters.Add(new DateTimeConverter());
options.Converters.Add(new BoolConverter());
_client.UseSystemTextJson(options);
}

public GiantBombRestClient(string apiToken)
Expand Down Expand Up @@ -95,17 +97,24 @@ public virtual async Task<T> ExecuteAsync<T>(RestRequest request) where T : new(
}

// Deserialize original requested type
T data = default(T);
IRestResponse<T> deserializedResponse = null;
Exception deserializeEx = null;
try
{
data = _client.Deserialize<T>(response).Data;
deserializedResponse = _client.Deserialize<T>(response);
}
catch (Exception dex)
{
deserializeEx = dex;
}

// Check ErrorException
if (deserializedResponse.ErrorException != null)
{
deserializeEx = deserializedResponse.ErrorException;
}

T data = deserializedResponse.Data;
if (data == null)
{
// handle GiantBomb raw errors without result wrapper
Expand Down
6 changes: 3 additions & 3 deletions GiantBomb.Api/GiantBomb.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<AssemblyName>GiantBomb.Api</AssemblyName>
<RootNamespace>GiantBomb.Api</RootNamespace>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>2.5.0</Version>
<Version>2.6.0</Version>
<Authors>kayub</Authors>
<Company>kayub</Company>
<Product>GiantBomb API (C#)</Product>
Expand All @@ -25,8 +25,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="RestSharp" Version="106.11.7" />
<PackageReference Include="RestSharp.Serializers.NewtonsoftJson" Version="106.11.7" />
<PackageReference Include="RestSharp" Version="106.12.0" />
<PackageReference Include="RestSharp.Serializers.SystemTextJson" Version="106.12.0" />
</ItemGroup>

<ItemGroup>
Expand Down
40 changes: 40 additions & 0 deletions GiantBomb.Api/Serialization/BoolConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GiantBomb.Api.Serialization
{
internal class BoolConverter : JsonConverter<bool>
{
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// return literal boolean
if (reader.TokenType == JsonTokenType.True || reader.TokenType == JsonTokenType.False)
{
return reader.GetBoolean();
}

// string encoded booleans
if (reader.TokenType == JsonTokenType.String)
{
string boolString = reader.GetString();
if (bool.TryParse(boolString, out bool result))
{
return result;
}

return Convert.ToInt32(boolString) != 0;
}

// final fallback to number, expected to throw if this doesn't work.
return Convert.ToInt32(reader.GetInt32()) != 0;
}

public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
{
// we don't need to write anything.
throw new NotSupportedException();
}
}

}
22 changes: 22 additions & 0 deletions GiantBomb.Api/Serialization/DateTimeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GiantBomb.Api.Serialization
{
internal class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}

public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss"));
}
}

}
87 changes: 87 additions & 0 deletions GiantBomb.Api/Serialization/SnakeCaseNamingPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Text;
using System.Text.Json;

namespace GiantBomb.Api.Serialization
{
internal class SnakeCaseNamingPolicy : JsonNamingPolicy
{
internal static SnakeCaseNamingPolicy SnakeCase { get; } = new SnakeCaseNamingPolicy();

// SnakeCase implementation copied from Newtonsoft.Json
private enum SeparatedCaseState
{
Start,
Lower,
Upper,
NewWord
}

private static string ToSeparatedCase(string s, char separator)
{
if (string.IsNullOrEmpty(s))
{
return s;
}

StringBuilder sb = new StringBuilder();
SeparatedCaseState state = SeparatedCaseState.Start;

for (int i = 0; i < s.Length; i++)
{
if (s[i] == ' ')
{
if (state != SeparatedCaseState.Start)
{
state = SeparatedCaseState.NewWord;
}
}
else if (char.IsUpper(s[i]))
{
switch (state)
{
case SeparatedCaseState.Upper:
bool hasNext = (i + 1 < s.Length);
if (i > 0 && hasNext)
{
char nextChar = s[i + 1];
if (!char.IsUpper(nextChar) && nextChar != separator)
{
sb.Append(separator);
}
}
break;
case SeparatedCaseState.Lower:
case SeparatedCaseState.NewWord:
sb.Append(separator);
break;
}

char c;
c = char.ToLowerInvariant(s[i]);
sb.Append(c);

state = SeparatedCaseState.Upper;
}
else if (s[i] == separator)
{
sb.Append(separator);
state = SeparatedCaseState.Start;
}
else
{
if (state == SeparatedCaseState.NewWord)
{
sb.Append(separator);
}

sb.Append(s[i]);
state = SeparatedCaseState.Lower;
}
}

return sb.ToString();
}

public override string ConvertName(string name) => ToSeparatedCase(name, '_');
}
}

0 comments on commit d6d1fd4

Please sign in to comment.