c# - jsonconvert - Deserializar dinĂ¡micamente a una propiedad en RestSharp
restsharp github (2)
Estoy jugando con Harvest API y estoy tratando de mapear automáticamente las entidades de la forma más fácil posible, desafortunadamente cuando hago una solicitud como GET /projects
, genera un resultado como este:
[{
project: {
name: "Test"
}
},
{
project: {
name: "Test 2"
}]
En RestSharp, no puedo hacer esto directamente:
client.Execute<List<Project>>(request)
Porque va a buscar una propiedad llamada Project
. Así que tengo que hacer otra clase que tenga esa propiedad, y llamarlo así:
client.Execute<List<ProjectContainer>>(request)
No quiero hacer una clase ''contenedor'' para cada entidad, así que pensé que encontré una solución inteligente para hacer una clase que puedo usar en todos:
public class ListContainer<T> where T : IHarvestEntity
{
public T Item { get; set; }
}
Pero, por supuesto, el deserializador no tiene idea de que necesita asignar el nombre de la entidad (o "Proyecto") a la propiedad Item
. En la documentación de restsharp encontré que podría usar [DeserializeAs(Name = "CustomProperty")]
para indicar al deserializador qué campo asignar a esta propiedad. Sin embargo, los atributos solo permiten constantes, lo que significa que no puedo hacer:
[DeserializeAs(Name = typeof(T).FullName)]
public T Item { get; set; }
¿Alguien sabe una solución inteligente a esto? ¿Entonces no tengo que crear 10 clases de contenedores diferentes?
Para que sea realmente simple, puede usar List<dynamic>
y acceder a la propiedad / propiedades por nombre con una sola línea.
var names = client.Execute<List<dynamic>>(request).Data.Select(
item => item["project"]["name"]).ToList(); // list of names
Si esto no es suficiente, entonces podría improvisar su propio asignador y extraer una colección de, por ejemplo Project
instancias del Project
:
var projects = client.Execute<List<dynamic>>(request).Data.Select(
item => Map<Project>(item)).ToList(); // list of Project instances
donde el método de Map
podría ser algo así como
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
dado se define como
public class Project
{
public Project(string name)
{
Name = name;
}
public string Name { get; set; }
}
Te sugiero que uses el equivalente de XPath para Json. Con Json.NET puedes parse la cadena y crear un objeto dinámico.
Con SelectToken puedes consultar valores, o usar Linq .
El código se ve algo como esto (no lo probé):
// 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();
Puede codificar las rutas o configurar las rutas que desee.
--- Editar ---
Aquí hay un ejemplo que probé, convirtiendo la cadena json en una lista de proyectos.
var projects = JArray.Parse(response.Content).Select(r => new Project(r["project"]["name"].Value<string>())).ToList();