一、使用Json.Net
Json.Net是支持序列化和反序列化DataTable、DataSet、Entity Framework和Entity的。
1、把DataTable轉換成json格式,使用最新Json.Net DLL ,已經內置轉換器。
DataTable dt = catDAO.GetAllCategory(); string result = JsonConvert.SerializeObject(dt);
注意:Json.Net老版本轉換DataTable,需要自定義一個JavaScriptConverter派生類。
//引入命名空間 using Newtonsoft.Json; using Newtonsoft.Json.Converters; void Main() { //執行轉換 DataTable dt = catDAO.GetAllCategory(); string result = JsonConvert.SerializeObject(dt, new DataTableConverter()); } ////// DataTable JSON轉換類 /// public class DataTableConverter : JavaScriptConverter { public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { DataTable dt = obj as DataTable; ;//將Datatable轉成Dictionary完成序列化 Dictionary<string, object> result = new Dictionary<string, object>(); if (dt != null) { ArrayList arrList = new ArrayList(); foreach (DataRow dr in dt.Rows)//循環每行 { Dictionary<string, object> dic = new Dictionary<string, object>(); foreach (DataColumn dc in dt.Columns) { dic.Add(dc.ColumnName, dr[dc.ColumnName]);//Dic中存儲列名和每列值 } arrList.Add(dic);//ArrayList中保存各行信息 } result[dt.TableName] = arrList; ;//表名作為Key,ArrayList作為值 } return result; } public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { if (dictionary == null) throw new ArgumentNullException("dictionary"); if (type == typeof(DataTable)) { //將Dictionary轉成Datatable完成反序列化 foreach (KeyValuePair<string, object> table in dictionary) { DataTable dt = new DataTable(table.Key);//表名 ArrayList rows = (ArrayList)table.Value; //列名 Dictionary<string, object> row = serializer.ConvertToType < string, object>>(rows[0]); foreach (string item in row.Keys) { dt.Columns.Add(item); } //每行數據 for (int i = 0; i < rows.Count; i++) { DataRow dr = dt.NewRow(); Dictionary<string, object> dic = serializer.ConvertToType < string, object>>(rows[i]); foreach (KeyValuePair<string, object> item in dic) { dr[item.Key] = item.Value; } dt.Rows.Add(dr); } return dt; } } return null; } /// /// 獲取本轉換器支持的類型 /// public override IEnumerable SupportedTypes { get { return new Type[] { typeof (DataTable) }; } } } </dictionary<</dictionary<
2、對Json.Net序列化和反序列化的控制
實例化一個JsonSerializerSettings對象,並把它賦值給JsonConvert的參數即可.
var jSetting = new JsonSerializerSettings(); string json = JsonConvert.SerializeObject(obj,jSetting);
DataTable:
//序列化DataTable DataTable dt = new DataTable(); dt.Columns.Add("Age", Type.GetType("System.Int32")); dt.Columns.Add("Name", Type.GetType("System.String")); dt.Columns.Add("Sex", Type.GetType("System.String")); dt.Columns.Add("IsMarry", Type.GetType("System.Boolean")); for (int i = 0; i < 4; i++) { DataRow dr = dt.NewRow(); dr["Age"] = i + 1; dr["Name"] = "Name" + i; dr["Sex"] = i % 2 == 0 ? "男" : "女"; dr["IsMarry"] = i % 2 > 0 ? true : false; dt.Rows.Add(dr); } Console.WriteLine(JsonConvert.SerializeObject(dt, new JsonSerializerSettings() { Formatting = Newtonsoft.Json.Formatting.Indented })); //[ // { // "Age": 1, // "Name": "Name0", // "Sex": "男", // "IsMarry": false // }, // { // "Age": 2, // "Name": "Name1", // "Sex": "女", // "IsMarry": true // }, // { // "Age": 3, // "Name": "Name2", // "Sex": "男", // "IsMarry": false // }, // { // "Age": 4, // "Name": "Name3", // "Sex": "女", // "IsMarry": true // } //]
利用上面字符串進行反序列化
string json = JsonConvert.SerializeObject(dt); dt = JsonConvert.DeserializeObject(json); foreach (DataRow dr in dt.Rows) { Console.WriteLine("{0}\t{1}\t{2}\t{3}\t", dr[0], dr[1], dr[2], dr[3]); }
//1 Name0 男 False
//2 Name1 女 True
//3 Name2 男 False
//4 Name3 女 True
二、空值的處理
這里的空值指的是引用類型為NULL時,Json.Net如何處理.通過設置jSetting.NullValueHandling的值來確定,該值為枚舉類型。
- NullValueHandling.Ignore: 忽略為NULL的值
- NullValueHandling.Include: 默認值,包括為NULL的值
實例:
void Main() { Staff jack = new Staff { Name = "Jack", Age = 31, Gender = "Male", DepartmentName = "Personnel Department", Leader = null }; var jSetting = new JsonSerializerSettings(); jSetting.NullValueHandling = NullValueHandling.Ignore; string json = JsonConvert.SerializeObject(jack, jSetting); Console.WriteLine(json); //{"Name":"Jack","Age":31,"Gender":"Male","DepartmentName":"Personnel Department"} } //員工類: public class Staff { public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } public string DepartmentName { get; set; } public Staff Leader { get; set; } }
三、默認值的處理
一般是對於值類型的處理,通過設置jSetting.DefaultValueHandling的值來確定,該值為枚舉類型.
- DefaultValueHandling.Ignore:序列化和反序列化時,忽略默認值
- DefaultValueHandling.Include:序列化和反序列化時,包含默認值
給成員設置默任值,用到"DefaultValue(value)"特性,當然別忘了引入命名空間"System.ComponentModel",假設員工的年齡默認值為30。
[DefaultValue(30)] public int Age { get; set; }
序列化時我想忽略為默認值的成員:
Staff jack = new Staff { Name = "Jack", Age = 30, Gender = "Male", DepartmentName = "Personnel Department", Leader = null }; var jSetting = new JsonSerializerSettings(); jSetting.DefaultValueHandling = DefaultValueHandling.Ignore; string json = JsonConvert.SerializeObject(jack, jSetting); Console.WriteLine(json); //{"Name":"Jack","Gender":"Male","DepartmentName":"Personnel Department"}
四、忽略某些屬性
首先介紹Json.Net成員序列化的模式:OptOut 和 OptIn.
- OptOut:默認值。類中所有公有成員會被序列化,如果不想被序列化,可以用特性JsonIgnore。
- OptIn:在這種情況下,所有的成員不會被序列化,類中的成員只有標有特性JsonProperty的才會被序列化,當類的成員很多,但客戶端僅僅需要一部分數據時,很有用。
假如客戶僅僅需要員工的姓名,此時序列化:
void Main() { Staff jack = new Staff { Name = "Jack", Age = 30, Gender = "Male", DepartmentName = "Personnel Department", Leader = null }; string json = JsonConvert.SerializeObject(jack); Console.WriteLine(json); //{"Name":"Jack"} } [JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)] public class Staff { [JsonProperty] public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } public string DepartmentName { get; set; } public Staff Leader { get; set; } }
如果客戶不想要員工的領導信息,序列化:
void Main() { Staff tom = new Staff { Name = "Tome", Age = 42, Gender = "Male", DepartmentName = "Personnel Department" }; Staff jack = new Staff { Name = "Jack", Age = 30, Gender = "Male", DepartmentName = "Personnel Department", Leader = tom }; string json = JsonConvert.SerializeObject(jack); Console.WriteLine(json); //{"Name":"Jack","Age":30,"Gender":"Male","DepartmentName":"Personnel Department"} } public class Staff { public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } public string DepartmentName { get; set; } [JsonIgnore] public Staff Leader { get; set; } }
五、支持非公共成員
Json.Net序列化對象時,默認情況下僅僅序列化公有成員,如果想要非公有成員也被序列化,就要在該成員上加特性"JsonProperty"。
六、日期處理
JsonConverters會在序列化和反序列化時被用到。JsonConverters允許手動對Json的控制。當Json的結構很復雜和你想改變一個類型怎么樣被序列化時,這是非常有用的。
當一個JsonConverters被添加到JsonSerializer時,它會檢查每一個要被序列化和反序列化的值,並返回CanConvert,如果為True,則JsonConverter讀和寫這個值;
需要注意的是,雖然JsonConverter能夠使你可以完全的控制Json的值,但是很多的Json.Net序列化的特性被限制,像是類型名稱和引用處理。
所有的JsonConvert都在命名空間 "Newtonsoft.Json.Converters"下
1、使用IsoDateTimeConverter 和 JavaScriptDateTimeConverter標准格式
這是Json.Net中自帶的兩個處理日期的類:
- IsoDateTimeConverter :默認的ISO"標准日期格式,它的格式是"yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"。
- JavaScriptTimeConverter:它的格式是 "new Date(ticks)",其實返回的是一個JavaScript的Date對象.
有兩種方式來應用JsonConverter,改變Json序列化和反序列化的行為.
1、如果你要序列化的日期格式是統一的,可以考慮如下方式
假設我們為員工添加兩個日期類型的成員,出生日期和入職日期,我們的客戶要求日期類型的成員返回javascript的日期對象
void Main() { Staff jack = new Staff { Name = "Jack", Age = 30, Gender = "Male", DepartmentName = "Personnel Department", BirthDate = new DateTime(1982, 2, 12), EmploymentDate = new DateTime(2010, 12, 12) }; string json = JsonConvert.SerializeObject(jack, new JavaScriptDateTimeConverter()); Console.WriteLine(json); //{"Name":"Jack","Age":30,"Gender":"Male","DepartmentName":"Personnel Department","Leader":null,"BirthDate":new Date(382291200000),"EmploymentDate":new Date(1292083200000)} } public class Staff { public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } public string DepartmentName { get; set; } public Staff Leader { get; set; } public DateTime BirthDate { get; set; } public DateTime EmploymentDate { get; set; } }
2、如果想要不同的日期類型成員序列化后,以不同的形式顯示.
現在我們的客戶要求出生日期以"ISO"標准日期格式返回,入職日期以Javascript的Date對象格式返回,修改我們的員工類,,通過特性"JsonConverter"來實現差異化。
void Main() { Staff jack = new Staff { Name = "Jack", Age = 30, Gender = "Male", DepartmentName = "Personnel Department", BirthDate = new DateTime(1982, 2, 12), EmploymentDate = new DateTime(2010, 12, 12) }; string json = JsonConvert.SerializeObject(jack); Console.WriteLine(json); //{"Name":"Jack","Age":30,"Gender":"Male","DepartmentName":"Personnel Department","Leader":null,"BirthDate":"1982-02-12T00:00:00","EmploymentDate":new Date(1292083200000)} } public class Staff { public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } public string DepartmentName { get; set; } public Staff Leader { get; set; } [JsonConverter(typeof(IsoDateTimeConverter))] public DateTime BirthDate { get; set; } [JsonConverter(typeof(JavaScriptDateTimeConverter))] public DateTime EmploymentDate { get; set; } }
2、自定義日期格式
要求返回的格式是"2012年4月20日"這種格式,使用“IsoDatetimeConverter"內部的日期格式DefaultDateTimeFormat。
void Main() { Staff jack = new Staff { Name = "Jack", Age = 30, Gender = "Male", DepartmentName = "Personnel Department", BirthDate = new DateTime(1982, 2, 12), EmploymentDate = new DateTime(2010, 12, 12) }; IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy'年'MM'月'dd'日'" }; string json = JsonConvert.SerializeObject(jack, dtConverter); Console.WriteLine(json); //{"Name":"Jack","Age":30,"Gender":"Male","DepartmentName":"Personnel Department","Leader":null,"BirthDate":"1982年02月12日","EmploymentDate":"2010年12月12日"} } public class Staff { public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } public string DepartmentName { get; set; } public Staff Leader { get; set; } public DateTime BirthDate { get; set; } [JsonConverter(typeof(JavaScriptDateTimeConverter))] public DateTime EmploymentDate { get; set; } }
七、自定義序列化的字段名稱
默認情況下,Json.Net序列化后結果中的字段名稱和類中屬性的名稱一致.如果想自定義序列化后的字段名稱,可以使用JsonProperty.例如:
public class Person { public int Id { get; set; } public string Name { get; set; } }
默認序列化的結果為: {"Id":1,"Name":"楊過"},如果不想用默認的字段名稱,可以使用如下方式:
public class Person { [JsonProperty(PropertyName = "PersonId")] public int Id { get; set; } [JsonProperty(PropertyName = "PersonName")] public string Name { get; set; } }
這樣序列化的結果為:{"PersonId":1,"PersonName":"楊過"}
八、枚舉值的自定義格式化問題
默認情況下對於實體里面的枚舉類型系統是格式化成改枚舉對應的整型數值,那如果需要格式化成枚舉對應的字符怎么處理呢?Newtonsoft.Json也幫我們想到了這點,下面看實例
void Main() { string json = JsonConvert.SerializeObject(new TestEnmu()); Console.WriteLine(json); //{"Type":0} } public enum NotifyType { ////// Emil發送 /// Mail = 0, /// /// 短信發送 /// SMS = 1 } public class TestEnmu { /// /// 消息發送類型 /// public NotifyType Type { get; set; } }
現在改造一下,輸出"Type":"Mail"
public class TestEnmu { ////// 消息發送類型 /// [JsonConverter(typeof(StringEnumConverter))] public NotifyType Type { get; set; } }
其它的都不變,在Type屬性上加上了JsonConverter(typeof(StringEnumConverter))表示將枚舉值轉換成對應的字符串,而StringEnumConverter是Newtonsoft.Json內置的轉換類型,最終輸出結果:{"Type":"Mail"}
九、Dynamic類型的序列化
在.Net4.0中,Dynamic基本上有兩種用法.
- 第一種是作為屬性來用,在這種情況下序列化時會根據實際的類型來序列化。
- 第二種用法是繼承了IDynamicMetaObjectProvider 接口或者DynamicObject基類。例如.Net中內置的類ExpandoObject ,這三者之間的關系是:ExpandoObject,DynamicObject都繼承了IDynamicMetaObjectProvider.
這種情況下,只有DynamicMetaObject.GetDynamicMemberNames的返回的成員的屬性會被序列化.
首先新建一個類,繼承基類 DynamicObject,在主程序中,做如下操作:
void Main() { dynamic md = new MyDynamic();//必須是用dynamic來聲明變量,不能用MyDynamic,否則它就不是動態類型了。 md.Name = "Jack"; Action<string> output = new Action<string>((value) => { Console.WriteLine(value); }); md.Output = output; Console.WriteLine(JsonConvert.SerializeObject(md)); md.Output(md.Name); //{ "Name":"Jack","Output":{ "Delegate":{ },"target0":{ },"method0":{ "Name":"b__0_0","AssemblyName":"query_dmdxhj, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null","ClassName":"UserQuery+<>c","Signature":"Void //Jack } public class MyDynamic : DynamicObject { //用來存儲動態添加的變量和值 private Dictionary<string, object> members = new Dictionary<string, object>(); ///b__0_0(System.String)","Signature2":"System.Void b__0_0(System.String)","MemberType":8,"GenericArguments":null} } } /// 獲取所有的動態成員名稱 /// /// 動態成員名稱 public override IEnumerable<string> GetDynamicMemberNames() { return members.Keys; } /// /// 設置動態成員名稱,也就是在發生賦值語句時出發該方法 /// 例如:dynamic dy = new MyDynamic(); /// dy.Name = "Jack"; /// /// 用於動態設置操作 /// 預設的值 /// public override bool TrySetMember(SetMemberBinder binder, object value) { if (!members.ContainsKey(binder.Name)) { members.Add(binder.Name, value); } else members[binder.Name] = value; return true; } /// /// 根據名稱獲取動態成員的值 /// 例如:dynamic dy = new MyDynamic(); /// var name = dy.Name; /// /// 用戶動態獲取操作 /// 將獲取的值賦給的對象 /// public override bool TryGetMember(GetMemberBinder binder, out object result) { if (members.ContainsKey(binder.Name)) { result = members[binder.Name]; return true; } else return base.TryGetMember(binder, out result); } /// /// 如果成員的類型是委托,則調用它 /// /// 用戶動態委托操作 /// 委托調用的參數 /// 委托調用返回的結果 /// public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (members.ContainsKey(binder.Name) && members[binder.Name] is Delegate) { result = (members[binder.Name] as Delegate).DynamicInvoke(args); return true; } else { return base.TryInvokeMember(binder, args, out result); } } }
是的,委托類型也被序列化了,這並不是我們想要的,有沒有方法來將它排除呢?答案就在GetDynamicMemberNames方法,默認我們返回的是所有的Keys,只要我們加一定的限制條件即可.修改之后的代碼
public override IEnumerable<string> GetDynamicMemberNames() { foreach (string key in members.Keys) { if (!(members[key] is Delegate)) yield return key; } }
此時的運行結果:
{"Name":"Jack"}
Jack