Server Side JSON/XML的相關寫法
這幾天碰到一個需要從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的格式種類:a
基本上有些情況你只能毫無怨言的收下對方餵給你的JSON,如果他是一個懂狀況的程式設計師,他可能會餵給你這樣的結構。
{
{
"productID": 1,
"name": "ABC"
},
{
"productID": 2,
"name": "DEF"
}
}
這樣的結構很好解,基本上你只要準備一套ORM Class跟善用List
//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; ... }
JSON的格式種類:b
有時也會有一些毫無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;
Update 20150708:
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;
同場加映:如何避免輸出無謂的XML Root Node給JSON
以上述的那個毫無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);
※ 相關連結
- 利用匿名型別來化簡JSON ORM讀取時之冗長程式碼
- 延伸參考:YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE;HttpClient因IDisposable反而引發連線資源耗盡(TIME_WAIT)問題
- HttpClient所引爆的Sockets Port耗盡問題