c# - 如何从 JsonConvert.PopulateObject 中排除某些基本类型或接口中不存在的属性?
问题描述
是否可以为 JsonConvert.PopulateObject 定义选项以排除目标对象的接口实现中不存在的 json 中给出的字段?
public interface IElementWriter
{
string Name { get; set; }
}
public interface IElementUpdateWriter : IElementWriter
{
string FirstName { get; set; }
}
public interface IElementInsertWriter : IElementWriter
{
DateTime? CreationDate { get; set; }
}
public class Element:IElementWriter, IElementInsertWriter, IElementUpdateWriter {
public int ID { get; set; }
public string Name { get; set; }
public DateTime? CreationDate { get; set; }
public string FirstName { get; set; }
}
static void Main(string[] args)
{
IElementWriter element = new Element() { ID = 1, Name = "SourceName", CreationDate=DateTime.Today, FirstName="SourceFirstName" };
string json = "{ id:'8', Name:'newName', FirstName:'newFirstName' }";
JsonConvert.PopulateObject(json, element, new JsonSerializerSettings() {
});
Console.WriteLine(JsonConvert.SerializeObject(element));
Console.ReadLine();
}
结果:
{"ID":8,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"newFirstName"}
必需,因为 IElementWriter 没有 ID 也没有名字:
{"ID":1,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"SourceFirstName"}
解决方案
没有简单的设置JsonSerializerSettings
会导致JsonConvert.PopulateObject()
填充派生类型的实例,就好像它是某个基类型的实例一样。要确认这一点,您可以检查 源JsonSerializerInternalReader.Populate()
,它仅将 areader
和作为参数,并直接从其类型target
中提取目标的合同:
public void Populate(JsonReader reader, object target)
{
Type objectType = target.GetType();
JsonContract contract = Serializer._contractResolver.ResolveContract(objectType);
因此,您的选择包括:
修改
Element
类的定义并添加[JsonIgnore]
到您不想填充的属性中。您可能不想这样做,因为它会阻止属性被序列化或反序列化。
使用自定义合同解析器忽略
Element
不属于IElementWriter
.这似乎是更好的解决方案。
假设您选择选项 #2,您可以引入以下自定义合约解析器:
public class UpcastingContractResolver<TDerived, TBase> : DefaultContractResolver where TDerived: TBase
{
readonly HashSet<string> baseProperties = ((JsonObjectContract)JsonSerializer.Create().ContractResolver.ResolveContract(typeof(TBase)))
.Properties.Select(p => p.UnderlyingName)
.ToHashSet();
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
if (type == typeof(TDerived) || type.IsSubclassOf(typeof(TDerived)))
{
foreach (var property in properties)
{
if (!baseProperties.Contains(property.UnderlyingName))
property.Ignored = true;
}
}
return properties;
}
}
然后,按照Newtonsoft的建议在某处缓存一个实例以获得性能:
static IContractResolver elementAsElementWriterResolver = new UpcastingContractResolver<Element, IElementWriter>();
element
并按如下方式填充您的:
// Only populate those properties present in IElementWriter
JsonConvert.PopulateObject(json, element, new JsonSerializerSettings
{
ContractResolver = elementAsElementWriterResolver
});
演示小提琴在这里。
推荐阅读
- r - 使用 rsample 从评估拆分中删除观察结果
- c# - 迁移到 .Net Core v3.1 后 IAsyncQueryProvider 生成错误
- php - 从 php(变量)运行 bash 交互式脚本(带输入)
- java - 为什么实例只能被主要方法看到
- python - 删除pandas df中每一行的字符串中的最后一个字符
- html - 在引导行中对齐 mat-spinner
- sdk - 交互式多点触控应用的框架推荐
- python-3.x - 如何使用 conda-build“使用 CLI 选项进行构建”?
- python - Keras TLSTM ext 分类不同的训练和测试形状
- java - 不允许多次按下按钮