Dapatkan nilai dari JToken yang mungkin tidak ada (praktik terbaik)

117

Apa praktik terbaik untuk mengambil nilai JSON yang bahkan mungkin tidak ada di C # menggunakan Json.NET ?

Saat ini saya berurusan dengan penyedia JSON yang mengembalikan JSON yang terkadang berisi pasangan kunci / nilai tertentu, dan terkadang tidak. Saya telah menggunakan (mungkin salah) metode ini untuk mendapatkan nilai saya (contoh untuk mendapatkan ganda):

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

Sekarang itu berfungsi dengan baik, tetapi jika ada banyak, itu tidak praktis. Saya akhirnya menulis metode ekstensi, dan hanya setelah menulisnya saya bertanya-tanya apakah mungkin saya bodoh ... bagaimanapun, berikut adalah metode ekstensi (saya hanya menyertakan kasus untuk double dan string, tetapi pada kenyataannya saya memiliki beberapa lebih):

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

Dan inilah contoh penggunaan metode ekstensi:

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

BTW, Maafkan apa yang mungkin merupakan pertanyaan yang sangat bodoh, karena sepertinya ada sesuatu yang harus ada fungsi bawaan untuk ... Saya memang mencoba Google, dan dokumentasi Json.NET , namun saya tidak kompeten dalam menemukan solusi untuk pertanyaan saya atau tidak jelas dalam dokumentasi.

Paul Hazen
sumber
Saya tahu ini agak terlambat, tetapi Anda mungkin ingin mencoba versi sederhana di GetValuebawah ini
LB

Jawaban:

210

Ini cukup banyak untuk apa metode generik Value(). Anda mendapatkan perilaku yang Anda inginkan jika menggabungkannya dengan tipe nilai nullable dan ??operator:

width = jToken.Value<double?>("width") ?? 100;
svick.dll
sumber
4
Ini adalah metode ekstensi.
Dave Van den Eynde
2
@PaulHazen, tidak seburuk itu ... Anda baru saja menemukan kembali roda itu saja.
devinbost
Ini tidak berfungsi jika "lebar" tidak ada di json dan JToken adalah null
Deepak
2
@Deepak Itu berhasil jika "lebar" tidak ada. Tentu saja tidak akan berhasil jika jTokenmemang demikian null, tapi bukan itu pertanyaan yang diajukan. Dan Anda dapat dengan mudah memperbaikinya dengan menggunakan nol operator kondisional: width = jToken?.Value<double?>("width") ?? 100;.
svick
1
JToken.Value<T>melempar pengecualian jika JToken adalah JValue
Kyle Delaney
22

Saya akan menulis GetValueseperti di bawah ini

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

Dengan cara ini Anda bisa mendapatkan nilai tidak hanya tipe dasar tetapi juga objek kompleks. Berikut ini contohnya

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");
LB
sumber
Itu cukup keren, tapi saya suka pemisahan kekhawatiran yang hanya memberi saya tipe data sederhana. Meskipun gagasan tentang pemisahan itu agak kabur saat berhubungan dengan penguraian JSON. Karena saya menerapkan model pengamat / yang dapat diamati (dengan mvvm juga), saya cenderung menyimpan semua penguraian saya di satu tempat, dan membuatnya tetap sederhana (bagian dari itu juga merupakan data yang tidak dapat diprediksi yang dikembalikan kepada saya).
Paul Hazen
@PaulHazen Saya tidak bisa mengatakan bahwa saya mengerti Anda. Pertanyaan Anda adalah retrieving JSON values that may not even existdan semua yang saya usulkan adalah mengubah GetValuemetode Anda . Saya pikir ini berfungsi seperti yang Anda inginkan
LB
Semoga saya bisa sedikit lebih jelas kali ini. Metode Anda bekerja dengan baik, dan akan mencapai apa yang saya inginkan. Namun, konteks yang lebih besar yang tidak dijelaskan dalam pertanyaan saya adalah bahwa kode tertentu yang saya kerjakan adalah kode yang saya ingin sangat dapat ditransfer. Meskipun dapat diperdebatkan bahwa metode Anda menghalangi, metode ini memperkenalkan kemampuan untuk menghilangkan nama objek dari GetValue <T>, yang merupakan pola yang ingin saya hindari demi memindahkan kode saya ke platform yang memiliki pengurai JSON yang lebih baik (katakanlah , Win8 misalnya). Jadi, untuk apa yang saya tanyakan, ya, kode Anda akan sempurna.
Paul Hazen
9

Inilah cara Anda dapat memeriksa apakah token itu ada:

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

Ia memeriksa apakah "Item" ada di "Hasil".

Ini adalah contoh yang TIDAK berfungsi yang menyebabkan pengecualian:

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

Anda cukup mengetik, dan itu akan melakukan konversi untuk Anda, misalnya

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

Ini akan secara otomatis kembali nulljika kunci tersebut tidak ada dalam objek, jadi tidak perlu mengujinya.

Dave Van den Eynde
sumber
1

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

misalnya

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

Downhillski
sumber
1

Ini menangani nulls

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

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

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