2012-08-13 16 views
47

TL; DR VersioneConversione di un JToken (o stringa) per un determinato tipo

Ho un oggetto di tipo JToken (ma può anche essere un string) e ho bisogno di convertirlo in un tipo di contenuto in la variabile type:

Type type = typeof(DateTime); /* can be any other Type like string, ulong etc */ 
var obj = jsonObject["date_joined"]; /* contains 2012-08-13T06:01:23Z+05:00 */ 
var result = Some_Way_To_Convert(type, obj); 

Quanto sopra result dovrebbe essere un oggetto DateTime con il valore riportato in date_joined.

Full Story

sto utilizzando sia RestSharp e Json.NET in un progetto Windows Phone e mi sono bloccato durante il tentativo di deserializzare risposte JSON da un API REST.

Quello che sto effettivamente cercando di realizzare è scrivere un metodo generico che possa facilmente mappare la mia risposta JSON alle mie entità CLR, proprio come si fa già con RestSharp. L'unico problema è che l'implementazione RestSharp predefinita non funziona per me e non riesce a analizzare correttamente il JSON poiché la risposta non sempre restituisce tutte le proprietà (non restituisco i campi che sono null dal server REST).

Ecco perché ho deciso di utilizzare Json.NET di Newtonsoft poiché ha un motore di deserializzazione Json molto più potente. Sfortunatamente, non supporta i nomi di proprietà/campi fuzzy come RestSharp (o non ne ho trovato nessuno), quindi non viene mappato correttamente alle entità CLR quando uso qualcosa come JsonConvert.DeserializeObject<User>(response.Content).

Ecco ciò che il mio JSON sembra (un esempio in realtà):

{ 
    "id" : 77239923, 
    "username" : "UzEE", 
    "email" : "[email protected]", 
    "name" : "Uzair Sajid", 
    "twitter_screen_name" : "UzEE", 
    "join_date" : "2012-08-13T05:30:23Z05+00", 
    "timezone" : 5.5, 
    "access_token" : { 
     "token" : "nkjanIUI8983nkSj)*#)([email protected]", 
     "scope" : [ "read", "write", "bake pies" ], 
     "expires" : 57723 
    }, 
    "friends" : [{ 
     "id" : 2347484", 
     "name" : "Bruce Wayne" 
    }, 
    { 
     "id" : 996236, 
     "name" : "Clark Kent" 
    }] 
} 

Ed ecco un esempio dei miei soggetti CLR:

class AccessToken 
{ 
    public string Token { get; set; } 
    public int Expires { get; set; } 
    public string[] Scope { get; set; } 
    public string Secret { get; set; } /* may not always be returned */ 
} 

class User 
{ 
    public ulong Id { get; set; } 
    public string UserName { get; set; } 
    public string Email { get; set; } 
    public string Name { get; set; } 
    public string TwitterScreenName { get; set; } 
    public DateTime JoinDate { get; set; } 
    public float Timezone { get; set; } 
    public bool IsOnline { get; set; } /* another field that might be blank e.g. */ 

    public AccessToken AccessToken { get; set; } 

    public List<User> Friends { get; set; } 
} 

Quello che voglio è un modo semplice per analizzare quanto sopra JSON negli oggetti CLR dati. Ho esaminato il codice sorgente RestSharp e ho visto il codice JsonDeserializer e sono stato in grado di scrivere un metodo di estensione generico DeserializeResponse<T> su JObject che dovrebbe restituire un oggetto di tipo T. La destinazione d'uso è qualcosa di simile:

var user = JObject.Parse(response.Content).DeserializeResponse<User>(); 

Il metodo di cui sopra dovrebbe analizzare la data risposta JSON a un oggetto entità utente. Ecco un frammento di codice reale di quello che sto facendo nel metodo DeserializeResponse<User> estensione (la sua base di codice RestSharp):

public static T DeserializeResponse<T>(this JObject obj) where T : new() 
{ 
    T result = new T(); 
    var props = typeof(T).GetProperties().Where(p => p.CanWrite).ToList(); 
    var objectDictionary = obj as IDictionary<string, JToken>; 

    foreach (var prop in props) 
    { 
     var name = prop.Name.GetNameVariants(CultureInfo.CurrentCulture).FirstOrDefault(n => objectDictionary.ContainsKey(n)); 
     var value = name != null ? obj[name] : null; 

     if (value == null) continue; 

     var type = prop.PropertyType; 

     if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) 
     { 
      type = type.GetGenericArguments()[0]; 
     } 

     // This is a problem. I need a way to convert JToken value into an object of Type type 
     prop.SetValue(result, ConvertValue(type, value), null); 
    } 

    return result; 
} 

Sto indovinando che la conversione dovrebbe essere una cosa davvero semplice da fare perché il suo un banale compito. Ma ho cercato un bel po 'di tempo e ancora non ho trovato il modo di farlo tramite Json.NET (e, a essere onesti, la documentazione è abbastanza per capire e manca qualche esempio).

Qualsiasi aiuto sarebbe davvero apprezzato.

+0

Qualsiasi aiuto qui? Questa è la seconda volta che ho fatto qualsiasi domanda su SO e tuttavia non ho mai avuto una risposta:/ –

+0

http://stackoverflow.com/questions/9589218/get-value-from-jtoken-that-may-not-exist- best practice – Guillaume

+0

@Guillaume Grazie. L'avevo già letto prima di postare. Il problema è che sto scrivendo un parser che dovrebbe essere in grado di gestire qualsiasi valore incontrato così un semplice 'jToken.value ()' non funzionerà dato che non so che sarebbe un 'double'. Non conosco il tipo prima mano. –

risposta

90

C'è un metodo ToObject ora.

var obj = jsonObject["date_joined"]; 
var result = obj.ToObject<DateTime>(); 

Funziona anche con qualsiasi tipo complesso, e obbediscono a regole JsonPropertyAttribute

var result = obj.ToObject<MyClass>(); 

public class MyClass 
{ 
    [JsonProperty("date_field")] 
    public DateTime MyDate {get;set;} 
} 
+2

+1 Questo è un modo molto più semplice di farlo che chiamare '.ToString()' e quindi deserializzarlo. Non sono sicuro al 100% quale sia la differenza sotto il cofano, ma questo è molto più sintatticamente sintattico. – theyetiman

+2

Se non si conosce il tipo come tempo di progettazione, esiste una versione sovraccaricata di ToObject() che accetta Type come parametro. Sono d'accordo. Questa è la soluzione più pulita. Dovrebbe davvero essere la "risposta". –

22
System.Convert.ChangeType(jtoken.ToString(), targetType); 

o

JsonConvert.DeserializeObject(jtoken.ToString(), targetType); 

--EDIT--

Uzair, Ecco un esempio completo solo per mostrarvi che lavorano

string json = @"{ 
     ""id"" : 77239923, 
     ""username"" : ""UzEE"", 
     ""email"" : ""[email protected]"", 
     ""name"" : ""Uzair Sajid"", 
     ""twitter_screen_name"" : ""UzEE"", 
     ""join_date"" : ""2012-08-13T05:30:23Z05+00"", 
     ""timezone"" : 5.5, 
     ""access_token"" : { 
      ""token"" : ""nkjanIUI8983nkSj)*#)([email protected]"", 
      ""scope"" : [ ""read"", ""write"", ""bake pies"" ], 
      ""expires"" : 57723 
     }, 
     ""friends"" : [{ 
      ""id"" : 2347484, 
      ""name"" : ""Bruce Wayne"" 
     }, 
     { 
      ""id"" : 996236, 
      ""name"" : ""Clark Kent"" 
     }] 
    }"; 

var obj = (JObject)JsonConvert.DeserializeObject(json); 
Type type = typeof(int); 
var i1 = System.Convert.ChangeType(obj["id"].ToString(), type); 
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type); 
+3

Entrambi questi non funzionano. 'System.Convert.ChangeType()' ha bisogno di un terzo parametro 'IFormatProvider'. E 'JsonConvert.DeserializeObject()' genera un'eccezione: "Rilevato carattere imprevisto durante l'analisi del valore: U. Percorso '', linea 0, posizione 0." –

+0

@UzairSajid Ho testato quei codici prima di postare. 'IFormatProvider' è facoltativo. Per la seconda parte. Se pubblichi il tuo codice + dati, posso provare a lavorarci sopra. –

+2

Non ha funzionato esattamente come hai detto (in realtà la firma del metodo per 'System.Convert.ChangeType()' è diversa su Windows Phone) perché ha bisogno di un terzo parametro 'IFormatProvider' obbligatorio. Ma sono riuscito a farlo funzionare passando un 'null' come terzo parametro e gestendo esplicitamente alcuni casi edge (come l'analisi di un valore di' "1" 'a' bool true' e una 'stringa' a' URI') per adesso. Vedremo se funziona ancora a lungo termine. –

1
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type); 

tiri un'eccezione di parsing d ue alle citazioni mancanti attorno al primo argomento (credo). Ho ottenuto il lavoro aggiungendo le virgolette:

var i2 = JsonConvert.DeserializeObject("\"" + obj["id"].ToString() + "\"", type); 
0

sono stato in grado di convertire usando sotto metodo per la mia WebAPI:

[HttpPost] 
public HttpResponseMessage Post(dynamic item) // Passing parameter as dynamic 
{ 
JArray itemArray = item["Region"]; // You need to add JSON.NET library 
JObject obj = itemArray[0] as JObject; // Converting from JArray to JObject 
Region objRegion = obj.ToObject<Region>(); // Converting to Region object 
} 
Problemi correlati