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

Add JsonContent.Create overloads which accept JsonTypeInfo #89614

Merged
merged 6 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -156,6 +156,8 @@ internal JsonContent() { }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
[System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext.")]
public static System.Net.Http.Json.JsonContent Create<T>(T inputValue, System.Net.Http.Headers.MediaTypeHeaderValue? mediaType = null, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static System.Net.Http.Json.JsonContent Create<T>(T? inputValue, System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> jsonTypeInfo, System.Net.Http.Headers.MediaTypeHeaderValue? mediaType = null) { throw null; }
public static System.Net.Http.Json.JsonContent Create(object? inputValue, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Net.Http.Headers.MediaTypeHeaderValue? mediaType = null) { throw null; }
protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context) { throw null; }
protected override bool TryComputeLength(out long length) { throw null; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ System.Net.Http.Json.JsonContent</PackageDescription>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(CommonPath)System\ThrowHelper.cs"
Link="Common\System\ThrowHelper.cs" />
<Compile Include="System\Net\Http\Json\HttpClientJsonExtensions.Get.AsyncEnumerable.cs" />
<Compile Include="System\Net\Http\Json\HttpClientJsonExtensions.cs" />
<Compile Include="System\Net\Http\Json\HttpContentJsonExtensions.AsyncEnumerable.cs" />
Expand All @@ -22,7 +24,6 @@ System.Net.Http.Json.JsonContent</PackageDescription>
<Compile Include="System\Net\Http\Json\HttpClientJsonExtensions.Patch.cs" />
<Compile Include="System\Net\Http\Json\HttpContentJsonExtensions.cs" />
<Compile Include="System\Net\Http\Json\JsonContent.cs" />
<Compile Include="System\Net\Http\Json\JsonContentOfT.cs" />
<Compile Include="System\Net\Http\Json\LengthLimitReadStream.cs" />
</ItemGroup>

Expand All @@ -33,7 +34,6 @@ System.Net.Http.Json.JsonContent</PackageDescription>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<Compile Include="System\Net\Http\Json\HttpContentJsonExtensions.netcoreapp.cs" />
<Compile Include="System\Net\Http\Json\JsonContent.netcoreapp.cs" />
<Compile Include="System\Net\Http\Json\JsonContentOfT.netcoreapp.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public static Task<HttpResponseMessage> PatchAsJsonAsync<TValue>(this HttpClient
throw new ArgumentNullException(nameof(client));
}

JsonContent<TValue> content = new(value, jsonTypeInfo);
JsonContent content = JsonContent.Create(value, jsonTypeInfo);
return client.PatchAsync(requestUri, content, cancellationToken);
}

Expand All @@ -129,7 +129,7 @@ public static Task<HttpResponseMessage> PatchAsJsonAsync<TValue>(this HttpClient
throw new ArgumentNullException(nameof(client));
}

JsonContent<TValue> content = new(value, jsonTypeInfo);
JsonContent content = JsonContent.Create(value, jsonTypeInfo);
return client.PatchAsync(requestUri, content, cancellationToken);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static Task<HttpResponseMessage> PostAsJsonAsync<TValue>(this HttpClient
throw new ArgumentNullException(nameof(client));
}

JsonContent<TValue> content = new(value, jsonTypeInfo);
JsonContent content = JsonContent.Create(value, jsonTypeInfo);
return client.PostAsync(requestUri, content, cancellationToken);
}

Expand All @@ -65,7 +65,7 @@ public static Task<HttpResponseMessage> PostAsJsonAsync<TValue>(this HttpClient
throw new ArgumentNullException(nameof(client));
}

JsonContent<TValue> content = new(value, jsonTypeInfo);
JsonContent content = JsonContent.Create(value, jsonTypeInfo);
return client.PostAsync(requestUri, content, cancellationToken);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static Task<HttpResponseMessage> PutAsJsonAsync<TValue>(this HttpClient c
throw new ArgumentNullException(nameof(client));
}

JsonContent<TValue> content = new(value, jsonTypeInfo);
JsonContent content = JsonContent.Create(value, jsonTypeInfo);
return client.PutAsync(requestUri, content, cancellationToken);
}

Expand All @@ -65,7 +65,7 @@ public static Task<HttpResponseMessage> PutAsJsonAsync<TValue>(this HttpClient c
throw new ArgumentNullException(nameof(client));
}

JsonContent<TValue> content = new(value, jsonTypeInfo);
JsonContent content = JsonContent.Create(value, jsonTypeInfo);
return client.PutAsync(requestUri, content, cancellationToken);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,67 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#if !NETCOREAPP
using System.Diagnostics;
#endif
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;

namespace System.Net.Http.Json
{
public sealed partial class JsonContent : HttpContent
{
private readonly JsonSerializerOptions? _jsonSerializerOptions;
public Type ObjectType { get; }
private readonly JsonTypeInfo _typeInfo;
public Type ObjectType => _typeInfo.Type;
public object? Value { get; }

[RequiresUnreferencedCode(HttpContentJsonExtensions.SerializationUnreferencedCodeMessage)]
[RequiresDynamicCode(HttpContentJsonExtensions.SerializationDynamicCodeMessage)]
private JsonContent(
object? inputValue,
Type inputType,
MediaTypeHeaderValue? mediaType,
JsonSerializerOptions? options)
JsonTypeInfo jsonTypeInfo,
MediaTypeHeaderValue? mediaType)
{
if (inputType is null)
{
throw new ArgumentNullException(nameof(inputType));
}

if (inputValue != null && !inputType.IsAssignableFrom(inputValue.GetType()))
{
throw new ArgumentException(SR.Format(SR.SerializeWrongType, inputType, inputValue.GetType()));
}
Debug.Assert(jsonTypeInfo is not null);
brantburnett marked this conversation as resolved.
Show resolved Hide resolved

Value = inputValue;
ObjectType = inputType;
_typeInfo = jsonTypeInfo;
Headers.ContentType = mediaType ?? JsonHelpers.GetDefaultMediaType();
_jsonSerializerOptions = options ?? JsonHelpers.s_defaultSerializerOptions;
}

[RequiresUnreferencedCode(HttpContentJsonExtensions.SerializationUnreferencedCodeMessage)]
[RequiresDynamicCode(HttpContentJsonExtensions.SerializationDynamicCodeMessage)]
public static JsonContent Create<T>(T inputValue, MediaTypeHeaderValue? mediaType = null, JsonSerializerOptions? options = null)
=> Create(inputValue, typeof(T), mediaType, options);
=> Create(inputValue, GetJsonTypeInfo(typeof(T), options), mediaType);

[RequiresUnreferencedCode(HttpContentJsonExtensions.SerializationUnreferencedCodeMessage)]
[RequiresDynamicCode(HttpContentJsonExtensions.SerializationDynamicCodeMessage)]
public static JsonContent Create(object? inputValue, Type inputType, MediaTypeHeaderValue? mediaType = null, JsonSerializerOptions? options = null)
=> new JsonContent(inputValue, inputType, mediaType, options);
{
ThrowHelper.ThrowIfNull(inputType);
EnsureTypeCompatibility(inputValue, inputType);

return new JsonContent(inputValue, GetJsonTypeInfo(inputType, options), mediaType);
}

public static JsonContent Create<T>(T? inputValue, JsonTypeInfo<T> jsonTypeInfo,
MediaTypeHeaderValue? mediaType = null)
{
ThrowHelper.ThrowIfNull(jsonTypeInfo);

return new JsonContent(inputValue, jsonTypeInfo, mediaType);
}

public static JsonContent Create(object? inputValue, JsonTypeInfo jsonTypeInfo,
MediaTypeHeaderValue? mediaType = null)
{
ThrowHelper.ThrowIfNull(jsonTypeInfo);
EnsureTypeCompatibility(inputValue, jsonTypeInfo.Type);

return new JsonContent(inputValue, jsonTypeInfo, mediaType);
}

protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context)
=> SerializeToStreamAsyncCore(stream, async: true, CancellationToken.None);
Expand All @@ -63,10 +72,6 @@ protected override bool TryComputeLength(out long length)
return false;
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "The ctor is annotated with RequiresUnreferencedCode.")]
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
Justification = "The ctor is annotated with RequiresDynamicCode.")]
private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, CancellationToken cancellationToken)
{
Encoding? targetEncoding = JsonHelpers.GetEncoding(this);
Expand All @@ -80,11 +85,11 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
{
if (async)
{
await JsonSerializer.SerializeAsync(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
await JsonSerializer.SerializeAsync(transcodingStream, Value, _typeInfo, cancellationToken).ConfigureAwait(false);
}
else
{
JsonSerializer.Serialize(transcodingStream, Value, ObjectType, _jsonSerializerOptions);
JsonSerializer.Serialize(transcodingStream, Value, _typeInfo);
}
}
finally
Expand All @@ -101,11 +106,11 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
}
}
#else
Debug.Assert(async);
Debug.Assert(async, "HttpContent synchronous serialization is only supported since .NET 5.0");

using (TranscodingWriteStream transcodingStream = new TranscodingWriteStream(targetStream, targetEncoding))
{
await JsonSerializer.SerializeAsync(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
await JsonSerializer.SerializeAsync(transcodingStream, Value, _typeInfo, cancellationToken).ConfigureAwait(false);
// The transcoding streams use Encoders and Decoders that have internal buffers. We need to flush these
// when there is no more data to be written. Stream.FlushAsync isn't suitable since it's
// acceptable to Flush a Stream (multiple times) prior to completion.
Expand All @@ -117,17 +122,39 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
{
if (async)
{
await JsonSerializer.SerializeAsync(targetStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
await JsonSerializer.SerializeAsync(targetStream, Value, _typeInfo, cancellationToken).ConfigureAwait(false);
}
else
{
#if NETCOREAPP
JsonSerializer.Serialize(targetStream, Value, ObjectType, _jsonSerializerOptions);
JsonSerializer.Serialize(targetStream, Value, _typeInfo);
#else
Debug.Fail("Synchronous serialization is only supported since .NET 5.0");
Debug.Fail("HttpContent synchronous serialization is only supported since .NET 5.0");
#endif
}
}
}

[RequiresUnreferencedCode(HttpContentJsonExtensions.SerializationUnreferencedCodeMessage)]
[RequiresDynamicCode(HttpContentJsonExtensions.SerializationDynamicCodeMessage)]
private static JsonTypeInfo GetJsonTypeInfo(Type inputType, JsonSerializerOptions? options)
{
Debug.Assert(inputType is not null);

// Ensure the options supports the call to GetTypeInfo
options ??= JsonHelpers.s_defaultSerializerOptions;
options.TypeInfoResolver ??= JsonSerializerOptions.Default.TypeInfoResolver;
options.MakeReadOnly();

return options.GetTypeInfo(inputType);
}

private static void EnsureTypeCompatibility(object? inputValue, Type inputType)
{
if (inputValue is not null && !inputType.IsAssignableFrom(inputValue.GetType()))
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
{
throw new ArgumentException(SR.Format(SR.SerializeWrongType, inputType, inputValue.GetType()));
}
}
}
}

This file was deleted.

This file was deleted.

Loading
Loading