wake-up-neo.com

Konvertieren eines JToken (oder einer Zeichenfolge) in einen bestimmten Typ

TL; DR Version

Ich habe ein Objekt vom Typ JToken (kann aber auch eine string sein) und muss es in einen Typ konvertieren, der in der Variablen type enthalten ist:

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

Die obige result sollte ein DateTime-Objekt mit dem in date_joined angegebenen Wert sein.

Ganze Geschichte

Ich verwende sowohl RestSharp als auch Json.NET in einem Windows Phone-Projekt und stecke fest, während ich versuche, JSON-Antworten von einer REST-API zu deserialisieren.

Eigentlich versuche ich, eine generische Methode zu schreiben, mit der ich meine JSON-Antwort auf einfache Weise meinen CLR-Entitäten zuordnen kann, so wie Sie es mit RestSharp bereits tun können. Das einzige Problem ist, dass die standardmäßige RestSharp-Implementierung für mich nicht funktioniert und das JSON nicht erfolgreich analysiert werden kann, da die Antwort nicht immer alle Eigenschaften zurückgibt (ich gebe keine Felder zurück, die null von REST Server).

Aus diesem Grund habe ich mich für Json.NET von Newtonsoft entschieden, da es über eine viel leistungsstärkere Json-Deserialisierungs-Engine verfügt. Leider unterstützt es keine unscharfen Eigenschafts-/Feldnamen wie RestSharp (oder ich habe keine gefunden), sodass es meinen CLR-Entitäten auch nicht richtig zugeordnet wird, wenn ich so etwas wie say JsonConvert.DeserializeObject<User>(response.Content) verwende.

So sieht mein Json aus (ein Beispiel):

{
    "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"
    }]
}

Und hier ist ein Beispiel für meine CLR-Entitäten:

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

Was ich möchte, ist eine einfache Möglichkeit, den obigen JSON in die angegebenen CLR-Objekte zu zerlegen. Ich habe mich im RestSharp-Quellcode umgesehen und den JsonDeserializer-Code gesehen. Außerdem konnte ich eine generische Erweiterungsmethode DeserializeResponse<T> für JObject schreiben, die ein Objekt vom Typ T zurückgeben sollte. Der Verwendungszweck ist ungefähr so:

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

Die obige Methode sollte die angegebene Json-Antwort auf ein Benutzerentitätsobjekt analysieren. Hier ist ein tatsächlicher Codeausschnitt dessen, was ich in der DeserializeResponse<User>-Erweiterungsmethode mache (sie basiert auf RestSharp-Code):

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

Ich vermute, dass die Konvertierung eine wirklich einfache Sache sein sollte, da es eine triviale Aufgabe ist. Aber ich habe schon eine ganze Weile gesucht und immer noch keine Möglichkeit gefunden, dies über Json.NET zu tun (und um ehrlich zu sein, die Dokumentation ist ein bisschen zu verständlich und es fehlen einige Beispiele).

Jede Hilfe wäre wirklich dankbar.

57
Uzair Sajid

Es gibt jetzt eine ToObject-Methode.

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

Es funktioniert auch mit jedem komplexen Typ und folgt den JsonPropertyAttribute-Regeln

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

public class MyClass 
{ 
    [JsonProperty("date_field")]
    public DateTime MyDate {get;set;}
}
108
Softlion
System.Convert.ChangeType(jtoken.ToString(), targetType);

oder

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

--EDIT--

Uzair, Hier ist ein vollständiges Beispiel, um Ihnen zu zeigen, dass sie funktionieren

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);
22
L.B
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type);

wirft eine Analyse-Ausnahme wegen fehlender Anführungszeichen um das erste Argument (denke ich). Ich habe es durch die Anführungszeichen zum Laufen gebracht:

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

Ich konnte die folgende Methode für meine WebAPI konvertieren:

[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