Как исключить свойства из JsonConvert.PopulateObject, которые не существуют в каком-либо базовом типе или интерфейсе? - PullRequest
1 голос
/ 05 июня 2019

Можно ли определить параметры для 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, "Название": "NEWNAME", "CreationDate": "2019-06-05T00: 00: 00 + 02: 00", "FirstName": "newFirstName"}

Обязательно , поскольку IElementWriter не имеет ни ID, ни FirstName:

{ "ID": 1, "Имя": "NEWNAME", "CreationDate": "2019-06-05T00: 00: 00 + 02: 00", "FirstName": "SourceFirstName"}

1 Ответ

0 голосов
/ 06 июня 2019

В JsonSerializerSettings нет простого параметра, который заставил бы JsonConvert.PopulateObject() заполнить экземпляр производного типа, как если бы он был экземпляром некоторого базового типа. Чтобы подтвердить это, вы можете проверить источник для JsonSerializerInternalReader.Populate(), который принимает в качестве аргументов только reader и target и извлекает контракт цели непосредственно из его типа:

public void Populate(JsonReader reader, object target)
{
    Type objectType = target.GetType();

    JsonContract contract = Serializer._contractResolver.ResolveContract(objectType);

Таким образом, ваши опции включают в себя:

  1. Измените определение вашего Element класса и добавьте [JsonIgnore] к свойствам, которые вы не хотите заполнять.

    Вы, вероятно, не хотите этого делать, потому что это предотвратит сериализацию или десериализацию свойств.

  2. Используйте пользовательский обработчик контрактов , чтобы игнорировать все свойства 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 
                           });

Демонстрационная скрипка здесь .

...