Skip to content

Add JsonContent.Create overloads that accept JsonTypeInfo. #51544

@layomia

Description

@layomia

EDIT New proposal can be found here: #51544 (comment)

Details

Background

The existing JsonContent type in System.Net.Http.Json uses existing JsonSerializerOptions-based JsonSerializer.Serialize(Async) overloads which root reflection code and all built-in STJ converters (even when not needed in an app). We should add a new type that uses new JsonSerializerContext-based overloads, which are AOT-friendly and allow for a smaller app. Also the existing type has been marked as "requires unreferenced code", so it is beneficial to provide an alternative which is trim-friendly.

API Proposal

public sealed partial class JsonContent<TValue> : HttpContent
{
  internal JsonContent() { }
  public TValue? Value { get { throw null; } }
}

We should also expose static Create methods for these types to the existing JsonContent :

public sealed partial class JsonContent : HttpContent
{
  // Existing
  // internal JsonContent() { }
  // public Type ObjectType { get { throw null; } }
  // public object? Value { get { throw null; } }
  // public static JsonContent Create(object? inputValue, Type inputType, MediaTypeHeaderValue? mediaType = null, JsonSerializerOptions? options = null) { throw null; }
  // public static JsonContent Create<T>(T inputValue, MediaTypeHeaderValue? mediaType = null, JsonSerializerOptions? options = null) { throw null; }

  // New create methods for JsonContent<TValue>
  public static JsonContent<object?> Create(object? inputValue, Type inputType, JsonSerializerContext context, MediaTypeHeaderValue? mediaType = null) { throw null; }
  public static JsonContent<TValue> Create(TValue? inputValue, JsonTypeInfo<TValue> jsonTypeInfo, MediaTypeHeaderValue? mediaType = null) { throw null; }
}

The new create methods should go on JsonContent - if we put them on JsonContent<TValue>, then it will be possible for users to write awkward code like this:

JsonContent<object> content = JsonContent<int>.Create(myVal, type, context);

Usage

Just like the existing JsonContent type, JsonContent<TValue> can be used when building a HttpResponseMessage based on JSON. The difference is that now the JSON will be serialized in a trim-safe way which is also optimized for app size:

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

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

Q/A

Can JsonContent<TValue> derive from JsonContent<T>?

It's better not to do this, because the ILLinker won't be able to trim away reflection-based code and extra JsonConverters used by the existing JsonContent type. The underlying infrastructure for serializing the JSON content is based on overriding virtual methods. Making the new JsonContent<TValue> type derive from the existing type is bad because the linker will preserve the virtual and override methods of called methods in the hierarchy, because it doesn't know which one will be called at run-time. This is discussed in more detail here.

Metadata

Metadata

Assignees

Labels

Cost:SWork that requires one engineer up to 1 weekapi-approvedAPI was approved in API review, it can be implementedarea-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions