需求場景:
當用戶在頁面上點擊"編輯"按鈕時, 將生成一個自定義對象集合(List<JenkinsTask>), 之后遍歷該集合中的所有任務, 依次啟動Jenkins的構建任務.
同時, aspx頁面上放有一個Ajax的Timer空間, Timer每5秒觸發一次. 每次觸發Timer的時候, 遍歷List<JenkinsTask>集合, 將任務執行結果顯示在頁面的文本框中.
該功能已經由Javascrit實現了, 但是本機調試正常, 客戶機有的正常、有的異常, 而且出錯的情況還各不相同. 於是, 在服務器端, 重新實現一下該功能.
問題:
HTTP協議的無狀態特性, 將無法保存List<JenkinsTask>集合, 所以想到序列化該集合並將之保存在ViewState中, C#提供的序列化器有BinaryFormatter、SoapFormatter, 前者以二級制形式序列化對象, 后者以Soap協議格式序列化對象; 還有XML也提供了序列化器. 雖然本次是在服務器段實現該功能, 但考慮到將來可能還會再改到javascript的實現形式, 希望采用Json格式的序列化器.
Json格式序列化器有兩種實現System.Runtime.Serialization.Json名空間下的DataContractJsonSerializer類, 和System.Web.Script.Serialization命名空間下的JavaScriptSerializer類, 前者的過程類似於BinaryFormatter的序列化過程, 需要設計IO流等等, 而JavaScriptSerializer類相對就簡單的多, 也不需要添加[Serializable]等Attribute特性字段.
實現: (注意: 簡單自定義類 和 復雜自定義類 的操作不同), 解決方案摘取的代碼, 不完整, 還忘見諒.
1. 引用System.Web.Script.Serialization命名空間;
//頁面中的Ajax控件Timer

<asp:Timer ID="timerJenkins" runat="server" ontick="timerJenkins_Tick" Enabled="false" Interval="5000">
</asp:Timer>
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="timerJenkins" EventName="Tick" />
</Triggers>
<ContentTemplate>
<table class="infoTable">
<tr runat="server" id="trAppResult">
<td class="infoTital">
</td>
<td class="infoContent">
<div runat="server" id="divBuildResult" style="width: 700px;">
</div>
<div runat="server" id="divBuildResultStatus" style="width: 700px;">
</div>
</td>
</tr>
</table>
<div id="divButton">
<asp:Button ID="btn" runat="server" Text="" OnClientClick="return false;" Width="0"
Height="0" />
<asp:Button ID="btnStructure" runat="server" Text="編譯" CssClass="btn_mouseout" onmouseover="this.className='btn_mouseover'"
onmouseout="this.className='btn_mouseout'" onmousedown="this.className='btn_mousedown'"
onmouseup="this.className='btn_mouseup'" Style="margin-left: 30px; margin-right: 15px;"
OnClick="btnStructure_Click" />
<asp:Button ID="btnSubmit" runat="server" Text="提交" OnCommand="btnSubmit_Click" CssClass="btn_mouseout"
onmouseover="this.className='btn_mouseover'" onmouseout="this.className='btn_mouseout'"
onmousedown="this.className='btn_mousedown'" onmouseup="this.className='btn_mouseup'"
Style="margin-right: 15px;" />
<asp:Button ID="btnReturn" runat="server" Text="返回" OnClientClick="location.href='DevelopmentApplyList.aspx';return false;"
CssClass="btn_mouseout" onmouseover="this.className='btn_mouseover'" onmouseout="this.className='btn_mouseout'"
onmousedown="this.className='btn_mousedown'" onmouseup="this.className='btn_mouseup'" />
</div>
</ContentTemplate>
</asp:UpdatePanel>
2. 對於簡單的自定義類(類成員多為簡單數據類型), 再點擊按鈕的事件中創建List集合對象, 使用new JavaScriptSerializer().Serialize()即可完成序列化, 在Ajax的Timer控件后台方法中, 使用new JavaScriptSerializer().Deserialize()即可完成反序列化.
實例如下:
//JenkinsTast.cs

namespace WebSite.Common
{
public class JenkinsTask
{
private static readonly string JenkinsBaseUrl = ConfigurationManager.AppSettings["JenkinsPath"];
private static readonly string JenkinsUrlFormat = JenkinsBaseUrl+"/job/{0}";
private static readonly string JenkinsStartUrlFormat = JenkinsUrlFormat + "/build?delay=0sec";
private static readonly string JenkinsResponseUrlFormat = JenkinsUrlFormat + "/buildHistory/ajax";
private static readonly string JenkinsNoticeInfoStart = "構建任務< {0} >已啟動...";
private static readonly string JenkinsNoticeInfoRun = "構建任務< {0} >正在執行...";
public JenkinsTask()
{ }
public JenkinsTask(string structureName)
{
this.StructureTaskName = structureName;
this.StructureTaskStatus = Enum.GetName(typeof(JenkinsTaskStatus), JenkinsTaskStatus.Init);
}
public string StructureTaskName { get; set; }
public string StructureTaskStatus { get; set; }
public int LastStructureId { get; set; }
public string TarPackageName { get; set; }
public string OutputHTML { get; set; }
public string OutputText { get; set; }
//啟動構建任務
public void StartJenkinsTask()
{
//獲取構建任務ID
string strId = getLastStructureId(XSSAccessHelper.GetJenkinsResponse(new Uri(string.Format(JenkinsUrlFormat, this.StructureTaskName)), "GET", null));
int id = 0;
if (!string.IsNullOrEmpty(strId))
{
id = Convert.ToInt32(strId);
}
//啟動構建任務
XSSAccessHelper.GetJenkinsResponse(new Uri(string.Format(JenkinsStartUrlFormat, this.StructureTaskName)), "GET", null);
//獲取構建任務信息, 將結果填入OutputHTML(Web異步無法啟動, 這里改為只填寫最近一次構建任務的ID)
//GetStructureResponseInfo(id+1);
this.LastStructureId = id + 1;
this.OutputText = string.Format(JenkinsNoticeInfoStart,this.StructureTaskName);
}
private string getLastStructureId(string responseHTML)
{
string result = "";
string regId = "<\\s*a\\s*href=\"lastBuild/\">Last\\s+build\\s*\\(#\\d+\\)\\s*,[\\W\\w]+?<\\s*\\/\\s*a\\s*>";
Regex reg = new Regex(regId,RegexOptions.IgnoreCase);
if(reg.IsMatch(responseHTML))
{
try
{
result = reg.Match(responseHTML).Value;
result = result.Substring(result.IndexOf("#")+1, result.IndexOf(")") - result.IndexOf("#")-1);
}
catch
{
throw;
}
}
return result;
}
//獲取構建任務信息, 將結果填入OutputHTML
public void GetStructureResponseInfo()
{
this.OutputHTML = XSSAccessHelper.GetJenkinsResponse(new Uri(string.Format(JenkinsResponseUrlFormat, this.StructureTaskName)), "n:" + this.LastStructureId.ToString(), "GET", null);
this.OutputText = string.Format(JenkinsNoticeInfoRun, this.StructureTaskName);
}
}
public enum JenkinsTaskStatus
{
Init = 0,
CompileRunning = 1,
CompileFinish = 2,
PackageRunning = 3,
PackageFinish = 4
}
}
//Test.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Script.Serialization;
namespace WebSite
{
public partial class TestUCPage_TEST_ : Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
HttpRequest req = HttpContext.Current.Request;
}
}
protected void btnStructure_Click(object sender, EventArgs e)
{
this.btnSubmit.Enabled = false;
if (this.chkEnvironment.SelectedItem == null)
{
this.chkEnvironment.BackColor = System.Drawing.Color.FromName("#FEB8B8");
return;
}
else
{
this.chkEnvironment.BackColor = System.Drawing.Color.FromName("#FFFFFF");
}
//生成構建任務列表
if (!string.IsNullOrEmpty(this.hiddenFldStructureList.Value))
{
JenkinsTask jt = new JenkinsTask(this.hiddenFldStructureList.Value);
saveJenkinsTaskList(jt);
this.timerJenkins.Enabled = true;
}
}
protected void timerJenkins_Tick(object sender, EventArgs e)
{
JenkinsTask jt = getJenkinsTaskList();
if (jt != null)
{
this.divResult.innerHTML = jt.OutHtml;
}
saveJenkinsTaskList(jt);
}
private JenkinsTask getJenkinsTaskList()
{
return new JavaScriptSerializer().Deserialize<JenkinsTask>(ViewState["JenkinsTaskList"].ToString());
}
private void saveJenkinsTaskList(JenkinsTask jt)
{
ViewState["JenkinsTaskList"] = new JavaScriptSerializer().Serialize(jt);
}
}
}
這樣就已經可以了, 使用默認的JavaScriptConverter實現時, 必須要一個無參的構造函數.
3. 對於復雜的自定義類型(含有自定義類成員, 或沒有無參構造函數, 或私有寫屬性.....等等). 對於復雜類型, 默認的JavaScriptConverter會造成序列化或反序列化無法正常工作, 此時需要自己實現JavaScriptConverter(抽象類), 來完成序列化和反序列過程.
//Jenkins.cs, 類中屬性的Set方法為private, 且沒有默認構造函數

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Runtime.Serialization;
using System.Web.Script.Serialization;
//using System.Security.Permissions;
namespace WebSite.Common
{
public class JenkinsTask
{
private static readonly string JenkinsBaseUrl = ConfigurationManager.AppSettings["JenkinsPath"];
private static readonly string JenkinsUrlFormat = JenkinsBaseUrl+"/job/{0}";
private static readonly string JenkinsStartUrlFormat = JenkinsUrlFormat + "/build?delay=0sec";
private static readonly string JenkinsResponseUrlFormat = JenkinsUrlFormat + "/buildHistory/ajax";
private static readonly string JenkinsNoticeInfoStart = "構建任務< {0} >已啟動...";
private static readonly string JenkinsNoticeInfoRun = "構建任務< {0} >正在執行...";
public JenkinsTask(string structureName)
{
this.StructureTaskName = structureName;
this.StructureTaskStatus = Enum.GetName(typeof(JenkinsTaskStatus), JenkinsTaskStatus.Init);
}
public JenkinsTask(string structureName, JenkinsTaskStatus structureStatus, int structureId, string outHtml, string outText)
{
this.StructureTaskName = structureName;
this.StructureTaskStatus = Enum.GetName(typeof(JenkinsTaskStatus),structureStatus);
this.LastStructureId = structureId;
this.OutputHTML = outHtml;
this.OutputText = outText;
}
public string StructureTaskName { get; private set; }
public string StructureTaskStatus { get; private set; }
public int LastStructureId { get; private set; }
public string TarPackageName { get; private set; }
public string OutputHTML { get; private set; }
public string OutputText { get; private set; }
//啟動構建任務
public void StartJenkinsTask()
{
//獲取構建任務ID
string strId = getLastStructureId(XSSAccessHelper.GetJenkinsResponse(new Uri(string.Format(JenkinsUrlFormat, this.StructureTaskName)), "GET", null));
int id = 0;
if (!string.IsNullOrEmpty(strId))
{
id = Convert.ToInt32(strId);
}
//啟動構建任務
XSSAccessHelper.GetJenkinsResponse(new Uri(string.Format(JenkinsStartUrlFormat, this.StructureTaskName)), "GET", null);
//獲取構建任務信息, 將結果填入OutputHTML(Web異步無法啟動, 這里改為只填寫最近一次構建任務的ID)
//GetStructureResponseInfo(id+1);
this.LastStructureId = id + 1;
this.OutputText = string.Format(JenkinsNoticeInfoStart,this.StructureTaskName);
}
private string getLastStructureId(string responseHTML)
{
string result = "";
string regId = "<\\s*a\\s*href=\"lastBuild/\">Last\\s+build\\s*\\(#\\d+\\)\\s*,[\\W\\w]+?<\\s*\\/\\s*a\\s*>";
Regex reg = new Regex(regId,RegexOptions.IgnoreCase);
if(reg.IsMatch(responseHTML))
{
try
{
result = reg.Match(responseHTML).Value;
result = result.Substring(result.IndexOf("#")+1, result.IndexOf(")") - result.IndexOf("#")-1);
}
catch
{
throw;
}
}
return result;
}
//獲取構建任務信息, 將結果填入OutputHTML
public void GetStructureResponseInfo()
{
this.OutputHTML = XSSAccessHelper.GetJenkinsResponse(new Uri(string.Format(JenkinsResponseUrlFormat, this.StructureTaskName)), "n:" + this.LastStructureId.ToString(), "GET", null);
this.OutputText = string.Format(JenkinsNoticeInfoRun, this.StructureTaskName);
}
}
public enum JenkinsTaskStatus
{
Init = 0,
CompileRunning = 1,
CompileFinish = 2,
PackageRunning = 3,
PackageFinish = 4
}
/// <summary>
/// 單個Jenkins任務的序列化和反序列化
/// </summary>
public class JenkinsTaskConverter : JavaScriptConverter
{
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
IDictionary<string, object> dict = new Dictionary<string, object>();
if (obj != null)
{
JenkinsTask jt = obj as JenkinsTask;
dict["StructureTaskName"] = jt.StructureTaskName;
dict["StructureTaskStatus"] = jt.StructureTaskStatus;
dict["LastStructureId"] = jt.LastStructureId;
dict["OutputHTML"] = jt.OutputHTML;
dict["OutputText"] = jt.OutputText;
}
return dict;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
return new JenkinsTask(Convert.ToString(dictionary["StructureTaskName"]), (JenkinsTaskStatus)Enum.Parse(typeof(JenkinsTaskStatus), Convert.ToString(dictionary["StructureTaskStatus"])), Convert.ToInt32(dictionary["LastStructureId"]), Convert.ToString(dictionary["OutputHTML"]), Convert.ToString(dictionary["OutputText"]));
}
public override IEnumerable<Type> SupportedTypes
{
get
{
yield return typeof(JenkinsTask);
}
}
}
/// <summary>
/// Jenkins任務集合的序列化和反序列化, 測試代碼, 此處只提供個針對 序列化復雜類集合 的思路...
/// </summary>
//public class JenkinsTaskListConverter : JavaScriptConverter
//{
// public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
// {
// IDictionary<string, object> dict = new Dictionary<string, object>();
// if (obj != null)
// {
// List<JenkinsTask> jtLst = obj as List<JenkinsTask>;
// if (jtLst != null && jtLst.Count > 0)
// {
// dict["JenkinsTaskCollection"] = jtLst;
// }
// //dict["ItemCount"] = jtLst.Count;
// }
// return dict;
// }
// public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
// {
// return dictionary["JenkinsTaskCollection"] as List<JenkinsTask>;
// }
// public override IEnumerable<Type> SupportedTypes
// {
// get { yield return typeof(List<JenkinsTask>); }
// }
// }
}
//Test.aspx.cs, 注意在Page_Load時候注冊了自定義的JavaScriptConverter轉換器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Script.Serialization;
namespace WebSite
{
public partial class TestUCPage_TEST_ : Page
{
protected void Page_Load(object sender, EventArgs e)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
//注冊JenkinsTask轉換器
jss.RegisterConverters(new JenkinsTaskConverter[] { new JenkinsTaskConverter() });
if (!this.IsPostBack)
{
HttpRequest req = HttpContext.Current.Request;
}
}
protected void btnStructure_Click(object sender, EventArgs e)
{
this.btnSubmit.Enabled = false;
if (this.chkEnvironment.SelectedItem == null)
{
this.chkEnvironment.BackColor = System.Drawing.Color.FromName("#FEB8B8");
return;
}
else
{
this.chkEnvironment.BackColor = System.Drawing.Color.FromName("#FFFFFF");
}
//生成構建任務列表
if (!string.IsNullOrEmpty(this.hiddenFldStructureList.Value))
{
JenkinsTask jt = new JenkinsTask(this.hiddenFldStructureList.Value);
saveJenkinsTaskList(jt);
this.timerJenkins.Enabled = true;
}
}
protected void timerJenkins_Tick(object sender, EventArgs e)
{
this.timerJenkins.Enabled = false;
JenkinsTask jt = getJenkinsTaskList();
if (jt != null)
{
this.divResult.innerHTML = jt.OutHtml;
}
saveJenkinsTaskList(jt);
this.timerJenkins.Enabled = true;
}
private JenkinsTask getJenkinsTaskList()
{
return jss.Deserialize<JenkinsTask>(ViewState["JenkinsTaskList"].ToString());
}
private void saveJenkinsTaskList(JenkinsTask jt)
{
ViewState["JenkinsTaskList"] = jss.Serialize(jt);
}
}
}
總結:
額外的, 在Javascript中, 是可以讀取隱藏域中的 序列化后的內容的, 這樣的就能在JS中用eval("("+strSerialize+")")獲得JSon對象.
對於今天上午遇到的場景, 其實用 列1中, 所描述的"簡單類型"的情形, 就能解決問題. 之所以, 引出后邊的東東, 是因為開始時候JenkinsTask任務中, 沒有寫默認構造函數, 其中的幾個屬性也是Set訪問器也是private的, 所以一直無法反序列化成功. 雖然犯了回低級錯誤, 但是對這幾個序列化器的內容梳理的一邊, 標記下也算沒白費功夫.
//服務端跨站訪問Jenkins的代碼, 服務端實現的訪問Jenkis的代碼, 與本貼主題無關, 標記一下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text.RegularExpressions;
using System.Configuration;
using System.Net;
using System.IO;
using System.Text;
namespace WebSite.Common
{
public class XSSAccessHelper
{
private static readonly string jenkinsUrl = ConfigurationManager.AppSettings["JenkinsPath"];
private static readonly string jenkinsName = ConfigurationManager.AppSettings["JenkinsName"];
private static readonly string jenkinsPwd = ConfigurationManager.AppSettings["JenkinsPwd"];
public static string GetJenkinsResponse(Uri url, string requestType, string postRequestBody)
{
return GetJenkinsResponse(url, null, requestType, postRequestBody);
}
/// <summary>
/// 從Jenkins上獲取返回值信息
/// </summary>
/// <param name="url">請求的Jenkins地址</param>
/// <param name="requestHeader">自定義的請求頭, 每個請求頭為以":"分隔的名值對, 多個名值對之間以","分隔</param>
/// <param name="requestType">請求頭類型("GET" or "POST")</param>
/// <param name="postRequestBody">POST類型的請求包體, 可為NULL或空字符串</param>
/// <returns>從Jenkins上返回的HTML信息(已去除回車換行)</returns>
public static string GetJenkinsResponse(Uri url,string requestHeader,string requestType, string postRequestBody)
{
string responseHTML = "";
#region SetRequest
HttpWebRequest hwRequest = getJenkinsRequest(url);
hwRequest.Method = requestType;
if (!string.IsNullOrEmpty(requestHeader))
{
foreach (string str in requestHeader.Split(','))
{
if (!string.IsNullOrEmpty(str))
hwRequest.Headers.Add(str);
}
}
if(requestType == "POST")
{
//異步請求必須添加ContentType請求頭
hwRequest.ContentType = "application/x-www-form-urlencoded";
//請求包體
if (!string.IsNullOrEmpty(postRequestBody))
{
byte[] postBodyBytes = Encoding.Default.GetBytes(postRequestBody);//傳遞的值
hwRequest.ContentLength = postBodyBytes.Length;
//把傳遞的值寫到流中
using (Stream requestStream = hwRequest.GetRequestStream())
{
try
{
requestStream.Write(postBodyBytes, 0, postBodyBytes.Length);
}
catch (Exception ex)
{
throw ex;
}
}
}
}
#endregion
#region GetResponse
using (HttpWebResponse hwrResponse = hwRequest.GetResponse() as HttpWebResponse)
using (Stream responseStream = hwrResponse.GetResponseStream())
using (StreamReader sReader = new StreamReader(responseStream, Encoding.UTF8))
{
try
{
responseHTML = formatHtml(sReader.ReadToEnd());
}
catch (Exception ex)
{
throw ex;
}
}
return responseHTML;
#endregion
}
private static HttpWebRequest getJenkinsRequest(Uri requestUrl)
{
CredentialCache cCache = new CredentialCache();
cCache.Add(new Uri(jenkinsUrl), "Basic", new NetworkCredential(jenkinsName, jenkinsPwd));
return (HttpWebRequest)WebRequestHelper.GetRequest(requestUrl, cCache);
}
private static string formatHtml(string responseHTML)
{
return responseHTML.Replace("\r\n", "").Replace("\n", "");
}
}
public class WebRequestHelper
{
public static WebRequest GetRequest(Uri requestUrl, CredentialCache credential)
{
HttpWebRequest request = null;
if (requestUrl != null)
{
try
{
request = HttpWebRequest.Create(requestUrl) as HttpWebRequest;
if (credential != null)
{
request.Credentials = credential;
NetworkCredential nCredential = CredentialCache.DefaultNetworkCredentials;
request.Headers["Authorization"] = "Basic" + Convert.ToBase64String(Encoding.Default.GetBytes(nCredential.UserName + ":" + nCredential.Password));
}
}
catch
{
throw;
}
}
return request;
}
}
}
補充:

JavaScriptSerializer序列化和反序列化JSON:使用自定義JavaScriptConverter JSON的序列化和反序列化已經成為Web開發必不可少的知識。現在常用的有System.Web.Script.Serialization下的JavaScriptSerializer來進行處理;另外一個比較常用且高效的類庫是JSON.NET。 在開發一些小的應用時,由於想盡量較少項目的依賴,所以不太願意使用JSON.NET。JavaScriptSerializer基本上能滿足簡單的需求,但當一個屬性要序列化成別的名字時,就顯得力不從心了。還有就是可能業務需要,某些屬性不希望在某一個條件下進行序列化。鑒於上面的業務需要,我們就需要自定義一個JavaScriptConverter。 自定義JavaScriptConverter的使用流程: 定義一個JavaScriptConverter,指明期支持的類型。 在序列化方法調用前注冊該轉換器。 顯然,這兩個步驟都不是很麻煩。我們舉一個具體的業務環境來說明:在ExtJS的TreePanel控件中,TreeNode的屬性有id、text等,其中如果checked屬性存在,則屬性結果顯示節點選擇框。在C#中,checked是關鍵字,所以不能定義為屬性,TreeNode的C#代碼如下: 復制代碼 public class TreeNode { public string id { get; set; } public string text { get; set; } public bool? isChecked { get; set; } public List<TreeNode> children { get; set; } } 復制代碼 我將checked屬性映射到類中的isChecked字段,該字段可以有三個狀態:null、true和false,當為null的時候,將不會序列化該字段,前台也不會顯示節點選擇框。 現在要實現一個TreeNodeJSConverter,代碼如下: 復制代碼 public class TreeNodeJSConverter : JavaScriptConverter { public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { TreeNode node = new TreeNode(); object value = null; if (dictionary.TryGetValue("id", out value)) node.id = (string)value; if (dictionary.TryGetValue("text", out value)) node.text = (string)value; if (dictionary.TryGetValue("children", out value)) { if (value != null && value.GetType() == typeof(ArrayList)) { var list = (ArrayList)value; node.children = new List<TreeNode>(); foreach (Dictionary<string, object> item in list) { node.children.Add((TreeNode)this.Deserialize(item, type, serializer)); } } else { node.children = null; } } return node; } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { Dictionary<string, object> dic = new Dictionary<string, object>(); var node = obj as TreeNode; if (node == null) return null; if(!string.IsNullOrEmpty(node.id)) dic.Add("id", node.id); if (!string.IsNullOrEmpty(node.text)) dic.Add("text", node.text); if (node.isChecked.HasValue) dic.Add("checked", node.isChecked.Value); if (node.children != null) dic.Add("children", node.children); return dic; } public override IEnumerable<Type> SupportedTypes { get { return new Type[] { typeof(TreeNode) }; } } } 復制代碼 代碼討論:TreeNodeJSConverter類首先需要繼承JavaScriptConverter,然后實現它的相關方法:Deserialize、Serialize和SupportedTypes,分別是反序列化、序列化和支持的類型。 在序列化方法Serialize中,我們需要將屬性添加到一個字典結構中,就可以完成序列化的工作了。具體要序列化那些字段還是要判斷一下相應的值是否存在。 在反序列化方法Deserialize中,字典結構存放着相應的值,當值存在的時候就可以為TreeNode相應的字段賦值。比較麻煩的事children屬性,因為其為嵌套的List類型,而JSON中的數組結構會被轉化成ArrayList結構,所以我們只需要遞歸的循環ArrayList中的每一個項,將其轉換為TreeNode就可以了。 最后是SupportedTypes字段,返回支持的類型,我們這里顯示的返回TreeNode的類型。 定義好了轉換器之后,我們需要為在序列化的時候注冊該轉換器即可: 復制代碼 public static string SerializeToJson(object obj) { JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new TreeNodeJSConverter() }); return serializer.Serialize(obj); } public static T DeserializeJson<T>(string jsonString) { JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new TreeNodeJSConverter() }); return serializer.Deserialize<T>(jsonString); } 復制代碼 代碼中標紅的部分就是注冊轉化器的地方,用起來很方便,代碼都貼出來了,不再提供源碼下載了。。親,早點睡吧!
折騰着一上午, 已經用掉很多時間了, 這里就草草標記一下, 不再整理了. 其中參考了3篇文章, 深受啟發.
我已經把三篇放一起了, 文章地址: http://www.cnblogs.com/cs_net/articles/2361181.html
三篇原帖地址:
http://heeroluo.net/ShowPost15.aspx
http://www.cnblogs.com/Henllyee/archive/2008/07/18/1246161.html
http://www.cnblogs.com/zhuisha/archive/2008/12/08/1350463.html