Десериализовать первое свойство, не соответствующее свойствам какого-либо целевого объекта, в определенное свойство

Я делаю некоторую интеграцию веб-API с Newtonsoft.Json, и, как всегда, мне приходится делать глупые трюки, чтобы правильно десериализовать то, что они отправляют обратно.

В этом случае API будет отправлять ответы, напоминающие такую ​​структуру:

{ "contacts": [ ... ], "has-more": true, "offset": 38817 }

Свойства "has-more" и "offset" в значительной степени постоянны для разных ответов метода и были определены соответствующим образом для объекта ответа, в который я десериализую. Объект ответа выглядит примерно так:

public class APIResponse {
    public JContainer Result { get; set; }
    [JsonProperty("has-more")]
    public bool HasMore { get; set; }
    [JsonProperty("offset")]
    public int Offset { get; set; }
}

Это первое свойство «контактов» может варьироваться; для некоторых методов я мог бы получить "контакты", некоторые могли бы получить "компании", а другие могли бы получить кто-знает-что. У меня также нет никакого способа быть уверенным, что каждый ответ будет иметь такое «переменное» свойство или что он будет первым, говоря позиционно.

Для этого примера я хотел бы, чтобы десериализатор посмотрел на Json и сказал: «Давайте посмотрим, я не вижу ничего, связанного с «контактами», поэтому мы поместим это в «Результат», а затем я Из атрибутов JsonProperty видно, что «has-more» и «offset» входят в HasMore и Offset. Хорошо, все готово, вот ваш объект».

Я подозреваю, что это связано с некоторыми трюками с пользовательскими JsonConverter или IContractResolver, но я просто не связываю здесь точки. Я попытался сделать простой пользовательский преобразователь контрактов, но, похоже, он использует преобразователи контрактов для преобразования имен свойств объекта в имена свойств для поиска в тексте JSON, а не наоборот.


person db2    schedule 30.09.2016    source источник
comment
Вы должны иметь возможность использовать [JsonExtensionData].   -  person dbc    schedule 30.09.2016
comment
@dbc Это определенно похоже на то, что это может выполнить свою работу. Я попробую это сегодня днем.   -  person db2    schedule 30.09.2016


Ответы (1)


Вы можете использовать базовый класс + производные для каждого типа ответа.

public class APIResponseBase {
    [JsonProperty("has-more")]
    public bool HasMore { get; set; }
    [JsonProperty("offset")]
    public int Offset { get; set; }
}

public class ContactsResponse : APIResponseBase {
  public IEnumerable<Contact> Contacts { get; set; }
}

public class CompaniesResponse : APIResponseBase {
  public IEnumerable<Company> Companies { get; set; }
}

var contactsResponse = JsonConvert.Deserialize<ContactsResponse>(json);
IEnumerable<Contact> contacts = contactsResponse.Contacts
person Jeff    schedule 30.09.2016
comment
Это кажется достойной идеей. Я полагаю, что мог бы превратить метод Execute(), который делает фактический вызов API, в универсальный, который принимает тип подкласса ответа для десериализации. - person db2; 30.09.2016
comment
Ты получил это. ;) дженерики сильны - person Jeff; 30.09.2016