คำตอบที่ได้รับเลือกจากเจ้าของกระทู้
ความคิดเห็นที่ 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 ครับ จ้องไว้เลย
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 ครับ จ้องไว้เลย
แสดงความคิดเห็น
C# JSON จาก response
จาก 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>();
List<string> DValue = new List<string>();
List<string> DPath = new List<string>();
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]
[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]