Получите ценность от 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;

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;


1

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

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

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

body?.SelectToken("path-double-prop")?.ToObject<double>();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.