Получите ценность от JToken, которая может не существовать (лучшие практики)

117

Как лучше всего получать значения JSON, которые могут даже не существовать в C #, с помощью Json.NET ?

Прямо сейчас я имею дело с поставщиком JSON, который возвращает JSON, который иногда содержит определенные пары ключ / значение, а иногда нет. Я использовал (возможно, неправильно) этот метод для получения своих значений (например, для получения двойника):

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

Теперь это работает нормально, но когда их много, это громоздко. Я закончил тем, что написал метод расширения, и только после его написания я задался вопросом, не был ли я глуп ... в любом случае, вот метод расширения (я включаю только случаи для двойных и строковых, но на самом деле у меня довольно много Больше):

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

А вот пример использования метода расширения:

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

Кстати, простите, что может быть действительно глупым вопросом, поскольку кажется, что для чего-то должна быть встроенная функция ... Я пробовал документацию Google и Json.NET , однако я либо неспособен найти решение для мой вопрос или в документации непонятно.

Пол Хазен
источник
Я знаю, что уже немного поздно, но вы можете попробовать эту упрощенную версию GetValueниже
LB

Ответы:

210

Это в значительной степени то, для чего предназначен общий метод Value(). Вы получите именно то поведение, которое хотите, если объедините его с типами значений, допускающими значение NULL, и ??оператором:

width = jToken.Value<double?>("width") ?? 100;
svick
источник
4
Это метод расширения.
Дэйв Ван ден Эйнде
2
@PaulHazen, все не так уж плохо ... Ты только что изобрел колесо заново, вот и все.
devinbost
Это не работает, если "width" не существует в json и JToken имеет значение null
Deepak
2
@Deepak Это работает, если "ширина" не существует. Конечно, это не сработает, если jTokenэто так null, но вопрос не об этом. И вы можете легко исправить с помощью Нулевого условного оператора: width = jToken?.Value<double?>("width") ?? 100;.
svick
1
JToken.Value<T>выдает исключение, если JToken является JValue
Кайл Делани
22

Я бы написал GetValueкак ниже

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

Таким образом можно получить значение не только основных типов, но и сложных объектов. Вот образец

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");
ФУНТ
источник
Это довольно круто, но мне нравится разделение проблем, которое дает мне только получение простых типов данных. Хотя понятие этого разделения немного размыто, когда дело доходит до парсинга JSON. Поскольку я реализую модель наблюдателя / наблюдаемого (также с mvvm), я стараюсь хранить весь свой синтаксический анализ в одном месте и сохранять его простым (частью этого также является непредсказуемость возвращаемых мне данных).
Пол Хазен
@PaulHazen Я не могу сказать, что понимаю тебя. Ваш вопрос был, retrieving JSON values that may not even existи все, что я предложил, это изменить ваш GetValueметод. Я думаю, это работает так, как вы хотите
LB
Надеюсь, на этот раз я могу быть более ясным. Ваш метод отлично работает и дает именно то, что я хочу. Однако более широкий контекст, не объясняемый в моем вопросе, заключается в том, что конкретный код, над которым я работаю, - это код, который я хочу легко переносить. Хотя можно утверждать, что ваш метод мешает, он вводит возможность десериализации объектов из GetValue <T>, что является шаблоном, которого я хочу избежать ради переноса моего кода на платформу с лучшим парсером JSON (скажем, , Win8 например). Итак, для того, о чем я просил, да, ваш код был бы идеальным.
Paul Hazen
9

Вот как вы можете проверить, существует ли токен:

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

Он проверяет, существует ли «Items» в «Result».

Это НЕ рабочий пример, вызывающий исключение:

if (jobject["Result"]["Items"] != null) { ... }
Артур Алексеев
источник
3

Вы можете просто привести тип, и он выполнит преобразование за вас, например

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

Он автоматически вернется, nullесли указанный ключ отсутствует в объекте, поэтому нет необходимости проверять его.

Дэйв Ван ден Эйнде
источник
1

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

например

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

Downhillski
источник
1

Это заботится о нулях

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

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

body?.SelectToken("path-double-prop")?.ToObject<double>();
Максимум
источник