基本用法
Json.Net是支持序列化和反序列化DataTable,DataSet,Entity Framework和Entity的。下面分别举例说明序列化和反序列化。
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));
利用上面字符串进行反序列化
string json = JsonConvert.SerializeObject(dt); dt=JsonConvert.DeserializeObject<DataTable>(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]); }
高级用法
预备知识:
要想实现对Json.Net序列化和反序列化的控制,就要用到类JsonSerializerSettings,用法很简单实例化一个对象,并把它赋值给JsonConvert的参数即可.
var jSetting = new JsonSerializerSettings(); string json = JsonConvert.SerializeObject(obj,jSetting);
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; } }
1、空值的处理
这里的空值指的是引用类型为NULL时,Json.Net如何处理.通过设置jSetting.NullValueHandling的值来确定,该值为枚举类型。
- NullValueHandling.Ignore: 忽略为NULL的值
- NullValueHandling.Include: 默认值,包括为NULL的值
实例:
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);
结果:
2、默认值的处理
一般是对于值类型的处理,通过设置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);
结果:
3、忽略某些属性
首先介绍Json.Net序列化的模式:OptOut 和 OptIn.
- OptOut:默认值,类中所有公有成员会被序列化,如果不想被序列化,可以用特性JsonIgnore。
- OptIn:默认情况下,所有的成员不会被序列化,类中的成员只有标有特性JsonProperty的才会被序列化,当类的成员很多,但客户端仅仅需要一部分数据时,很有用。
假如客户仅仅需要员工的姓名,此时
[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; } }
序列化:
Staff jack = new Staff { Name = "Jack", Age = 30, Gender = "Male", DepartmentName = "Personnel Department", Leader = null }; string json = JsonConvert.SerializeObject(jack);
结果:
如果客户不想要员工的领导信息
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; } }
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);
结果:
4、支持非公共成员
Json.Net序列化对象时,默认情况下仅仅序列化公有成员,如果想要非公有成员也被序列化,就要在该成员上加特性"JsonProperty"。
5、日期处理
JsonConverters会在序列化和反序列化时被用到。JsonConverters允许手动对Json的控制。当Json的结构很复杂和你想改变一个类型怎么样被序列化时,这是非常有用的。当一个JsonConverters被添加到JsonSerializer时,它会检查每一要被序列化和反序列化的值,并返回CanConvert,如果为True,则JsonConverter读和写这个值;需要注意的是,虽然JsonConverter能够使你可以完全的控制Json的值,但是很多的Json.Net序列化的特性被限制,像是类型名称和引用处理。所有的JsonConvert都在命名空间 "Newtonsoft.Json.Converters"下
1、IsoDateTimeConverter 和 JavaScriptDateTimeConverter
这是Json.Net中自带的两个处理日期的类,默认是IsoDateTimeConverter ,它的格式是"yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK".另一个是JavaScriptTimeConverter,它的格式是 "new Date(ticks)",其实返回的是一个JavaScript的Date对象.
有两种方式来应用JsonConverter,改变Json序列化和反序列化的行为.
1、如果你要序列化的日期格式是统一的,可以考虑如下方式
假设我们为员工添加两个日期类型的成员,出生日期和入职日期
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; } }
我们的客户要求日期类型的成员返回javascript的日期对象
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);
结果:
2、如果想要不同的日期类型成员序列化后,以不同的形式显示.
现在我们的客户要求出生日期以"ISO"标准日期格式返回,入职日期以Javascript的Date对象格式返回,修改我们的员工类,,通过特性"JsonConverter"来实现差异化。
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; } }
序列化:
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);
结果:
2、自定义日期格式
要求返回的格式是"2012年4月20日"这种格式,使用“IsoDatetimeConverter"内部的日期格式DefaultDateTimeFormat。
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);
结果:
6、自定义序列化的字段名称
默认情况下,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":"杨过"}
7、枚举值的自定义格式化问题
默认情况下对于实体里面的枚举类型系统是格式化成改枚举对应的整型数值,那如果需要格式化成枚举对应的字符怎么处理呢?Newtonsoft.Json也帮我们想到了这点,下面看实例
public enum NotifyType { /// <summary> /// Emil发送 /// </summary> Mail=0, /// <summary> /// 短信发送 /// </summary> SMS=1 } public class TestEnmu { /// <summary> /// 消息发送类型 /// </summary> public NotifyType Type { get; set; } } JsonConvert.SerializeObject(new TestEnmu());
输出结果:
现在改造一下,输出"Type":"Mail"
public class TestEnmu { /// <summary> /// 消息发送类型 /// </summary> [JsonConverter(typeof(StringEnumConverter))] public NotifyType Type { get; set; } }
其它的都不变,在Type属性上加上了JsonConverter(typeof(StringEnumConverter))表示将枚举值转换成对应的字符串,而StringEnumConverter是Newtonsoft.Json内置的转换类型,最终输出结果
8、Dynamic类型的序列化
在.Net4.0中,Dynamic基本上有两种用法.
第一种是作为属性来用,在这种情况下序列化时会根据实际的类型来序列化.
第二种用法是继承了IDynamicMetaObjectProvider 接口或者DynamicObject 基类,例如.Net中内置的类ExpandoObject ,这三者之间的关系是:ExpandoObject,DynamicObject都继承了IDynamicMetaObjectProvider.这种情况下,只有DynamicMetaObject.GetDynamicMemberNames的返回的成员的属性会被序列化.
首先新建一个类,继承基类 DynamicObject
public class MyDynamic : DynamicObject { //用来存储动态添加的变量和值 private Dictionary<string, object> members = new Dictionary<string, object>(); /// <summary> /// 获取所有的动态成员名称 /// </summary> /// <returns>动态成员名称</returns> public override IEnumerable<string> GetDynamicMemberNames() { return members.Keys; } /// <summary> /// 设置动态成员名称,也就是在发生赋值语句时出发该方法 /// 例如:dynamic dy = new MyDynamic(); /// dy.Name = "Jack"; /// </summary> /// <param name="binder">用于动态设置操作</param> /// <param name="value">预设的值</param> /// <returns></returns> 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; } /// <summary> /// 根据名称获取动态成员的值 /// 例如:dynamic dy = new MyDynamic(); /// var name = dy.Name; /// </summary> /// <param name="binder">用户动态获取操作</param> /// <param name="result">将获取的值赋给的对象</param> /// <returns></returns> 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); } /// <summary> /// 如果成员的类型是委托,则调用它 /// </summary> /// <param name="binder">用户动态委托操作</param> /// <param name="args">委托调用的参数</param> /// <param name="result">委托调用返回的结果</param> /// <returns></returns> 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); } } }
在主程序中,做如下操作:
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);
结果:
是的,委托类型也被序列化了,这并不是我们想要的,有没有方法来将它排除呢?答案就在GetDynamicMemberNames方法,默认我们返回的是所有的Keys,只要我们加一定的限制条件即可.修改之后的代码
public override IEnumerable<string> GetDynamicMemberNames() { foreach (string key in members.Keys) { if(!(members[key] is Delegate)) yield return key; } }
此时的运行结果: