Erhalten Sie Wert von JToken, der möglicherweise nicht vorhanden ist (Best Practices)

117

Was ist die beste Vorgehensweise zum Abrufen von JSON-Werten, die möglicherweise nicht einmal in C # mit Json.NET vorhanden sind? ?

Im Moment habe ich es mit einem JSON-Anbieter zu tun, der JSON zurückgibt, das manchmal bestimmte Schlüssel / Wert-Paare enthält und manchmal nicht. Ich habe diese Methode (möglicherweise falsch) verwendet, um meine Werte abzurufen (Beispiel für das Abrufen eines Double):

if(null != jToken["width"])
    width = double.Parse(jToken["width"].ToString());
else
    width = 100;

Das funktioniert gut, aber wenn es viele gibt, ist es umständlich. Am Ende habe ich eine Erweiterungsmethode geschrieben, und erst nachdem ich sie geschrieben habe, habe ich mich gefragt, ob ich vielleicht dumm war ... hier ist die Erweiterungsmethode (ich schließe nur Fälle für Double und String ein, aber in Wirklichkeit habe ich einige Mehr):

public static T GetValue<T>(this JToken jToken, string key,
                            T defaultValue = default(T))
{
    T returnValue = defaultValue;

    if (jToken[key] != null)
    {
        object data = null;
        string sData = jToken[key].ToString();

        Type type = typeof(T);

        if (type is double)
            data = double.Parse(sData);
        else if (type is string)
            data = sData;

        if (null == data && type.IsValueType)
            throw new ArgumentException("Cannot parse type \"" + 
                type.FullName + "\" from value \"" + sData + "\"");

        returnValue = (T)Convert.ChangeType(data, 
            type, CultureInfo.InvariantCulture);
    }

    return returnValue;
}

Und hier ist ein Beispiel für die Verwendung der Erweiterungsmethode:

width = jToken.GetValue<double>("width", 100);

Übrigens, bitte verzeihen Sie, was eine wirklich dumme Frage sein kann, da es so aussieht, als ob es eine eingebaute Funktion für ... geben sollte. Ich habe Google und die Json.NET- Dokumentation ausprobiert , bin jedoch entweder nicht in der Lage , die Lösung für diese zu finden meine Frage oder es ist nicht klar in der Dokumentation.

Paul Hazen
quelle
Ich weiß, dass es etwas spät ist, aber vielleicht möchten Sie diese vereinfachte Version von GetValueunten ausprobieren
LB

Antworten:

210

Dies ist so ziemlich das, wofür die generische Methode Value()ist. Sie erhalten genau das gewünschte Verhalten, wenn Sie es mit nullbaren Werttypen und dem ??Operator kombinieren :

width = jToken.Value<double?>("width") ?? 100;
svick
quelle
4
Es ist eine Erweiterungsmethode.
Dave Van den Eynde
2
@PaulHazen, es ist nicht so schlimm ... Du hast das Rad nur ein bisschen neu erfunden, das ist alles.
Devinbost
Dies funktioniert nicht, wenn "width" im json nicht vorhanden ist und JToken null ist
Deepak
2
@Deepak Es funktioniert, wenn "width" nicht existiert. Natürlich funktioniert es nicht, wenn es so jTokenist null, aber das ist nicht das, was die Frage gestellt hat. Und Sie können dies leicht beheben, indem Sie den Null-Bedingungsoperator verwenden : width = jToken?.Value<double?>("width") ?? 100;.
Svick
1
JToken.Value<T>löst eine Ausnahme aus, wenn das JToken ein JValue ist
Kyle Delaney
22

Ich würde GetValuewie folgt schreiben

public static T GetValue<T>(this JToken jToken, string key, T defaultValue = default(T))
{
    dynamic ret = jToken[key];
    if (ret == null) return defaultValue;
    if (ret is JObject) return JsonConvert.DeserializeObject<T>(ret.ToString());
    return (T)ret;
}

Auf diese Weise können Sie nicht nur den Wert der Basistypen, sondern auch komplexer Objekte ermitteln. Hier ist ein Beispiel

public class ClassA
{
    public int I;
    public double D;
    public ClassB ClassB;
}
public class ClassB
{
    public int I;
    public string S;
}

var jt = JToken.Parse("{ I:1, D:3.5, ClassB:{I:2, S:'test'} }");

int i1 = jt.GetValue<int>("I");
double d1 = jt.GetValue<double>("D");
ClassB b = jt.GetValue<ClassB>("ClassB");
PFUND
quelle
Das ist ziemlich cool, aber ich mag die Trennung von Bedenken, die mir nur das Erhalten einfacher Datentypen gibt. Obwohl der Begriff dieser Trennung beim JSON-Parsing etwas verschwommen ist. Da ich ein Beobachter- / Beobachtungsmodell implementiere (auch mit mvvm), neige ich dazu, alle meine Analysen an einem Ort zu halten und es einfach zu halten (ein Teil davon ist auch die Unvorhersehbarkeit der an mich zurückgegebenen Daten).
Paul Hazen
@ PaulHazen Ich kann nicht sagen, dass ich dich verstehe. Ihre Frage war retrieving JSON values that may not even existund alles, was ich vorgeschlagen habe, war, Ihre GetValueMethode zu ändern . Ich denke, es funktioniert so, wie Sie wollen
LB
Hoffentlich kann ich diesmal etwas klarer sein. Ihre Methode funktioniert hervorragend und würde genau das erreichen, was ich will. Der größere Kontext, der in meiner Frage nicht erläutert wird, ist jedoch, dass der bestimmte Code, an dem ich arbeite, Code ist, den ich in hohem Maße übertragbar sein möchte. Es ist zwar fraglich, ob Ihre Methode im Weg steht, bietet jedoch die Möglichkeit, Objekte aus GetValue <T> zu deserialisieren. Dies ist ein Muster, das ich vermeiden möchte, um meinen Code auf eine Plattform mit einem besseren JSON-Parser zu verschieben (z. B.) , Win8 zum Beispiel). Also, für das, was ich gefragt habe, ja, Ihr Code wäre perfekt.
Paul Hazen
9

So können Sie überprüfen, ob das Token vorhanden ist:

if (jobject["Result"].SelectToken("Items") != null) { ... }

Es wird geprüft, ob "Items" in "Result" vorhanden ist.

Dies ist ein NICHT funktionierendes Beispiel, das eine Ausnahme verursacht:

if (jobject["Result"]["Items"] != null) { ... }
Artur Alexeev
quelle
3

Sie können einfach typisieren und es wird die Konvertierung für Sie durchführen, z

var with = (double?) jToken[key] ?? 100;

Es wird automatisch zurückgegeben, nullwenn der Schlüssel nicht im Objekt vorhanden ist, sodass kein Test erforderlich ist.

Dave Van den Eynde
quelle
1

TYPE variable = jsonbody["key"]?.Value<TYPE>() ?? DEFAULT_VALUE;

z.B

bool attachMap = jsonbody["map"]?.Value<bool>() ?? false;

Downhillski
quelle
1

Dies kümmert sich um Nullen

var body = JObject.Parse("anyjsonString");

body?.SelectToken("path-string-prop")?.ToString();

body?.SelectToken("path-double-prop")?.ToObject<double>();
Max
quelle