Conversion D'un JToken (ou d'une chaîne) en un Type donné

TL; version DR

J'ai un objet de type JToken, mais peut aussi être un string) et j'ai besoin de le convertir en un Type contenu dans le type variable:

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

Le result ci-dessus doit être un objet DateTime avec la valeur donnée dans date_joined.

Histoire Complète

J'utilise à la fois RestSharp et Json.NET dans un projet Windows Phone et je suis bloqué en essayant de désérialiser les réponses JSON à partir d'une API REST.

Ce que j'essaie de faire accomplir est d'écrire une méthode générique qui peut facilement mapper ma réponse JSON dans mes entités CLR, un peu comme vous pouvez le faire avec RestSharp déjà. Le seul problème est que l'implémentation RestSharp par défaut ne fonctionne pas pour moi et qu'elle ne parvient pas à analyser avec succès le JSON puisque la réponse ne renvoie pas toujours toutes les propriétés (Je ne retourne pas les champs qui sont null du serveur REST).

C'est pourquoi J'ai décidé D'utiliser Newtonsoft Json.NET comme il a un Json beaucoup plus puissant moteur de désérialisation. Malheureusement, il ne supporte pas les noms de propriétés/champs flous comme RestSharp (ou je n'en ai pas trouvé), donc il ne correspond pas correctement à mes entités CLR lorsque j'utilise quelque chose comme say JsonConvert.DeserializeObject<User>(response.Content).

Voici à quoi ressemble mon Json (un exemple en fait):

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

Et voici un exemple de mes entités 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; }
}

Ce que je veux, c'est un moyen simple d'analyser le JSON ci-dessus dans les objets CLR donnés. J'ai regardé autour du code source RestSharp et j'ai vu le code JsonDeserializer et j'ai été capable d'écrire une méthode d'extension générique DeserializeResponse<T> sur JObject qui devrait renvoyer un objet de type T. L'utilisation prévue est quelque chose comme ceci:

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

La méthode ci-dessus doit analyser la réponse JSON donnée à un objet D'entité utilisateur. Voici un extrait de code réel de ce que je fais dans la méthode d'extension DeserializeResponse<User> (elle est basée sur le code 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;
}

Je suppose que la conversion devrait être une chose vraiment simple à faire depuis ses une tâche triviale. Mais je cherche depuis un bon moment maintenant et je n'ai toujours pas trouvé un moyen de le faire via Json.NET (et soyons honnêtes, la documentation est un peu à comprendre et manque d'exemples).

Toute aide serait vraiment appréciée.

55
demandé sur Uzair Sajid 2012-08-13 05:05:25

4 réponses

Il existe maintenant une méthode ToObject.

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

Il fonctionne également avec n'importe quel type complexe et obéit aux règles JsonPropertyAttribute

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

public class MyClass 
{ 
    [JsonProperty("date_field")]
    public DateTime MyDate {get;set;}
}
103
répondu Softlion 2013-12-05 18:20:31
System.Convert.ChangeType(jtoken.ToString(), targetType);

Ou

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

--modifier--

Uzair, Voici un exemple complet juste pour vous montrer qu'ils travaillent

string json = @"{
        ""id"" : 77239923,
        ""username"" : ""UzEE"",
        ""email"" : ""uzee@email.net"",
        ""name"" : ""Uzair Sajid"",
        ""twitter_screen_name"" : ""UzEE"",
        ""join_date"" : ""2012-08-13T05:30:23Z05+00"",
        ""timezone"" : 5.5,
        ""access_token"" : {
            ""token"" : ""nkjanIUI8983nkSj)*#)(kjb@K"",
            ""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);
22
répondu L.B 2013-06-18 19:13:35
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type);

Lance une exception d'analyse en raison des guillemets manquants autour du premier argument (je pense). Je l'ai fait fonctionner en ajoutant les guillemets:

var i2 = JsonConvert.DeserializeObject("\"" + obj["id"].ToString() + "\"", type);
1
répondu barjac 2013-01-30 14:19:57

J'ai pu convertir en utilisant la méthode ci-dessous pour mon 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
}
0
répondu Chirag 2013-12-17 22:58:32