這幾天碰到一個需要從Server端去發出HTTP拿JSON的需求,與之前全Client端的經驗不一致,開發的過程倒也是零障礙啦,但是為了加速日後開發(免找資料)的速度,還是記錄在這邊會比較妥當。
一開始的問題一定是Server端的HTTP Request物件,承襲.NET 1.1的寫法,在這邊還是習慣使用HttpWebRequest,但經過研究後發現.NET提供了三種物件,這些物件的結構經過研究後如下所示。
System.Net.WebRequest(abstract) System.Net.HttpWebRequest(implement) System.Net.WebClient(wrapper)
在考量效能至上的情況下,當然是只能選擇把程式碼實作量最少的WebClient丟掉啦!且拋棄WebClient類別還有一個很重要的點,就是他並沒有把Timeout屬性包進去,當然啦,我們可以實作一個Extension來再包皮,但是這樣作原本的程式碼量少優勢就不見了,這樣一來不還是選擇HttpWebRequest。程式碼的概念如下,如果Timeout的話,會跳到catch裡。
System.Net.HttpWebRequest oWRq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create("JsonURL"); oWRq.Timeout = 1000; //設定網路對接逾時秒數為一秒 try { System.Net.HttpWebResponse oWRp = (System.Net.HttpWebResponse)oWRq.GetResponse(); using (System.IO.StreamReader oSR = new System.IO.StreamReader(oWRp.GetResponseStream(), System.Text.Encoding.UTF8)) { string cJSON = oSR.ReadToEnd(); var oTemp = Newtonsoft.Json.JsonConvert.DeserializeObject<ORM_Class>(cJSON); //wrote something... } } catch { return false; //如果是逾時或其它原因,一律傳回false }
基本上有些情況你只能毫無怨言的收下對方餵給你的JSON,如果他是一個懂狀況的程式設計師,他可能會餵給你這樣的結構。
{ { "productID": 1, "name": "ABC" }, { "productID": 2, "name": "DEF" } }
這樣的結構很好解,基本上你只要準備一套ORM Class跟善用List<T>就可以解決,像是這樣...
//ORM public class Prodcut { public int productID { get; set; } public string name { get; set; } } //然後 List<Prodcut> oProdcuts = (List<Prodcut>)JsonConvert.DeserializeObject(cJSON); //接下來你就可以去count它或操作他了 oProdcuts.Count; oProdcuts[0]; foreach(var oItem in oProdcuts) { oItem.productID; ... }
有時也會有一些毫無sense的人直接把XML轉JSON丟給你了事(留下一個大腸頭、闌尾),但是你又沒有要求的權利,在這裡不考慮多筆的狀況,JSON格式我改成如下:
{ "product": { "productID": 1, "name": "ABC" } }
就算是迫於無奈也得接受,這就是程式設計師的宿命,因此你為了這種骯髒的JSON還要再寫一層Wrapper ORM。程式碼如下:
//ORM public class ProdcutRoot { public Prodcut product { get; set; } } public class Prodcut { public int productID { get; set; } public string name { get; set; } } //然後 var oProdcuts = JsonConvert.DeserializeObject<ProdcutRoot>(cJSON); //接下來操作是 oProdcuts.products.productID; oProdcuts.products.name;
C# 4.0以後有支援dynamic宣告,也就是說,你可以搭配Newtonsoft.Json.JsonConvert.DeserializeObject來進行操作,因此可以省略掉Wrapper ORM的撰寫,以上面為例,可以改寫成下列的程式碼,更顯得超級清爽啊!
//ORM public class Prodcut { public int productID { get; set; } public string name { get; set; } } //然後 dynamic oJsonTemp = Newtonsoft.Json.JsonConvert.DeserializeObject(cJSON); //oJsonTemp.product即為動態運算式,此作業將於執行階段解決 var oProdcut = Newtonsoft.Json.JsonConvert.DeserializeObject<Prodcut>(oJsonTemp.product.ToString()); //接下來操作是 oProdcut.productID; oProdcut.name;
以上述的那個毫無sense的JSON,直翻回XML應同等如下(型別我就省略了,那不是討論的重點):
<product> <productID>1</productID> <name>ABC</name> </product>
翻一下JSON.NET應該可以找到「JsonConvert.SerializeXmlNode Method (XmlNode, Formatting, Boolean)」這個建構子,第三個引數Boolean的宣告如下:omitRootObject/Type: System.Boolean/Omits writing the root object。我想答案已經很明顯了,以下示範如何把這個XML灌入JSON.NET去Parse。
XmlDocument oXML = new XmlDocument(); oXML.LoadXml(@"XML字串;不再綴述"); string cJson = JsonConvert.SerializeXmlNode(oXML, Formatting.None, true);
※ 相關連結
ServerSide WebRequest HttpWebRequest WebClient JSON XML RootElement RootNode