如何从JsonConvert.PopulateObject中排除某些基本类型或接口中不存在的属性? [英] How to exclude properties from JsonConvert.PopulateObject that don't exist in some base type or interface?
问题描述
是否可以为JsonConvert.PopulateObject定义选项以排除目标对象的接口实现中不存在的json中给定的字段?
Is it possible to define options to JsonConvert.PopulateObject to exclude fields given in a json, that does not exists in the target object's interface implementation?
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,名称":"newName","CreationDate":"2019-06-05T00:00:00 + 02:00","FirstName":"newFirstName"}
{"ID":8,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"newFirstName"}
必需,因为IElementWriter没有ID或FirstName:
Required because IElementWriter does not have ID nor FirstName:
{"ID":1,"Name":"newName","CreationDate":"2019-06-05T00:00:00 + 02:00","FirstName":"SourceFirstName"}
{"ID":1,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"SourceFirstName"}
推荐答案
JsonSerializerSettings
中没有简单的设置会导致合同直接来自其类型:
There is no simple setting in JsonSerializerSettings
that will cause JsonConvert.PopulateObject()
to populate an instance of a derived type as if it were an instance of some base type. To confirm this, you can check the source for JsonSerializerInternalReader.Populate()
, which takes as arguments only a reader
and target
and pulls the target's contract directly from its type:
public void Populate(JsonReader reader, object target)
{
Type objectType = target.GetType();
JsonContract contract = Serializer._contractResolver.ResolveContract(objectType);
因此,您的选择包括:
-
修改
Element
类的定义,并将[JsonIgnore]
添加到您不想填充的属性中.
Modify the definition of your
Element
class and add[JsonIgnore]
to the properties you don't want to populate.
您可能不想这样做,因为它将阻止属性被序列化或反序列化.
You probably don't want to do this because it will prevent the properties from ever being serialized or deserialized.
使用自定义合同解析器忽略Element
的所有属性,而不是IElementWriter
的属性.
Use a custom contract resolver to ignore all properties of Element
that are not also properties of IElementWriter
.
这似乎是更好的解决方案.
This would seem to be the better solution.
假设您选择选项#2,则可以引入以下自定义合同解析器:
Assuming you choose option #2, you can introduce the following custom contract resolver:
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建议:
Then, cache an instance somewhere for performance as suggested by Newtonsoft:
static IContractResolver elementAsElementWriterResolver = new UpcastingContractResolver<Element, IElementWriter>();
并按照以下步骤填充element
:
// Only populate those properties present in IElementWriter
JsonConvert.PopulateObject(json, element, new JsonSerializerSettings
{
ContractResolver = elementAsElementWriterResolver
});
演示小提琴此处.
这篇关于如何从JsonConvert.PopulateObject中排除某些基本类型或接口中不存在的属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!