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

坚强是说给别人听的谎言 提交于 2019-12-02 04:18:27

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.

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