-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Inject service into JsonConverter #34773
Comments
Or, even better: add a property like this to public Func<Type, object> ServiceProvider { get; set; } And use it when constructing instances of converter specified with |
If the intent is to configure it on a per-callsite basis, couldn't the application code resolve the converter from DI? |
@pranavkm no, it's not per-callsite. For instance if have a type like this: public class Foo
{
[JsonConverter(typeof(MyCustomConverter))]
public int Id { get; set; }
public string Name { get; set; }
public int LuckyNumber { get; set; }
} I can't just add |
Current workaround : private class ConfigureJsonOptions : IConfigureOptions<JsonOptions>
{
private readonly IServiceProvider _services;
public ConfigureJsonOptions(IServiceProvider services)
{
_services = services;
}
public void Configure(JsonOptions options)
{
options.JsonSerializerOptions.Converters.Add(new ServiceProviderDummyConverter(_services));
}
}
/// <summary>
/// This isn't a real converter. It only exists as a hack to expose
/// IServiceProvider on the JsonSerializerOptions.
/// </summary>
public class ServiceProviderDummyConverter : JsonConverter<ServiceProviderDummyConverter>, IServiceProvider
{
private readonly IServiceProvider _services;
public ServiceProviderDummyConverter(IServiceProvider services)
{
_services = services;
}
public object GetService(Type serviceType) => _services.GetService(serviceType);
public override bool CanConvert(Type typeToConvert) => false;
public override ServiceProviderDummyConverter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, ServiceProviderDummyConverter value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
} In a real converter, you have access to the options, so you can retrieve the service provider via the converter above: var serviceProvider = options.Converters.OfType<IServiceProvider>().First(); |
What is the motivating use case for requiring DI in a JsonConverter? My understanding of JsonConverter is that it's meant to define a pure mapping between JSON and .NET types and vice versa, so a DI requirement in this context sounds like introducing unrelated concerns into the serialization layer. In any case, it might be possible to achieve what you're looking for using a ConditionalWeakTable on // dynamically attach IServiceProvider instances to JsonSerializerOptions
public static class JsonSerializerOptionsDependencyInjection
{
private readonly static ConditionalWeakTable<JsonSerializerOptions, IServiceProvider> s_serviceproviders = new();
public static IServiceProvider? GetServiceProvider(this JsonSerializerOptions options)
=> s_serviceproviders.TryGetValue(options, out var value) ? value : null;
public static void SetServiceProvider(this JsonSerializerOptions options, IServiceProvider provider)
=> s_serviceproviders.Add(options, provider);
}
public class FooConverter : JsonConverter<int>
{
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
IServiceProvider? provider = options.GetServiceProvider();
...
}
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
=> throw new NotImplementedException();
} |
@eiriktsarpalis in theory, I agree with you. Unfortunately the real world disagrees, as is often the case... My initial use case for this was an API that needed to expose obfuscated ids to prevent resource enumeration. The idea was that the application would manipulate "normal" int ids, and the obfuscation would occur at the application's boundaries (i.e. JSON serialization, ASP.NET Core model binding, etc). The obfuscation process uses cryptography, so it needs a key. And that key comes from the application settings, which I can only access through Of course, there are workarounds, like the one I posted above, or the one you suggested using So, it's not a major issue, definitely not a show-stopper, but it'd be nice to have a cleaner way to achieve this. Maybe just a general purpose
|
For .NET 7 we are looking at extending the converter model (parent issue #36785), and this would likely include a concept similar to |
@eiriktsarpalis could you elaborate please? What would it look like? I don't see what you mention in issue #36785 |
We're still in very early phases of planning (in fact we're still grooming our backlog as you can see :-)), so final design is TBD. But fundamentally we'd be looking at exposing new virtual Read&Write methods in |
@eiriktsarpalis I see, thanks. |
That's good feedback, thanks. We might want to consider ways to tie in the feature to the ASP.NET DI context, but that might require input from that team as well. cc @pranavkm |
I have a converter that needs to access a service from the DI container. How can I do that?
I know I could create the converter in a
IConfigureOptions<JsonOptions>
and globally add it to theJsonSerializerOptions
, but then it would apply everywhere, which I don't want. I want to apply it with an attribute.Is there a way to inject a service into a converter?
I guess not, especially since the JsonSerializer API is static, but I can think of one way to achieve the desired result with very little change: add an
IServiceProvider
property toJsonSerializerOptions
. This way, converter can retrieve the services they need.If you don't want to depend on Microsoft.Extensions.DependencyInjection, expose that property as
object
, and the user can do whatever they want with it.The text was updated successfully, but these errors were encountered: