2015-05-04 3 views
26

sto giocando con l'API di vendemmia e sto cercando di mappare automaticamente i soggetti più semplice possibile, purtroppo quando faccio una richiesta simile GET /projects genera un risultato in questo modo:dinamicamente deserializzazione di un immobile in RestSharp

[{ 
    project: { 
     name: "Test" 
    } 
}, 
{ 
    project: { 
     name: "Test 2" 
}] 

In RestSharp, non posso fare direttamente questa:

client.Execute<List<Project>>(request) 

perché sta andando a cercare una proprietà chiamata Project. Quindi devo fare un'altra classe che ha quella proprietà, e lo chiamo così:

client.Execute<List<ProjectContainer>>(request) 

Non voglio fare una classe di 'contenitore' per ogni entità, così ho pensato che ho trovato una soluzione intelligente per fare una classe che posso usare su tutti:

public class ListContainer<T> where T : IHarvestEntity 
{ 
    public T Item { get; set; } 
} 

Ma, naturalmente, il deserializzatore non ha alcuna idea di cui ha bisogno per mappare il nome dell'entità (o "Progetto") per la proprietà Item. Nella documentazione di restsharp ho scoperto che potevo usare [DeserializeAs(Name = "CustomProperty")] per dire al deserializzatore quale campo mappare a questa proprietà. Tuttavia, gli attributi consentono solo costanti, il che significa che non posso fare:

[DeserializeAs(Name = typeof(T).FullName)] 
public T Item { get; set; } 

Qualcuno sa una soluzione intelligente a questo? Quindi non devo creare 10 diverse classi di contenitori?

risposta

6

Suggerisco di utilizzare l'equivalente XPath per Json. Con Json.NET puoi parse la stringa e creare un oggetto dinamico.

Con SelectToken è possibile interrogare i valori o utilizzare Linq.

Il codice simile a questa (non ho provato):

// 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(); 

è possibile codificare i percorsi o configurare i percorsi come vuoi.

--- --- Modifica

Ecco un esempio che ho provato, convertendo la stringa JSON per un elenco di progetti.

var projects = JArray.Parse(response.Content).Select(r => new Project(r["project"]["name"].Value<string>())).ToList(); 
+0

oggetto dinamico è probabilmente la risposta alla domanda, ma un sacco di sviluppatori ti avvertirei di implementare semplicemente le classi XYZModel e usarle; ottieni tutti i vantaggi e le garanzie del tuo compilatore e non rischi (la stessa classe di) eccezioni di run-time. –

+1

Democraticamente penso che dovrei darti la taglia, ma sento che la risposta di Mikko risponde più correttamente alla mia situazione. Perché nel tuo esempio, perdo la mappatura automatica all'entità che è l'idea alla base di questo. Mi sbaglio di questo? – Chris

+0

La mappatura su un oggetto è possibile, usando l'oggetto progetto dalla risposta di Mikko: var project = new Project (o.SelectToken ("project"). SelectToken ("name"). Value) –

2

Per farla molto semplice, è possibile utilizzare List<dynamic> e accedere alla proprietà/proprietà per nome con una battuta.

var names = client.Execute<List<dynamic>>(request).Data.Select(
       item => item["project"]["name"]).ToList(); // list of names 

Se questo non è sufficiente, allora si potrebbe improvvisare il proprio mapper ed estrarre una raccolta di esempioProject casi:

var projects = client.Execute<List<dynamic>>(request).Data.Select(
       item => Map<Project>(item)).ToList(); // list of Project instances 

dove Map metodo potrebbe essere qualcosa di simile a

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); 
} 

dato Project è definito come

public class Project 
{ 
    public Project(string name) 
    { 
     Name = name; 
    } 
    public string Name { get; set; } 
}