web-dev-qa-db-fra.com

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

TL; DR Version

J'ai un objet de type JToken (mais je peux aussi être un string) et je dois le convertir en un type contenu dans la variable 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);

La result ci-dessus devrait ê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é lors de la tentative de désérialisation des réponses JSON à partir d'une API REST.

Ce que j'essaie en réalité d'accomplir est d'écrire une méthode générique qui mappe facilement ma réponse JSON dans mes entités CLR, comme vous pouvez déjà le faire avec RestSharp. Le seul problème est que l’implémentation RestSharp par défaut ne fonctionne pas pour moi et ne parvient pas à analyser correctement le JSON car la réponse ne renvoie pas toujours toutes les propriétés (je ne renvoie pas les champs qui sont null à partir du REST serveur).

C'est pourquoi j'ai décidé d'utiliser Json.NET de Newtonsoft, car il dispose d'un moteur de désériorisation Json beaucoup plus puissant. Malheureusement, il ne prend pas en charge les noms de propriétés/champs flous comme RestSharp (ou je n’en ai trouvé aucun), il n’est donc pas mappé correctement sur mes entités CLR lorsque j’utilise quelque chose comme, par exemple, JsonConvert.DeserializeObject<User>(response.Content)

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

{
    "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)*#)(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 parcouru le code source RestSharp et le code JsonDeserializer. J'ai également é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 ressemble à ceci:

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

La méthode ci-dessus doit analyser la réponse Json donnée à un objet entité User. Voici un extrait de code de ce que je fais dans la méthode d'extension DeserializeResponse<User> (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 devine que la conversion devrait être une chose très simple à faire puisque c'est une tâche triviale. Mais je cherche depuis un certain temps et je n’ai toujours pas trouvé le moyen de le faire via Json.NET (et soyons honnêtes, la documentation est un peu compliquée à comprendre et manque d’exemples).

Toute aide serait vraiment appréciée.

57
Uzair Sajid

Il existe une méthode ToObject maintenant.

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;}
}
108
Softlion
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"" : ""[email protected]"",
        ""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
L.B
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type);

lève une exception d'analyse en raison de citations manquantes autour du premier argument (je pense). Je l'ai fait fonctionner en ajoutant les guillemets:

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

J'ai été capable de 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
Chirag