Отримайте значення від 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(). Ви отримуєте саме таку поведінку, яку хочете, якщо поєднувати її з типами нульових значень та ??оператором:

width = jToken.Value<double?>("width") ?? 100;

4
Це метод розширення.
Дейв Ван ден Ейнде

2
@PaulHazen, це не так вже й погано ... Ви просто переробили колесо трохи.
devinbost

Це не працює, якщо "ширина" не існує в json, а JToken є нульовим
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, наприклад). Отже, для того, що я попросив, так, ваш код був би ідеальним.
Пол Хазен

9

Ось як можна перевірити, чи маркер існує:

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

Він перевіряє, чи існує "Елементи" в "Результат".

Це НЕ робочий приклад, який викликає виняток:

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.