C# JSON จาก response

ผมทดลองสร้าง new project ใหม่ โดยเอาโค๊ดที่ได้รับการสอนมาทดลองรัน
จาก https://pastebin.com/raw/biDFeGZb 
โดยค่อย ๆ ใส่ ที่ละ ส่วน ลงในฟอร์ม ในฟอร์ม มีแค่ 2 object
1. textbox search keyword
2. bottom สำหรับ เรียก resp
โครงสร้าง JSON (เผื่อผิดที่โครงสร้างใช้คนละอัน จึงส่งมาให้เพื่อจะเป็นอันเดียวกัน อันนี้ ใช้เฉพาะ เรียนรู้เท่านั้นครับ)
JSON >> https://pastebin.com/iRpG2Hiu
ผม ใช้ json parser online จากอันนี้ http://json.parser.online.fr/ เพื่อดูว่า อ้างอิง ผิดลำดับหรือเปล่า? ผลที่ได้ออกมาตามรูปนี้ >>

CODE >> https://pastebin.com/LpenQj52
หรือ 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using FireSharp.Config;
using FireSharp.Response;
using FireSharp.Interfaces;
using Newtonsoft.Json.Linq;
using FireSharp;

namespace AuthMatrix
{
    public partial class Form1 : Form
    {
        IFirebaseClient client;
        IFirebaseConfig config = new FirebaseConfig
        {
            AuthSecret = "XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx",
            BasePath = "https://a935099486553.firebaseio.com/"
        };
        List<string> DName = new List<string&gt;();
        List<string> DValue = new List<string&gt;();
        List<string> DPath = new List<string&gt;();
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            IFirebaseClient client = new FirebaseClient(config);

            FirebaseResponse response = client.Get("");
            JObject o = JObject.Parse(response.Body);
            AddValtoList(o);
            /*string checkdata = "";
            for (int i = 0; i < DName.Count; i++)
            {
                checkdata += String.Format("{0}. {1} : {2} ({3})\n", i + 1, DName[i], DValue[i], DPath[i]);[/i][/i][/i]
[i][i][i]            }[/i][/i][/i]
[i][i][i]            MessageBox.Show(checkdata);[/i][/i][/i]
[i][i][i]*/[/i][/i][/i]
[i][i][i]        }[/i][/i][/i]
[i][i][i]        private void AddVal(string n, string v, string p)[/i][/i][/i]
[i][i][i]        {[/i][/i][/i]
[i][i][i]            DName.Add(n);[/i][/i][/i]
[i][i][i]            DValue.Add(v);[/i][/i][/i]
[i][i][i]            DPath.Add(p);[/i][/i][/i]
[i][i][i]        }[/i][/i][/i]
[i][i][i]        private void AddValtoList(JObject o)[/i][/i][/i]
[i][i][i]        {[/i][/i][/i]
[i][i][i]            List<string> l1 = o.Properties().Select(p => p.Name).ToList();[/i][/i][/i]
[i][i][i]            foreach (var n1 in l1)//ถ้าใช้ string จะerror จึงเปลี่ยนเป็น var ผ่านได้[/i][/i][/i]
[i][i][i]            {[/i][/i][/i]
[i][i][i]                //List<string> l3 = ((JObject)o[n1]["Task"]).Properties().Select(p => p.Name).ToList();[/i][/i][/i]

[i][i][i]                foreach (JProperty n3 in o[n1])//ผมแก้บรรทัดนี้ ให้สอดคล้องกับโครงสร้าง JSON ที่ได้มา[/i][/i][/i]
[i][i][i]                {[/i][/i][/i]
[i][i][i]                    foreach (JProperty n4 in o[n1]["Task"]) //แก้บรรทดันี้ ให้สอดคล้อง JSON ที่ได้มา ขึ้น Error อันนี้ $exception {"Object reference not set to an instance of an object. System.NullReferenceException"[/i][/i][/i]
[i][i][i]                    {[/i][/i][/i]
[i][i][i]                        foreach (JProperty n5 in o[n1]["Task"][n3.Name][n4.Name])[/i][/i][/i]
[i][i][i]                        {[/i][/i][/i]
[i][i][i]                            if (n5.Value.Type.ToString() != "String")[/i][/i][/i]
[i][i][i]                            {[/i][/i][/i]
[i][i][i]                                foreach (JProperty n6 in o[n1]["Task"][n3.Name][n4.Name][n5.Name])[/i][/i][/i]
[i][i][i]                                {[/i][/i][/i]
[i][i][i]                                    if (n6.Value.Type.ToString() != "String")[/i][/i][/i]
[i][i][i]                                    {[/i][/i][/i]
[i][i][i]                                        foreach (JProperty n7 in o[n1]["Task"][n3.Name][n4.Name][n5.Name][n6.Name])[/i][/i][/i]
[i][i][i]                                        {[/i][/i][/i]
[i][i][i]                                            AddVal(n7.Name, (string)n7.Value, n7.Path);[/i][/i][/i]
[i][i][i]                                        }[/i][/i][/i]
[i][i][i]                                    }[/i][/i][/i]
[i][i][i]                                    else[/i][/i][/i]
[i][i][i]                                    {[/i][/i][/i]
[i][i][i]                                        AddVal(n6.Name, (string)n6.Value, n6.Path);[/i][/i][/i]
[i][i][i]                                    }[/i][/i][/i]
[i][i][i]                                }[/i][/i][/i]
[i][i][i]                            }[/i][/i][/i]
[i][i][i]                            else[/i][/i][/i]
[i][i][i]                            {[/i][/i][/i]
[i][i][i]                                AddVal(n5.Name, (string)n5.Value, n5.Path);[/i][/i][/i]
[i][i][i]                            }[/i][/i][/i]
[i][i][i]                        }[/i][/i][/i]
[i][i][i]                    }[/i][/i][/i]
[i][i][i]                }[/i][/i][/i]

[i][i][i]            }[/i][/i][/i]
[i][i][i]        }[/i][/i][/i]
[i][i][i]    }[/i][/i][/i]

[i][i][i]}[/i][/i][/i]


รบกวนสอบถาม
1.การอ้างอิง value ใน JSON (เมื่อได้ o เป็น JObject มาแล้ว)
JSONเลเวล1 คือ "Activities" //เพราะ FirebaseResponse response = client.Get("") เป็นรูทสุดท้าย ดังนั้น n1 มีค่าเท่ากับ l1 มีอย่างละตัวทั้งคู่
เหตุใดตัวอย่างถึงไม่ต้องอ้างอิง n1 ใส่เข้าไปด้วยเป็น o[n1][n3]["Task"]
2.จากรูป ผมคิดไปเองว่า ตัว Path จะสามารถใช้ contain(searchvalue) ได้ เนื่องจากรันไม่ถึงอันสุดท้ายจึงรบกวนสอบถามเพื่อให้แน่ใจ
[i][i][i]3. exception throw [/i][/i][/i]
[/i][/i][/i]
[i][i][i]{"Accessed JObject values with invalid key value: \"งานด้านปฏิบัติการ\": {\r\n \"Task\": {\r\n \"2_1งานลงทุนด้านอุปกรณ์\": {\r\n \"2_1_1การเลือกและขอซื้ออุปกรณ์ร้านมาตรฐาน\": {\r\n \"ร้านเปิดใหม่\": {\r\n \"อนุมัติ\": {\r\n \"POST\": \"GM\"\r\n }\r\n }\r\n },\r\n \"2_1_2การเลือกและขอซื้ออุปกรณ์ร้านนอกมาตรฐาน\": {\r\n \"กลั่นกรอง\": {\r\n \"POST\": \"GM\",\r\n \"สายงาน\": \"สานงานการตลาด\"\r\n },\r\n \"อนุมัติ\": {\r\n \"POST\": \"MD/CEO\"\r\n }\r\n }\r\n }\r\n }\r\n}. Object property name expected."}[/i][/i][/i]
[i][i][i]System.ArgumentException"[/i][/i][/i]
เมื่อก่อนไม่ขึ้น ตอนนี้ งง ขึ้นมาเฉยเลยแก้ไขอย่างไร หรือ ปล่อยผ่านได้ไหมครับ?
แก้ไขข้อความเมื่อ
คำตอบที่ได้รับเลือกจากเจ้าของกระทู้
ความคิดเห็นที่ 1
ตอนนี้มี JSON String 2 อัน คือมี Activities และไม่มี (https://ppantip.com/topic/39490843/comment10)
1. ตัวอย่างเดิม client.Get("")
2. ของใหม่ client.Get("Activities")

สำหรับ JSON ที่มี Activities ถ้าใช้คำสั่งที่ 2 จะทำงานได้ตามปกติ โดยไม่ต้องแก้เมธอด AddValtoList()
แต่เพราะต้องการทำความเข้าใจจึงเริ่มที่รูทเลยใช่ไหมครับ?

error ส่วนใหญ่มาจากการอ้างอิง object ที่ไม่มีใน JSON
การเข้าถึงโหนดมีสองรูปแบบภายใต้เงื่อนไขเดียวคือชื่อ
1. เข้าแบบ object ใช้ [] กับทั้ง array และ list
    1.1 ค่าคงที่ ในรูป object key เช่น JObject["level1"]["level2"]["level3"]["levelN"]
    1.2 ค่าตัวแปร ในรูป JProperty.Name เช่น JObject[node1.Name][node2.Name][node3.Name][nodeN.Name]
2. เข้าแบบ path กับ array ใช้ [] ส่วน list ใช้ .
    เช่น .SelectToken("level1.level2.levelN")
           .SelectToken("level1." + node2.Name + ".levelN")
           .SelectToken("level1[level2].levelN")    <- ยังไม่พบโครงสร้างแบบนี้ใน Firebase
           .SelectToken("$.level1[*].level2.levelN")    <- ใน Firebase ไม่ต้องอ้างด้วย array

จากการทดสอบ JSON ที่มี Activities ด้วย client.Get("") จะเห็นว่า level depht มีการเลื่อน

level1 - ผ่าน
List<string> l1 = o.Properties().Select(p => p.Name).ToList();
            foreach (string n1 in l1)

(ตรงนี้ l1 เป็น List<string> มีสมาชิก 1 ตัว เป็นชนิด string และเราตั้งชื่อให้มันเป็น n1 มีค่า "Activities"
สังเกต l1 != n1 แต่ n1 เป็น subset ของ l1 หรือเขียนเป็น l1 = {n1} และ n1 = "Activities" และ l1 = {"Activities"}
เพื่อให้ดูง่าย l1 จึงตั้งมาจาก level1 เป็น list, n1 มาจาก node1 เป็น object

การเข้าถึง Value ให้นึกถึง KeyValuePair<string, JToken> เสมอ
ฝั่งขวาคือ JToken เป็น object เมื่อไหร่กลายเป็น string นั่นคือ Value ที่เราต้องการ)

ระดับต่อไป เนื่องจากตัวอย่างเดิม(JSON ที่ไม่มี Activities) ต้องการข้าม level 2 ชื่อ Task
เพราะใน 3 object(งานด้านปฏิบัติการ, งานด้านโยบาย, งานบริหารทั่วไป) มี Task เหมือนกันหมด ไม่จำเป็นต้องลูป

แทนที่จะเขียนเป็น
foreach (JProperty n2 in o[n1])  // n1 มาจาก JProperty.Name ซึ่งเป็น string อยู่แล้ว จึงไม่ต้องอ้างเป็น n1.Name
{
    // n2 เป็น object ที่ถูกครอบด้วย JProperty
    // n2.Name = "Task"
    foreach (JProperty n3 in o[n1][n2.Name]) {
        //...
    }
}

ก็หดให้เหลือแค่
foreach (JProperty n3 in o[n1]["Task"]) {//..}


แต่ level2 สำหรับของใหม่(JSON ที่มี Activities) ไม่มี key node ชื่อ "Task"
แต่เป็น 3 อันนี้ที่เป็น level 2 (งานด้านปฏิบัติการ, งานด้านโยบาย, งานบริหารทั่วไป)
สิ่งที่ตามมาคือ คอมไพเลอร์ไม่อนุมัติงาน
System.NullReferenceException: 'Object reference not set to an instance of an object.'

ตอนนี้เรารู้ปัญหาแล้วก็ให้มันวนหาทั้งสามโหนดนั้นก่อนแล้วค่อยข้าม Task ใน level ถัดไป

foreach (JProperty n2 in o[n1])
{
                    foreach (JProperty n4 in o[n1][n2.Name]["Task"])
                    {
        //..
    }
}

และก่อนจะ build หรือ run ได้ ต้องแก้ level ที่เหลือให้หมดก่อน ซึ่งจะต้องเพิ่ม level สุดท้ายเป็น n8 ด้วย
เป็นผลมาจากการเพิ่มโหนดชื่อ "Activities" เข้ามา ที่เหลือก็ลองศึกษาตาม
ครูที่เก่งและดีที่สุดตอน debug ซึ่งก็คือหน้า Locals ครับ จ้องไว้เลย
แสดงความคิดเห็น
โปรดศึกษาและยอมรับนโยบายข้อมูลส่วนบุคคลก่อนเริ่มใช้งาน อ่านเพิ่มเติมได้ที่นี่