在ASP.NET Core中重写newtonsoft.json序列化,使typeNameHandling仅对对象而非数组



我的问题是,我在序列化的json上需要一些$type,我有一些子类,它对我非常有用。

这是我的启动

services.AddTransient<Microsoft.Extensions.Options.IConfigureOptions<Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions>, App_Start.MvcJsonOptionsSetup>();
services.AddControllers()
.AddNewtonsoftJson()
.SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddControllersAsServices();
internal class MvcJsonOptionsSetup : IConfigureOptions<MvcNewtonsoftJsonOptions>
{
private readonly IHttpContextAccessor _provider;
public MvcJsonOptionsSetup(IHttpContextAccessor provider)
{
}
public MvcJsonOptionsSetup(IHttpContextAccessor provider)
{
_provider = provider;
}
public Func<IServiceProvider> GetProvider()
{
return () => _provider.HttpContext?.RequestServices;
}
public override void Configure(MvcNewtonsoftJsonOptions options)
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
options.SerializerSettings.ConfigureRepositoryForJson<EntidadeBase>(GetProvider());
options.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;
}
}

问题出在数组上;我不想放$values,但有时返回类型不一样。

除了数组之外,有人做过TypeNAmeHandling.Auto的扩展吗?

例如,我使用剑道网格,返回的结果是:

{
"Data": {
"$type": "System.Collections.Generic.List`1[[Model.Entity, Model]], System.Private.CoreLib",
"$values": [
...
]
},
"Groups": null,
"Aggregates": null,
"Total": 4,
"Errors": null
}

我想要这个

{
"Data":  [
...
]
,
"Groups": null,
"Aggregates": null,
"Total": 4,
"Errors": null
} 

我没有使用

TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects 

为了使结果更简单,我只想在类型是子类的情况下返回。

这是结果类

public class DataSourceResult
{
public IEnumerable Data { get; set; }
public IEnumerable Groups { get; set; }
public object Aggregates { get; set; }
public int Total { get; set; }
public object Errors { get; set; }
}

最后,请注意,我不能将属性(如[JsonProperty(TypeNameHandling=TypeNameHandling.None)])放在所有类中,我的意思是,有些库是我使用的,不能更改,我正在寻找可以覆盖newtonsoft序列化程序并检查是否为数组并忽略typeNamehandling属性的东西。

使用Json.NET,无法完全根据要序列化的项的具体类型来启用或禁用类型信息的发布。相反,类型信息的发布由引用正在序列化的项的成员或集合控制*

只要这些成员(或集合项)被声明为可从IEnumerable分配的东西,就可以使用自定义协定解析器来抑制类型信息,例如:

public class SuppressCollectionTypeNamesContractResolver : DefaultContractResolver
{
internal static TypeNameHandling? RemoveCollectionTypenameHandling(TypeNameHandling? typeNameHandling, Type valueType)
=> typeNameHandling ?? (typeof(IEnumerable).IsAssignableFrom(valueType) && valueType != typeof(string) ? TypeNameHandling.None : typeNameHandling);
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.TypeNameHandling = RemoveCollectionTypenameHandling(property.TypeNameHandling, property.PropertyType);
return property;
}
protected override JsonArrayContract CreateArrayContract(Type objectType)
{
var contract = base.CreateArrayContract(objectType);
contract.ItemTypeNameHandling = RemoveCollectionTypenameHandling(contract.ItemTypeNameHandling, contract.CollectionItemType);
return contract;
}
}

并使用它来代替DefaultContractResolver,就像这样:

options.SerializerSettings.ContractResolver = new SuppressCollectionTypeNamesContractResolver();

请注意,Newtonsoft建议静态缓存自定义解析器以获得最佳性能。

在这里演示小提琴#1。

如果您可能将成员声明为objectdynamic,并且在其值为集合的情况下仍需要抑制类型信息,则必须使用自定义转换器,如以下所示:

public class SuppressCollectionTypeNamesConverter : JsonConverter
{
static readonly IContractResolver globalDefaultResolver = new JsonSerializer().ContractResolver;
IContractResolver resolver;

public SuppressCollectionTypeNamesConverter() : this(globalDefaultResolver) { }
public SuppressCollectionTypeNamesConverter(IContractResolver resolver) => this.resolver = resolver ?? globalDefaultResolver;

public override bool CanConvert(Type objectType) => typeof(IEnumerable).IsAssignableFrom(objectType) && objectType != typeof(string) && resolver.ResolveContract(objectType) is JsonArrayContract;
public override bool CanRead => false;      
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();        
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonArrayContract)serializer.ContractResolver.ResolveContract(value.GetType());
var itemType = contract.CollectionItemType;
writer.WriteStartArray();
foreach (object item in ((IEnumerable)value))
{
if (contract.ItemTypeNameHandling == TypeNameHandling.None)
serializer.Serialize(writer, item);
else
serializer.Serialize(writer, item, itemType);
}

writer.WriteEndArray();
}
}

并将其添加到以下选项中:

options.SerializerSettings.Converters.Add(new SuppressCollectionTypeNamesConverter());

在这里演示小提琴#2。


*这可以从调用JsonSerializerInternalWriter.ShouldWriteType()JsonSerializerInternalWriter.SerializeList()的参考源中确认

相关内容

  • 没有找到相关文章