รับคุณค่าจาก 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);

BTW โปรดยกโทษให้กับสิ่งที่อาจเป็นคำถามที่โง่จริงๆเนื่องจากดูเหมือนว่ามีบางสิ่งที่ควรมีในตัวสำหรับ ... ฉันลองใช้ Google และเอกสารJson.NETแล้ว แต่ฉันก็ไม่สามารถหาวิธีแก้ปัญหาได้ คำถามของฉันหรือไม่ชัดเจนในเอกสาร


ฉันรู้ว่ามันช้าไปหน่อย แต่คุณอาจต้องการลองรุ่นที่เรียบง่ายGetValueด้านล่างนี้
LB

คำตอบ:


210

นี่เป็นวิธีการทั่วไปValue()สำหรับ คุณจะได้รับพฤติกรรมที่คุณต้องการถ้าคุณรวมเข้ากับประเภทค่าว่างและตัว??ดำเนินการ:

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

4
มันเป็นวิธีการขยาย
Dave Van den Eynde

2
@PaulHazen ไม่ได้แย่ขนาดนั้น ... คุณเพิ่งคิดค้นล้อใหม่อีกนิดก็เป็นได้
devinbost

สิ่งนี้ใช้ไม่ได้หากไม่มี "width" ใน json และ JToken เป็นโมฆะ
Deepak

2
@Deepak ใช้งานได้ถ้าไม่มี "width" แน่นอนว่ามันใช้ไม่ได้ถ้าjTokenเป็นnullแต่นั่นไม่ใช่สิ่งที่ถาม และคุณสามารถแก้ไขปัญหาที่โดยใช้ตัวดำเนินการตามเงื่อนไข width = jToken?.Value<double?>("width") ?? 100;null:
svick

1
JToken.Value<T>มีข้อยกเว้นหาก JToken เป็น JValue
Kyle Delaney

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 ด้วย) ฉันมักจะเก็บการแยกวิเคราะห์ทั้งหมดไว้ในที่เดียวและทำให้มันง่าย (ส่วนหนึ่งก็คือความไม่สามารถคาดเดาได้ของข้อมูลที่ส่งกลับมาให้ฉันด้วย)
Paul Hazen

@PaulHazen ฉันไม่สามารถพูดได้ว่าฉันเข้าใจคุณ คำถามของคุณคือretrieving JSON values that may not even existและทั้งหมดที่ฉันเสนอคือเปลี่ยนGetValueวิธีการของคุณ ฉันคิดว่ามันได้ผลตามที่คุณต้องการ
LB

หวังว่าคราวนี้ฉันจะชัดเจนขึ้นอีกนิดนะ วิธีการของคุณได้ผลดีและจะสำเร็จตามที่ฉันต้องการ อย่างไรก็ตามบริบทที่ใหญ่กว่าที่ฉันไม่ได้อธิบายไว้ในคำถามของฉันคือรหัสเฉพาะที่ฉันกำลังทำงานอยู่คือรหัสที่ฉันต้องการให้โอนได้สูง แม้ว่าจะเป็นที่ถกเถียงกันอยู่ว่าวิธีการของคุณไปในทางที่ผิด แต่ก็แนะนำความสามารถในการ deserialize วัตถุจาก GetValue <T> ซึ่งเป็นรูปแบบที่ฉันต้องการหลีกเลี่ยงเพื่อประโยชน์ในการย้ายรหัสไปยังแพลตฟอร์มที่มีตัวแยกวิเคราะห์ JSON ที่ดีกว่า (พูด , Win8 เป็นต้น). ดังนั้นสำหรับสิ่งที่ฉันถามใช่รหัสของคุณจะสมบูรณ์แบบ
Paul Hazen

9

นี่คือวิธีตรวจสอบว่ามีโทเค็นอยู่หรือไม่:

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

ตรวจสอบว่ามี "รายการ" อยู่ใน "ผลลัพธ์" หรือไม่

นี่เป็นตัวอย่างที่ใช้ไม่ได้ซึ่งทำให้เกิดข้อยกเว้น:

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

3

คุณสามารถพิมพ์แคสต์และมันจะทำการแปลงให้คุณเช่น

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

มันจะส่งคืนโดยอัตโนมัติnullหากไม่มีคีย์ดังกล่าวอยู่ในออบเจ็กต์ดังนั้นจึงไม่จำเป็นต้องทดสอบมัน



1

สิ่งนี้จะดูแลค่าว่าง

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

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

body?.SelectToken("path-double-prop")?.ToObject<double>();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.