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

[release/6.0-preview4] [JSON source gen 3/3] Add new methods to JsonSerializer and System.Net.Http.Json APIs that take type metadata #51598

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ namespace System.Net.Http.Json
public static partial class HttpClientJsonExtensions
{
public static System.Threading.Tasks.Task<object?> GetFromJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, System.Type type, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<object?> GetFromJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, System.Type type, System.Text.Json.Serialization.JsonSerializerContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<object?> GetFromJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, System.Type type, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<object?> GetFromJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Type type, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<object?> GetFromJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Type type, System.Text.Json.Serialization.JsonSerializerContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<object?> GetFromJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Type type, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<TValue?> GetFromJsonAsync<TValue>(this System.Net.Http.HttpClient client, string? requestUri, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<TValue?> GetFromJsonAsync<TValue>(this System.Net.Http.HttpClient client, string? requestUri, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<TValue?> GetFromJsonAsync<TValue>(this System.Net.Http.HttpClient client, string? requestUri, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<TValue?> GetFromJsonAsync<TValue>(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<TValue?> GetFromJsonAsync<TValue>(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<TValue?> GetFromJsonAsync<TValue>(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage> PostAsJsonAsync<TValue>(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage> PostAsJsonAsync<TValue>(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Threading.CancellationToken cancellationToken) { throw null; }
Expand All @@ -28,7 +32,9 @@ public static partial class HttpClientJsonExtensions
public static partial class HttpContentJsonExtensions
{
public static System.Threading.Tasks.Task<object?> ReadFromJsonAsync(this System.Net.Http.HttpContent content, System.Type type, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<object?> ReadFromJsonAsync(this System.Net.Http.HttpContent content, System.Type type, System.Text.Json.Serialization.JsonSerializerContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<T?> ReadFromJsonAsync<T>(this System.Net.Http.HttpContent content, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task<T?> ReadFromJsonAsync<T>(this System.Net.Http.HttpContent content, System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
public sealed partial class JsonContent : System.Net.Http.HttpContent
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -56,6 +58,50 @@ public static partial class HttpClientJsonExtensions
return GetFromJsonAsyncCore<TValue>(taskResponse, options, cancellationToken);
}

public static Task<object?> GetFromJsonAsync(this HttpClient client, string? requestUri, Type type, JsonSerializerContext context, CancellationToken cancellationToken = default)
{
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}

Task<HttpResponseMessage> taskResponse = client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
return GetFromJsonAsyncCore(taskResponse, type, context, cancellationToken);
}

public static Task<object?> GetFromJsonAsync(this HttpClient client, Uri? requestUri, Type type, JsonSerializerContext context, CancellationToken cancellationToken = default)
{
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}

Task<HttpResponseMessage> taskResponse = client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
return GetFromJsonAsyncCore(taskResponse, type, context, cancellationToken);
}

public static Task<TValue?> GetFromJsonAsync<TValue>(this HttpClient client, string? requestUri, JsonTypeInfo<TValue> jsonTypeInfo, CancellationToken cancellationToken = default)
{
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}

Task<HttpResponseMessage> taskResponse = client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
return GetFromJsonAsyncCore(taskResponse, jsonTypeInfo, cancellationToken);
}

public static Task<TValue?> GetFromJsonAsync<TValue>(this HttpClient client, Uri? requestUri, JsonTypeInfo<TValue> jsonTypeInfo, CancellationToken cancellationToken = default)
{
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}

Task<HttpResponseMessage> taskResponse = client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
return GetFromJsonAsyncCore<TValue>(taskResponse, jsonTypeInfo, cancellationToken);
}

public static Task<object?> GetFromJsonAsync(this HttpClient client, string? requestUri, Type type, CancellationToken cancellationToken = default)
=> client.GetFromJsonAsync(requestUri, type, options: null, cancellationToken);

Expand Down Expand Up @@ -91,5 +137,23 @@ public static partial class HttpClientJsonExtensions
return await response.Content!.ReadFromJsonAsync<T>(options, cancellationToken).ConfigureAwait(false);
}
}

private static async Task<object?> GetFromJsonAsyncCore(Task<HttpResponseMessage> taskResponse, Type type, JsonSerializerContext context, CancellationToken cancellationToken)
{
using (HttpResponseMessage response = await taskResponse.ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
return await response.Content!.ReadFromJsonAsync(type, context, cancellationToken).ConfigureAwait(false);
}
}

private static async Task<T?> GetFromJsonAsyncCore<T>(Task<HttpResponseMessage> taskResponse, JsonTypeInfo<T> jsonTypeInfo, CancellationToken cancellationToken)
{
using (HttpResponseMessage response = await taskResponse.ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
return await response.Content!.ReadFromJsonAsync<T>(jsonTypeInfo, cancellationToken).ConfigureAwait(false);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -37,21 +39,61 @@ public static partial class HttpContentJsonExtensions

private static async Task<object?> ReadFromJsonAsyncCore(HttpContent content, Type type, Encoding? sourceEncoding, JsonSerializerOptions? options, CancellationToken cancellationToken)
{
Stream contentStream = await ReadHttpContentStreamAsync(content, cancellationToken).ConfigureAwait(false);
using (Stream contentStream = await GetContentStream(content, sourceEncoding, cancellationToken).ConfigureAwait(false))
{
return await JsonSerializer.DeserializeAsync(contentStream, type, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
}
}

// Wrap content stream into a transcoding stream that buffers the data transcoded from the sourceEncoding to utf-8.
if (sourceEncoding != null && sourceEncoding != Encoding.UTF8)
private static async Task<T?> ReadFromJsonAsyncCore<T>(HttpContent content, Encoding? sourceEncoding, JsonSerializerOptions? options, CancellationToken cancellationToken)
{
using (Stream contentStream = await GetContentStream(content, sourceEncoding, cancellationToken).ConfigureAwait(false))
{
contentStream = GetTranscodingStream(contentStream, sourceEncoding);
return await JsonSerializer.DeserializeAsync<T>(contentStream, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
}
}

using (contentStream)
public static Task<object?> ReadFromJsonAsync(this HttpContent content, Type type, JsonSerializerContext context, CancellationToken cancellationToken = default)
{
if (content == null)
{
return await JsonSerializer.DeserializeAsync(contentStream, type, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
throw new ArgumentNullException(nameof(content));
}

Encoding? sourceEncoding = JsonContent.GetEncoding(content.Headers.ContentType?.CharSet);

return ReadFromJsonAsyncCore(content, type, sourceEncoding, context, cancellationToken);
}

private static async Task<T?> ReadFromJsonAsyncCore<T>(HttpContent content, Encoding? sourceEncoding, JsonSerializerOptions? options, CancellationToken cancellationToken)
public static Task<T?> ReadFromJsonAsync<T>(this HttpContent content, JsonTypeInfo<T> jsonTypeInfo, CancellationToken cancellationToken = default)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}

Encoding? sourceEncoding = JsonContent.GetEncoding(content.Headers.ContentType?.CharSet);

return ReadFromJsonAsyncCore<T>(content, sourceEncoding, jsonTypeInfo, cancellationToken);
}

private static async Task<object?> ReadFromJsonAsyncCore(HttpContent content, Type type, Encoding? sourceEncoding, JsonSerializerContext context, CancellationToken cancellationToken)
{
using (Stream contentStream = await GetContentStream(content, sourceEncoding, cancellationToken).ConfigureAwait(false))
{
return await JsonSerializer.DeserializeAsync(contentStream, type, context, cancellationToken).ConfigureAwait(false);
}
}

private static async Task<T?> ReadFromJsonAsyncCore<T>(HttpContent content, Encoding? sourceEncoding, JsonTypeInfo<T> jsonTypeInfo, CancellationToken cancellationToken)
{
using (Stream contentStream = await GetContentStream(content, sourceEncoding, cancellationToken).ConfigureAwait(false))
{
return await JsonSerializer.DeserializeAsync<T>(contentStream, jsonTypeInfo, cancellationToken).ConfigureAwait(false);
}
}

private static async Task<Stream> GetContentStream(HttpContent content, Encoding? sourceEncoding, CancellationToken cancellationToken)
{
Stream contentStream = await ReadHttpContentStreamAsync(content, cancellationToken).ConfigureAwait(false);

Expand All @@ -61,10 +103,7 @@ public static partial class HttpContentJsonExtensions
contentStream = GetTranscodingStream(contentStream, sourceEncoding);
}

using (contentStream)
{
return await JsonSerializer.DeserializeAsync<T>(contentStream, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
}
return contentStream;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Xunit;
using System.Collections.Generic;
using System.Linq;
using System.Net.Test.Common;
using System.Text.Json;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace System.Net.Http.Json.Functional.Tests
{
public class HttpClientJsonExtensionsTests
{
[Theory]
[MemberData(nameof(ReadFromJsonTestData))]
public async Task TestGetFromJsonAsync(string json)
public async Task TestGetFromJsonAsync(string json, bool containsQuotedNumbers)
{
HttpHeaderData header = new HttpHeaderData("Content-Type", "application/json");
List<HttpHeaderData> headers = new List<HttpHeaderData> { header };
Expand All @@ -36,6 +36,21 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(

per = await client.GetFromJsonAsync<Person>(uri.ToString());
per.Validate();

if (!containsQuotedNumbers)
{
per = (Person)await client.GetFromJsonAsync(uri, typeof(Person), JsonContext.Default);
per.Validate();

per = (Person)await client.GetFromJsonAsync(uri.ToString(), typeof(Person), JsonContext.Default);
per.Validate();

per = await client.GetFromJsonAsync<Person>(uri, JsonContext.Default.Person);
per.Validate();

per = await client.GetFromJsonAsync<Person>(uri.ToString(), JsonContext.Default.Person);
per.Validate();
}
}
},
server => server.HandleRequestAsync(content: json, headers: headers));
Expand All @@ -44,8 +59,8 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
public static IEnumerable<object[]> ReadFromJsonTestData()
{
Person per = Person.Create();
yield return new object[] { per.Serialize() };
yield return new object[] { per.SerializeWithNumbersAsStrings() };
yield return new object[] { per.Serialize(), false };
yield return new object[] { per.SerializeWithNumbersAsStrings(), true };
}

[Fact]
Expand All @@ -58,6 +73,8 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(
{
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetFromJsonAsync(uri, typeof(Person)));
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetFromJsonAsync<Person>(uri));
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetFromJsonAsync(uri, typeof(Person), JsonContext.Default));
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetFromJsonAsync<Person>(uri, JsonContext.Default.Person));
}
},
server => server.HandleRequestAsync(statusCode: HttpStatusCode.InternalServerError));
Expand Down Expand Up @@ -170,6 +187,8 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync(

Person per = Assert.IsType<Person>(await client.GetFromJsonAsync((string)null, typeof(Person)));
per = Assert.IsType<Person>(await client.GetFromJsonAsync((Uri)null, typeof(Person)));
per = Assert.IsType<Person>(await client.GetFromJsonAsync((string)null, typeof(Person), JsonContext.Default));
per = Assert.IsType<Person>(await client.GetFromJsonAsync((Uri)null, typeof(Person), JsonContext.Default));

per = await client.GetFromJsonAsync<Person>((string)null);
per = await client.GetFromJsonAsync<Person>((Uri)null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;

namespace System.Net.Http.Json.Functional.Tests
{
internal partial class JsonContext : JsonSerializerContext
{
private JsonTypeInfo<int> _Int32;
public JsonTypeInfo<int> Int32
{
get
{
if (_Int32 == null)
{
JsonConverter customConverter;
if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(int))) != null)
{
_Int32 = JsonMetadataServices.CreateValueInfo<int>(Options, customConverter);
}
else
{
_Int32 = JsonMetadataServices.CreateValueInfo<int>(Options, JsonMetadataServices.Int32Converter);
}
}

return _Int32;
}
}
}
}
Loading