Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martincostello/53b10574fbee2d8bc7b0f75bf0855a84 to your computer and use it in GitHub Desktop.
Save martincostello/53b10574fbee2d8bc7b0f75bf0855a84 to your computer and use it in GitHub Desktop.
A class for plugging the JSON source generator with Refit
using System.Net;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Refit;
/// <summary>
/// A class representing an implementation of <see cref="IHttpContentSerializer"/> that can be
/// used with the <c>System.Text.Json</c> source generators. This class cannot be inherited.
/// </summary>
/// <param name="jsonSerializerContext">The <see cref="JsonSerializerContext"/> to use.</param>
public sealed class SystemTextJsonContentSerializerForSourceGenerator(JsonSerializerContext jsonSerializerContext) : IHttpContentSerializer
{
//// Based on https://github.com/reactiveui/refit/blob/main/Refit/SystemTextJsonContentSerializer.cs
private readonly JsonSerializerContext _jsonSerializerContext = jsonSerializerContext;
/// <inheritdoc />
public HttpContent ToHttpContent<T>(T item)
{
ArgumentNullException.ThrowIfNull(item);
#if NET8_0_OR_GREATER
var jsonTypeInfo = _jsonSerializerContext.GetTypeInfo(typeof(T));
return System.Net.Http.Json.JsonContent.Create(item, jsonTypeInfo);
#else
return new JsonContent(item, typeof(T), _jsonSerializerContext);
#endif
}
/// <inheritdoc />
public async Task<T?> FromHttpContentAsync<T>(HttpContent content, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(content);
using var stream = await content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
#if NET8_0_OR_GREATER
var jsonTypeInfo = _jsonSerializerContext.GetTypeInfo(typeof(T)) as JsonTypeInfo<T>;
return await JsonSerializer.DeserializeAsync(stream, jsonTypeInfo, cancellationToken).ConfigureAwait(false);
#else
object result = await JsonSerializer.DeserializeAsync(
stream,
typeof(T),
_jsonSerializerContext,
cancellationToken).ConfigureAwait(false);
return (T?)result;
#endif
}
/// <inheritdoc />
public string? GetFieldNameForProperty(PropertyInfo propertyInfo)
{
ArgumentNullException.ThrowIfNull(propertyInfo);
return propertyInfo
.GetCustomAttributes<JsonPropertyNameAttribute>(true)
.Select(x => x.Name)
.FirstOrDefault();
}
#if !NET8_0_OR_GREATER
/// <summary>
/// Based on https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs.
/// </summary>
private sealed class JsonContent : HttpContent
{
private readonly object _value;
private readonly Type _objectType;
private readonly JsonSerializerContext _serializerContext;
internal JsonContent(object inputValue, Type inputType, JsonSerializerContext context)
{
_value = inputValue;
_objectType = inputType;
_serializerContext = context;
Headers.ContentType = new("application/json") { CharSet = "utf-8" };
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context)
{
return JsonSerializer.SerializeAsync(stream, _value, _objectType, _serializerContext, CancellationToken.None);
}
protected override bool TryComputeLength(out long length)
{
length = 0;
return false;
}
}
#endif
}
@djeikyb
Copy link

djeikyb commented Feb 6, 2024

Thanks for this example! I tried it out with net8.0 and replaced ToHttpContent like:

public HttpContent ToHttpContent<T>(T item)
{
    ArgumentNullException.ThrowIfNull(item);
    return JsonContent.Create(item, typeof(T), options: _jsonSerializerContext.Options);
}

And deleted the JsonContent implementation. Then used it like:

services.AddRefitClient<IGitLabApi>(
    new RefitSettings
        {
            ContentSerializer =
                new SystemTextJsonContentSerializerForSourceGenerator(GitlabJsonContext.Default)
        }
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment