如何从JsonConvert.PopulateObject中排除某些基本类型或接口中不存在的属性? [英] How to exclude properties from JsonConvert.PopulateObject that don't exist in some base type or interface?

查看:439
本文介绍了如何从JsonConvert.PopulateObject中排除某些基本类型或接口中不存在的属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以为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);

因此,您的选择包括:

  1. 修改Element类的定义,并将[JsonIgnore]添加到您不想填充的属性中.

  1. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆