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

Newtonsoft package updates #787

Merged
merged 3 commits into from
Dec 8, 2023
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
15 changes: 11 additions & 4 deletions src/Flurl.Http.Newtonsoft/ExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Flurl.Http.Newtonsoft
public static class ExtensionMethods
{
/// <summary>
/// Deserializes JSON-formatted HTTP response body to a dynamic object.
/// Deserializes a JSON-formatted HTTP response body to a dynamic object.
/// </summary>
/// <returns>A Task whose result is a dynamic object containing data in the response body.</returns>
public static async Task<dynamic> GetJsonAsync(this IFlurlResponse resp) {
Expand All @@ -22,7 +22,7 @@ public static async Task<dynamic> GetJsonAsync(this IFlurlResponse resp) {
}

/// <summary>
/// Deserializes JSON-formatted HTTP response body to a list of dynamic objects.
/// Deserializes a JSON-formatted HTTP response body to a list of dynamic objects.
/// </summary>
/// <returns>A Task whose result is a list of dynamic objects containing data in the response body.</returns>
public static async Task<IList<dynamic>> GetJsonListAsync(this IFlurlResponse resp) {
Expand All @@ -31,7 +31,7 @@ public static async Task<IList<dynamic>> GetJsonListAsync(this IFlurlResponse re
}

/// <summary>
/// Deserializes JSON-formatted HTTP response body to a dynamic object. Intended to chain off an async call.
/// Deserializes a JSON-formatted HTTP response body to a dynamic object. Intended to chain off an async call.
/// </summary>
/// <returns>A Task whose result is a dynamic object containing data in the response body.</returns>
public static async Task<dynamic> ReceiveJson(this Task<IFlurlResponse> response) {
Expand All @@ -41,7 +41,7 @@ public static async Task<dynamic> ReceiveJson(this Task<IFlurlResponse> response
}

/// <summary>
/// Deserializes JSON-formatted HTTP response body to a list of dynamic objects. Intended to chain off an async call.
/// Deserializes a JSON-formatted HTTP response body to a list of dynamic objects. Intended to chain off an async call.
/// </summary>
/// <returns>A Task whose result is a list of dynamic objects containing data in the response body.</returns>
public static async Task<IList<dynamic>> ReceiveJsonList(this Task<IFlurlResponse> response) {
Expand All @@ -50,6 +50,13 @@ public static async Task<IList<dynamic>> ReceiveJsonList(this Task<IFlurlRespons
return await resp.GetJsonListAsync().ConfigureAwait(false);
}

/// <summary>
/// Deserializes a JSON-formatted error response body to a dynamic object.
/// </summary>
/// <returns>A Task whose result is a dynamic object containing data in the response body.</returns>
public static async Task<dynamic> GetResponseJsonAsync(this FlurlHttpException flurlException) =>
(flurlException.Call?.Response == null) ? null : await flurlException.Call.Response.GetJsonAsync().ConfigureAwait(false);

/// <summary>
/// Shortcut to use NewtonsoftJsonSerializer with this IFlurlClientBuilder.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Flurl.Http.Newtonsoft/Flurl.Http.Newtonsoft.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<LangVersion>9.0</LangVersion>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>Flurl.Http.Newtonsoft</PackageId>
<Version>0.9.0-pre2</Version>
<Version>0.9.0-pre3</Version>
<Authors>Todd Menier</Authors>
<Description>A Newtonsoft-based JSON serializer for Flurl.Http 4.0 and above.</Description>
<PackageProjectUrl>https://flurl.dev</PackageProjectUrl>
Expand All @@ -13,6 +13,7 @@
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageTags>flurl http json</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>https://github.com/tmenier/Flurl/releases</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Expand All @@ -26,6 +27,7 @@

<ItemGroup>
<None Include="..\..\icon.png" Pack="true" PackagePath="\" />
<None Include="README.md" Pack="true" PackagePath="\" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<ProjectReference Include="..\Flurl.Http\Flurl.Http.csproj" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
Expand Down
21 changes: 21 additions & 0 deletions src/Flurl.Http.Newtonsoft/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Flurl.Http.Newtonsoft

[![NuGet version (Flurl.Http.Newtonsoft)](https://img.shields.io/nuget/v/Flurl.Http.Newtonsoft.svg?style=flat-square)](https://www.nuget.org/packages/Flurl.Http.Newtonsoft/)

Flurl.Http 4.0 [removed](https://github.com/tmenier/Flurl/issues/517) the `Newtonsoft.Json` dependency in favor of `System.Text.Json` for its default JSON serializer implementation. This has several advantages, most notably that it dramatically reduces Flurl's footprint, which is especially important to developers using it in mobile apps and browser platforms.

But it's also a breaking change. Upgrading from 3.x may cause some pains, such as:

- Newtonsoft's [serialization attributes](https://www.newtonsoft.com/json/help/html/serializationattributes.htm) no longer have any effect, and need be replaced by their STJ equivalents.
- Newtonsoft's [configuration settings](https://www.newtonsoft.com/json/help/html/serializationsettings.htm) also no longer have any effect in Flurl.
- Support for `dynamic`s has also been [removed](https://github.com/tmenier/Flurl/issues/699) in Flurl, specifically due to the lack of support in STJ.

This package aims to solve these problems, making upgrading easier for those who still want or need Newtonsoft-based serialization. Included in this package:

- `NewtonsoftJsonSearializer`, an instance of which can be assigned to a Flurl client or request via `Settings.JsonSerializer`.
- Shortcuts for enabling the serializer more "globally":
- When using the clientless pattern: `FlurlHttp.Clients.UseNewtonsoft()`
- When using DI / [named clients](https://github.com/tmenier/Flurl/issues/770): `new FlurlClientCache().UseNewtonsoft()`
- Non-generic `dynamic`-returning `GetJson`, `GetJsonList`, `ReceiveJson`, and `ReceiveJsonList` extension methods.

Both `NewtonsoftJsonSerializer` and the `UseNewtonsoft` shortcuts take an optional `JsonSerializerSettings` parameter. In fact, the serializer was lifted directly from 3.x, so you can trust that it's battle-tested and highly performant.
28 changes: 28 additions & 0 deletions test/Flurl.Test/Http/NewtonsoftTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Net.Http;
using NUnit.Framework;
using Flurl.Http;
using Flurl.Http.Newtonsoft;
using Flurl.Http.Testing;
using System.Threading.Tasks;
using Flurl.Http.Configuration;
using Newtonsoft.Json;
using NUnit.Framework.Constraints;

namespace Flurl.Test.Http
{
Expand Down Expand Up @@ -55,6 +57,32 @@ public async Task null_response_returns_null_dynamic() {

var list = await resp.ReceiveJsonList();
Assert.IsNull(list);

var ex = new FlurlHttpException(new FlurlCall {
Request = new FlurlRequest(),
HttpRequestMessage = new HttpRequestMessage(),
Response = null,
});
var err = await ex.GetResponseJsonAsync();
Assert.IsNull(err);
}

[TestCase(false)]
[TestCase(true)]
public async Task can_get_error_json_untyped(bool useShortcut) {
HttpTest.RespondWithJson(new { code = 999, message = "our server crashed" }, 500);

try {
await "http://api.com".GetStringAsync();
}
catch (FlurlHttpException ex) {
var error = useShortcut ? // error is a dynamic this time
await ex.GetResponseJsonAsync() :
await ex.Call.Response.GetJsonAsync();
Assert.IsNotNull(error);
Assert.AreEqual(999, error.code);
Assert.AreEqual("our server crashed", error.message);
}
}
}

Expand Down