Динамически десериализовать свойство в RestSharp
Я играю с API Harvest, и я пытаюсь автоматически сопоставить объекты как можно проще, к сожалению, когда я делаю запрос типа GET /projects
, он генерирует такой результат:
[{
project: {
name: "Test"
}
},
{
project: {
name: "Test 2"
}]
В RestSharp я не могу напрямую это сделать:
client.Execute<List<Project>>(request)
Потому что он будет искать свойство под названием Project
. Поэтому я должен создать другой класс, обладающий этим свойством, и называть его следующим образом:
client.Execute<List<ProjectContainer>>(request)
Я не хочу создавать класс "контейнера" для каждого объекта, поэтому я решил, что нашел умное решение, чтобы сделать один класс, который я могу использовать для всех:
public class ListContainer<T> where T : IHarvestEntity
{
public T Item { get; set; }
}
Но, конечно, десериализатор понятия не имеет, что ему нужно сопоставить имя объекта (или "Проект" ) с свойством Item
. В документации restsharp я обнаружил, что могу использовать [DeserializeAs(Name = "CustomProperty")]
, чтобы сообщить десериализатору, какое поле будет отображено для этого свойства. Однако атрибуты допускают только константы, что означает, что я не могу:
[DeserializeAs(Name = typeof(T).FullName)]
public T Item { get; set; }
Кто-нибудь знает умное решение? Так что мне не нужно создавать 10 различных классов контейнеров?
Ответы
Ответ 1
Я предлагаю вам использовать эквивалент XPath для Json. С помощью Json.NET вы можете parse строку и создать динамический объект.
С SelectToken вы можете запрашивать значения или использовать Linq.
Код выглядит примерно так (я его не тестировал):
// execute the request
RestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
JObject o = JObject.Parse(content);
IList<string> projectNames = o.SelectToken("project").Select(s => (string)s.name).ToList();
Вы можете закодировать пути или настроить пути в любом случае.
--- Редактировать ---
Вот пример, который я тестировал, преобразование строки json в список проектов.
var projects = JArray.Parse(response.Content).Select(r => new Project(r["project"]["name"].Value<string>())).ToList();
Ответ 2
Чтобы это было очень просто, вы можете использовать List<dynamic>
и получить доступ к свойствам/свойствам по имени с помощью однострочного интерфейса.
var names = client.Execute<List<dynamic>>(request).Data.Select(
item => item["project"]["name"]).ToList(); // list of names
Если этого недостаточно, вы можете импровизировать свой собственный картограф и извлечь коллекцию, например. Project
экземпляры:
var projects = client.Execute<List<dynamic>>(request).Data.Select(
item => Map<Project>(item)).ToList(); // list of Project instances
где метод Map
может быть чем-то вроде
public T Map<T>(dynamic item) where T : class
{
// inline for clarity
var mappings = new Dictionary<Type,Func<dynamic,object>>
{
{ typeof(Project), map => new Project(map["project"]["name"]) }
};
return (T)mappings[typeof(T)].Invoke(item);
}
данный Project
определяется как
public class Project
{
public Project(string name)
{
Name = name;
}
public string Name { get; set; }
}