How to exclude properties from JsonConvert.PopulateObject that don't exist in some base type or interface?

吃可爱长大的小学妹 提交于 2019-12-31 04:44:05

问题


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();
}

Result:

{"ID":8,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"newFirstName"}

Required because IElementWriter does not have ID nor FirstName:

{"ID":1,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"SourceFirstName"}


回答1:


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);

Thus your options include:

  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.

  2. 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.

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;
    }
}

Then, cache an instance somewhere for performance as suggested by Newtonsoft:

static IContractResolver elementAsElementWriterResolver = new UpcastingContractResolver<Element, IElementWriter>();

And populate your element as follows:

// Only populate those properties present in IElementWriter
JsonConvert.PopulateObject(json, element, new JsonSerializerSettings
                           { 
                               ContractResolver = elementAsElementWriterResolver 
                           });

Demo fiddle here.



来源:https://stackoverflow.com/questions/56467203/how-to-exclude-properties-from-jsonconvert-populateobject-that-dont-exist-in-so

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!