Obtenha valor do JToken que pode não existir (práticas recomendadas)

117

Qual é a prática recomendada para recuperar valores JSON que podem nem mesmo existir em C # usando Json.NET ?

No momento, estou lidando com um provedor JSON que retorna JSON que às vezes contém determinados pares de chave / valor e às vezes não. Tenho usado (talvez incorretamente) este método para obter meus valores (exemplo para obter um duplo):

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

Isso funciona bem, mas quando há muitos deles é complicado. Acabei escrevendo um método de extensão e só depois de escrevê-lo me pergunto se não estava sendo burro ... de qualquer forma, aqui está o método de extensão (eu só incluo casos para double e string, mas na realidade tenho alguns Mais):

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

E aqui está um exemplo de uso do método de extensão:

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

BTW, por favor, perdoe o que pode ser uma pergunta realmente estúpida, já que parece que algo deveria haver uma função embutida para ... Eu tentei a documentação do Google e Json.NET , no entanto, sou inepto em encontrar a solução para minha dúvida ou não está claro na documentação.

Paul Hazen
fonte
Eu sei que é um pouco tarde, mas você pode tentar esta versão simplificada GetValueabaixo
LB

Respostas:

210

É basicamente para isso que Value()serve o método genérico . Você obtém exatamente o comportamento que deseja se combiná-lo com tipos de valor anuláveis ​​e o ??operador:

width = jToken.Value<double?>("width") ?? 100;
Svick
fonte
4
É um método de extensão.
Dave Van den Eynde
2
@PaulHazen, não é tão ruim ... Você só reinventou a roda um pouco.
devinbost
Isso não funciona se "largura" não existe no json e JToken é nulo
Deepak
2
@Deepak Funciona se "largura" não existir. Claro que não funciona se jTokenfor null, mas não é isso que a pergunta feita. E você pode facilmente corrigir isso usando o nulo operador condicional: width = jToken?.Value<double?>("width") ?? 100;.
svick
1
JToken.Value<T>lança uma exceção se o JToken for um JValue
Kyle Delaney
22

Eu escreveria GetValuecomo abaixo

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

Dessa forma, você pode obter o valor não apenas dos tipos básicos, mas também de objetos complexos. Aqui está um exemplo

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");
LIBRA
fonte
Isso é muito legal, mas eu gosto da separação de preocupações que apenas obter tipos de dados simples me proporciona. Embora a noção dessa separação seja um pouco confusa quando se trata de análise JSON. Como eu implemento um modelo observável / observável (com mvvm também), tendo a manter todas as minhas análises em um só lugar e simples (parte disso também é a imprevisibilidade dos dados retornados para mim).
Paul Hazen
@PaulHazen Não posso dizer que te entendo. Sua pergunta foi retrieving JSON values that may not even existe tudo que propus foi mudar seu GetValuemétodo. Acho que funciona como você deseja
LB
Espero poder ser um pouco mais claro desta vez. Seu método funciona muito bem e realizaria exatamente o que desejo. No entanto, o contexto maior não explicado em minha pergunta é que o código específico no qual estou trabalhando é um código que desejo que seja altamente transferível. Embora seja discutível que seu método atrapalhe, ele introduz a capacidade de desserializar objetos de GetValue <T>, que é um padrão que desejo evitar para mover meu código para uma plataforma que tenha um analisador JSON melhor (digamos , Win8 por exemplo). Então, pelo que perguntei, sim, seu código seria perfeito.
Paul Hazen
9

Veja como você pode verificar se o token existe:

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

Ele verifica se "Itens" existe em "Resultado".

Este é um exemplo que NÃO funciona e causa exceção:

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

Você pode simplesmente fazer o typecast e ele fará a conversão para você, por exemplo

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

Ele retornará automaticamente nullse a referida chave não estiver presente no objeto, portanto, não há necessidade de testá-la.

Dave Van den Eynde
fonte
1

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

por exemplo

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

Downhillski
fonte
1

Isso cuida de nulos

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

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

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