asp.net core 使用newtonsoft完美序列化WebApi返回的ValueTuple
2021-03-19 05:23
标签:null 微软 emc 根据 顺序 serialize configure pre content 由于开发功能的需要,又懒得新建太多的class,所以ValueTuple是个比较好的偷懒方法,但是,由于WebApi需要返回序列化后的json,默认的序列化只能将ValueTuple定义的各个属性序列化成Item1...n 但是微软还是良心的为序列化留下入口,编译器会在每个返回ValueTuple的函数或者属性上,增加一个TupleElementNamesAttribute特性,该类的TransformNames就是存着所设置的属性的名称(强烈需要记住:是每个使用到ValueTuple的函数或者属性才会添加,而不是加在有使用ValueTuple的类上),比如 (string str1,string str2) 那么 TransformNames=["str1","str2"],那么现在有如下一个class 经过测试,如下一个函数 这样一个函数testApi 的会加上 TupleElementNamesAttribute 特性,,TransformNames=["str1","str2","str3","str4","str5","int2"],注意了,,这里只会添加一个TupleElementNamesAttribute特性,然后把A里所有的名字按定义的顺序包含进去. 然后我们需要定义一个JsonConverter,用来专门针对一个函数或一个属性的返回值进行了序列化 接下来说说实现的原理: 1.newtonsoft.json的组件里,有一个ContactResolver类,用于对不同的类的解析,类库中自带的DefaultContractResolver默认定义了将类解析成各个JsonProperty,利用这个类,可用于将ValueTuple的定义的名字当做属性,返回给序列化器 2.asp.net core的Formatter,可以对Action输出的对象进行格式化,一般用于比如json的格式化器或者xml格式化器的定义,利用格式化器,在Action最后输出的时候,配合ContractResolver进行序列化 下面的实现中,很多地方需要判断是否为ValueTuple,为了节省代码,因此,先写一个Helper: 那么开始来定义一个ContractResolver,实现的原理请看注释 为了能兼容用于预先定义的ContractResolver,因此,先定义一个CompositeContractResolver,用于合并多个ContractResolver,可看可不看: 接下来,就该定义OutputFormatter了 到此,该定义的类都定义完了,下面是注册方法:在Start.cs中: 注册完成后,用下面的Action可测试: 总结一下,上面实现的原理是: 自定义一个OutputFormatter,在WriteResponseBodyAsync中,可以获取到当前的Action对应的MethodInfo,然后利用编译器在所有返回ValueTuple的地方,都加了TupleElementNamesAttribute的功能,获取到使用时定义的ValueTuple各个Item的名字,再利用ContractResolver的CreateProperty功能,将定义的各个Item转换为对应的name.然后使用newtonsoft的序列化器,进行json序列化. 以上代码只能处理返回时,返回的类型为ValueTuple 这种情况的变量s的序列化就没办法了 部分代码地址: https://github.com/kugarliyifan/Kugar.UI.Web/blob/master/Kugar.Core.Web.NetCore/Formatters/ValueTupleOutputFormatter.cs https://github.com/kugarliyifan/Kugar.UI.Web/blob/master/Kugar.Core.Web.NetCore/Converters/ValueTupleConverter.cs https://github.com/kugarliyifan/Kugar.UI.Web/blob/master/Kugar.Core.Web.NetCore/ValueTupleContractResolver.cs asp.net core 使用newtonsoft完美序列化WebApi返回的ValueTuple 标签:null 微软 emc 根据 顺序 serialize configure pre content 原文地址:https://www.cnblogs.com/kugar/p/12334210.html public class A
public T2 Prop2{set;get;}
public (string str5,int int2) Prop3{set;get;}
} public Astring str1,string str2),(string str3,string str4)> testApi(){}
public class ValueTupleConverter : JsonConverter
{
private string[] _tupleNames = null;
private NamingStrategy _strategy = null;
//也可以直接在这里传入特性
public ValueTupleConverter(TupleElementNamesAttribute tupleNames, NamingStrategy strategy = null)
{
_tupleNames = tupleNames.TransformNames.ToArrayEx();
_strategy = strategy;
}
//这里在构造函数里把需要序列化的属性或函数返回类型的names传进来
public ValueTupleConverter(string[] tupleNames, NamingStrategy strategy = null)
{
_tupleNames = tupleNames;
_strategy = strategy;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value != null && value is ITuple v)
{
writer.WriteStartObject();
for (int i = 0; i )
{
var pname = _tupleNames[i];
//根据规则,设置属性名
writer.WritePropertyName(_strategy?.GetPropertyName(pname, true) ?? pname);
if (v[i] == null)
{
writer.WriteNull();
}
else
{
serializer.Serialize(writer, v[i]);
}
}
writer.WriteEndObject();
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//只需要实现序列化,,不需要反序列化,因为只管输出,所以,这个写不写无所谓
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType.IsValueTuple();
}
}
public static class ValueTupleHelper
{
private static ConcurrentDictionary
public class CustomContractResolver : DefaultContractResolver
{
private MethodInfo _methodInfo = null;
private IContractResolver _parentResolver = null;
public CustomContractResolver(MethodInfo methodInfo, IContractResolver? parentContractResolver = null)
{
_methodInfo = methodInfo;
_parentResolver = parentContractResolver;
}
public override JsonContract ResolveContract(Type type)
{
if (!type.GetProperties()
.Where(x => x.CanRead && x.PropertyType.IsValueTuple())
.Any()) //如果Type类中不包含可读的ValueTuple类型的属性,则调用预定义的Resolver处理,当前Resolver只处理包含ValueTuple的类
{
return _parentResolver?.ResolveContract(type);
}
var rc = base.ResolveContract(type);
return rc;
}
public MethodInfo Method => _methodInfo;
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
//CreateProperty函数的结果,不需要额外加缓存,因为每个Method的返回Type,只会调用一次
JsonProperty property = base.CreateProperty(member, memberSerialization); //先调用默认的CreateProperty函数,创建出默认JsonProperty
var pi = member as PropertyInfo;
if (property.PropertyType.IsValueTuple())
{
var attr = pi.GetCustomAttribute
///
public class ValueTupleOutputFormatter : TextOutputFormatter
{
private static ConcurrentDictionary
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(opt =>
{
opt.OutputFormatters.Insert(0,new ValueTupleOutFormatter(x =>
{
x.NamingStrategy= new CamelCaseNamingStrategy(true,true); //这里主要是为了演示对CustomContractResolver的配置,设置了所有属性首字母小写
}));
}).AddNewtonsoftJson();
}
public class ApiTestController : ControllerBase
{
[FromBodyJson()]
public IActionResult test1(Liststring productid,int qty)> details)
{
return Content("success");
}
public ResultReturnstring str1, int int3)> Test()
{
return new SuccessResultReturnstring str1, int int3)>(("2222",222));
}
public Teststring Y1, string Y2), (string str1, string t2)> Test2()
{
return new Teststring Y1, string Y2),(string str1, string t2)>(("111","22222"),("3333","44444") );
}
}
public object Test2()
{
var s= new Teststring Y1, string Y2),(string str1, string t2)>(("111","22222"),("3333","44444") );
JsonConvert.SerializeObject(s);
return null;
}
文章标题:asp.net core 使用newtonsoft完美序列化WebApi返回的ValueTuple
文章链接:http://soscw.com/index.php/essay/66094.html