如何在 .net core 3.1 中的 Newtonsoft JsonConverter 中注入依赖项
Posted
技术标签:
【中文标题】如何在 .net core 3.1 中的 Newtonsoft JsonConverter 中注入依赖项【英文标题】:How to inject dependency in Newtonsoft JsonConverter in .net core 3.1 【发布时间】:2021-10-11 02:02:14 【问题描述】:我无法让依赖注入在 .net core 3.1 中为以下 Newtonsoft JsonConverter 工作。
我只想在属性级别使用它,而不是在全局级别。因此,它应该只在某个类的指定属性时才执行。
JsonConverter:
public class HelloWorldCustomConverter : JsonConverter<string>
private readonly IMyService _myService;
public HelloWorldCustomConverter(IMyService myService)
_myService = myService;
public override bool CanRead => false;
public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer)
throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
// append a value using the injected service
writer.WriteValue($"value-myService.GetValue()");
用法:
public class MyClass
public string Title get; set;
[JsonConverter(typeof(HelloWorldCustomConverter))]
public string Details get; set;
它是 .NET Core 3.1 和 Newtonsoft.json 版本 13.0.1。
感谢任何帮助,谢谢。
编辑 1:
我从 *** 查了很多答案,但到目前为止没有一个对我有用。他们中的大多数都已经过时了,或者缺少一些东西来使其正常工作。其中很少有我已经检查过但对我不起作用:
Cannot replace default JSON contract resolver in ASP.Core 3 Custom JsonConverter with parameters in .NET Core https://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm .Net Core Api - Custom JSON Resolver based on Request Values http://www.dotnet-programming.com/post/2017/05/07/Aspnet-core-Deserializing-Json-with-Dependency-Injection.aspx
编辑 2:我尝试了建议作为重复参考的帖子,但它在我的情况下不起作用。
https://***.com/questions/53288633/net-core-api-custom-json-resolver-based-on-request-values
我尝试过转头和其他各种选择,但没有运气。
James(日期:2108)建议的解决方法之一没有奏效。
参考:https://github.com/JamesNK/Newtonsoft.Json/issues/1910
你可以试试
public class JsonOptions : IConfigureOptions<MvcJsonOptions>
IHttpContextAccessor _accessor;
public JsonOptions(IHttpContextAccessor accessor)
_accessor = accessor;
public virtual void Configure(MvcJsonOptions options)
options.SerializerSettings.Converters.Add(new MyCustomConverter(_accessor));
在您的启动中注册它们 ervices.AddSingleton
() (不记得是否默认注册了 IHttpContextAccessor 所以 您可能还需要注册那个) 然后在您的 Read/WriteJson 方法中使用 _accessor.HttpContext 来 访问请求的上下文
【问题讨论】:
这方面有很多问题和答案,没有一个对你有用吗? @TheGeneral - 是的,我做到了。用一些参考更新了我的问题。 This answer 从MvcJsonOptions
适当更改为MvcNewtonsoftJsonOptions
对您不起作用?
作为替代方案,这可能对您有用:Pass additional data to JsonConverter。
@SunnySharma 我需要将依赖项注入到我的自定义JsonConverter
实现中,它不能有非默认构造函数,否则会导致异常。你的答案反过来使用了一个自定义的ContractResolver
,它显然可以有一个非默认的构造函数——所以我们的个人问题有点不同。我在下面发布了我的答案。它基于 Thomas 的 hack,但稍作修改以处理我的特定设置。
【参考方案1】:
从Thomas' blog post 中的 cmets 来看,您已经尝试过他的方法。无论您是否设法使其正常工作,当我尝试实施 Thomas 的 hack 时,我都会发布我遇到的问题的解决方案 - 也许这会对其他人有所帮助。
在我的设置中,自定义 JsonConverter
实际上不是由 MVC 框架直接实例化,而是通过 Newtonsoft 的 JToken.ToObject()
间接实例化,它创建了一个默认为 JsonSerializerSettings
的 JsonSerializer
。在ToObject()
的调用链下方,我的自定义JsonConverter
使用这些默认设置进行实例化。
TL;DR; 为了让 Thomas 的 hack 发挥作用,我需要将 IConfigureOptions<MvcNewtonsoftJsonOptions>.Configure()'
s 的实现更改为:
public void Configure(MvcNewtonsoftJsonOptions options)
JsonConvert.DefaultSettings = () =>
var settings = new JsonSerializerSettings();
settings.Converters.Add(new ServiceProviderDummyConverter(_httpContextAccessor, _serviceProvider));
return settings;
;
【讨论】:
【参考方案2】:这对我来说是这样的:
使用 ContractResolver。 (就我而言,我使用的是 Converter)。
自定义 ContractResolver。根据需要更改逻辑。
using HelloWorld.Attributes;
using HelloWorld.Helpers;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Reflection;
namespace HelloWorld.Serializers
public class MyCustomContractResolver : CamelCasePropertyNamesContractResolver
private readonly IServiceProvider _serviceProvider;
public MyCustomContractResolver(IServiceProvider serviceProvider)
_serviceProvider = serviceProvider;
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
var property = base.CreateProperty(member, memberSerialization);
// this condition is specific to my case, just to showcase how I'm accessing value from HTTP Context
if (Attribute.IsDefined(member, typeof(MyCustomAttribute),true))
if (property.PropertyType == typeof(string))
PropertyInfo propertyInfo = member as PropertyInfo;
// access required services here
var contextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>();
var customHelper = _serviceProvider.GetRequiredService<ICustomHelper>();
var attribute = (MyCustomAttribute)member.GetCustomAttribute(typeof(MyCustomAttributeAttribute));
property.ValueProvider = new StringValueProvider(propertyInfo, customHelper, contextAccessor, attribute);
return property;
public class StringValueProvider : IValueProvider
private PropertyInfo _targetProperty;
private readonly ICustomHelper _customHelper;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly MyCustomAttribute _attribute;
public StringValueProvider(
PropertyInfo targetProperty,
ICustomHelper customHelper,
IHttpContextAccessor httpContextAccessor,
MyCustomAttribute attribute)
_targetProperty = targetProperty;
_customHelper = customHelper;
_httpContextAccessor = httpContextAccessor;
_attribute = attribute;
// SetValue gets called by Json.Net during deserialization.
// The value parameter has the original value read from the JSON;
// target is the object on which to set the value.
public void SetValue(object target, object value)
_targetProperty.SetValue(target, value);
// GetValue is called by Json.Net during serialization.
// The target parameter has the object from which to read the value;
// the return value is what gets written to the JSON
public object GetValue(object target)
object value = _targetProperty.GetValue(target);
var userId = _httpContextAccessor.HttpContext.Request.Headers["UserId"].ToString();
return value == null ? value : _customHelper.SetGreetingsTextForUser(value.ToString(),userId, _attribute.UserRole);
带有 serviceProvider 注入的 MvcNewtonsoftJsonOptionsWrapper
using HelloWorld.Serializers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
namespace HelloWorld.Extensions
public class MvcNewtonsoftJsonOptionsWrapper : IConfigureOptions<MvcNewtonsoftJsonOptions>
IServiceProvider ServiceProvider;
public MvcNewtonsoftJsonOptionsWrapper(IServiceProvider serviceProvider)
this.ServiceProvider = serviceProvider;
public void Configure(MvcNewtonsoftJsonOptions options)
options.SerializerSettings.ContractResolver = new MyCustomContractResolver(ServiceProvider);
ServiceCollection Extension 注册 ContractResolver:
public static class ServiceCollectionExtensions
public static IServiceCollection MyCustomContractResolver(this IServiceCollection services)
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IConfigureOptions<MvcNewtonsoftJsonOptions>, MvcNewtonsoftJsonOptionsWrapper>();
return services;
在 Startup.cs 文件中注册 DI 中的 ContractResolver:
public void ConfigureServices(IServiceCollection services)
...
services.AddAppendSasTokenContractResolver();
...
就是这样。让我知道这是否适合你!
【讨论】:
【参考方案3】:恕我直言,您要实现的目标存在重大问题。序列化控制器方法的结果不应包含任何逻辑。
更重要的是,你的 api 应该允许内容协商,Accept: application/json
给你一个 json 字符串,Accept: application/xml
给你一个 xml 字符串。
相反,您应该利用依赖注入,在您的其他服务中注入 MyService
并在那里调用 myResult.whatever = myService.GetValue()
。
【讨论】:
感谢@Pieterjan,但这不是我想要的。以上是关于如何在 .net core 3.1 中的 Newtonsoft JsonConverter 中注入依赖项的主要内容,如果未能解决你的问题,请参考以下文章
如何在启动类中读取应用程序 URL,特别是在 .net core 3.1 中的“ConfigureService”方法中?
如何从 ASP.NET Core 3.1 中的存储库类创建确认电子邮件回调
如何从 Asp.NET Core 3.1 启动类访问 launchSettings.json 中的 `applicationUrl` 属性?