Obtenir une valeur de JToken qui n'existe peut-être pas (meilleures pratiques)

117

Quelle est la meilleure pratique pour récupérer des valeurs JSON qui n'existent même pas en C # à l'aide de Json.NET ?

Pour le moment, j'ai affaire à un fournisseur JSON qui renvoie JSON qui contient parfois certaines paires clé / valeur, et parfois non. J'ai utilisé (peut-être incorrectement) cette méthode pour obtenir mes valeurs (exemple pour obtenir un double):

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

Maintenant cela fonctionne bien, mais quand il y en a beaucoup, c'est encombrant. J'ai fini par écrire une méthode d'extension, et ce n'est qu'après l'avoir écrite que je me suis demandé si j'étais peut-être stupide ... de toute façon, voici la méthode d'extension (je n'inclus que des cas pour double et string, mais en réalité j'en ai pas mal plus):

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

Et voici un exemple d'utilisation de la méthode d'extension:

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

BTW, veuillez pardonner ce qui peut être une question vraiment stupide, car il semble qu'il devrait y avoir une fonction intégrée pour ... J'ai essayé la documentation Google et Json.NET , mais je suis soit incapable de trouver la solution à ma question ou ce n'est pas clair dans la documentation.

Paul Hazen
la source
Je sais qu'il est un peu tard, mais vous voudrez peut-être essayer cette version simplifiée GetValueci
LB

Réponses:

210

C'est à peu près à quoi Value()sert la méthode générique . Vous obtenez exactement le comportement souhaité si vous le combinez avec des types de valeur Nullable et l' ??opérateur:

width = jToken.Value<double?>("width") ?? 100;
svick
la source
4
C'est une méthode d'extension.
Dave Van den Eynde
2
@PaulHazen, ce n'est pas si mal ... Vous venez de réinventer un peu la roue, c'est tout.
devinbost
Cela ne fonctionne pas si "width" n'existe pas dans le json et que JToken est nul
Deepak
2
@Deepak Cela fonctionne si "width" n'existe pas. Bien sûr, cela ne fonctionne pas si jTokenc'est le cas null, mais ce n'est pas ce que la question a posée. Et vous pouvez facilement corriger cela en utilisant le nul opérateur conditionnel: width = jToken?.Value<double?>("width") ?? 100;.
svick
1
JToken.Value<T>lève une exception si le JToken est un JValue
Kyle Delaney
22

J'écrirais GetValuecomme ci-dessous

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

De cette façon, vous pouvez obtenir la valeur non seulement des types de base, mais également des objets complexes. Voici un échantillon

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");
KG
la source
C'est plutôt cool, mais j'aime la séparation des préoccupations que ne me procure que des types de données simples. Bien que la notion de cette séparation soit un peu floue en ce qui concerne l'analyse JSON. Depuis que j'implémente un modèle d'observateur / observable (avec mvvm également), j'ai tendance à garder toute mon analyse au même endroit et à rester simple (une partie de cela est également l'imprévisibilité des données qui me sont renvoyées).
Paul Hazen
@PaulHazen Je ne peux pas dire que je vous comprends. Votre question était retrieving JSON values that may not even existet tout ce que j'ai proposé était de changer votre GetValueméthode. Je pense que cela fonctionne comme vous le souhaitez
LB
J'espère pouvoir être un peu plus clair cette fois. Votre méthode fonctionne très bien et accomplirait exactement ce que je veux. Cependant, le plus grand contexte non expliqué dans ma question est que le code particulier sur lequel je travaille est un code que je veux être hautement transférable. Bien qu'il soit discutable que votre méthode gêne, elle introduit la possibilité de désérialiser des objets de GetValue <T>, qui est un modèle que je veux éviter pour déplacer mon code vers une plate-forme qui a un meilleur analyseur JSON (par exemple , Win8 par exemple). Donc, pour ce que j'ai demandé, oui, votre code serait parfait.
Paul Hazen
9

Voici comment vous pouvez vérifier si le jeton existe:

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

Il vérifie si "Items" existe dans "Result".

Ceci est un exemple qui ne fonctionne PAS qui provoque une exception:

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

Vous pouvez simplement faire un typage, et il effectuera la conversion pour vous, par exemple

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

Il retournera automatiquement nullsi ladite clé n'est pas présente dans l'objet, il n'est donc pas nécessaire de la tester.

Dave Van den Eynde
la source
1

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

par exemple

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

Ski alpin
la source
1

Cela prend en charge les valeurs nulles

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

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

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