想一想某一天,你在看LOL攻略的时候,系统忽然崩溃了,接着浏览器出现了密密麻麻的LOL账号和密码,你必定在想:“天啊,此次要发财了,说不定里面有超凡号或者王者号,我得赶忙所有记下来。”然而说完你就惊呆了,那么多的账号密码,并且所有写在了Json里面,一个一个复制粘贴要记到何时啊。。。若是这时候我在你身边,我必定会帮助你的,前提是,要分几个王者号给我噢。。。json
言归正传。数组
上面举的例子虽然有点不太现实,但实际上是想和你们说明一个问题,若是要解析Json或XML,请不要使用检索字符串的方式!!!浏览器
那么在说明如何解析Json和XML以前,咱们先来搞清楚两个概念:序列化和反序列化。工具
「序列化,即Serialization,是一个将对象的状态信息转变为能够存储或传输的形式的过程。」性能
「反序列化,即Deserialization,顾名思义是一个将能够存储或传输的序列转变为某个对象的状态信息的过程。」学习
从上面的定义,咱们能够得出两个结论:测试
① 这两个过程互为逆过程;ui
② 不管是序列化仍是反序列化,对象的状态信息都与一段序列相对应。spa
下面咱们就来看看如何用C#语言进行Json和XML的序列化和反序列化。设计
Json的序列化与反序列化
① 使用Newtonsoft.Json库
首先咱们先来看一段代码:
1 public void JsonDeserialize(string jsonString) 2 { 3 //Json的反序列化操做 4 JObject jObject = JsonConvert.DeserializeObject(jsonString) as JObject; 5 //清空listBox的结果 6 listBoxResult.Items.Clear(); 7 //将解析获得的数据显示到listBox中 8 listBoxResult.Items.Add("Name : " + jObject["name"] ?? "null"); 9 listBoxResult.Items.Add("Doing : " + jObject["doing"] ?? "null"); 10 listBoxResult.Items.Add("HandleContent : " + jObject["handleContent"] ?? "null"); 11 listBoxResult.Items.Add("DateTime : " + jObject["dateTime"] ?? "null"); 12 }
上面这个方法的做用是,接收一段Json序列做为参数,在完成反序列化后,将全部字段的值输出到ListBox中,下面是简单的测试:
能够看到,仅仅用了一行代码,Json的内容就按照字段全都被获取到了,下面就来好好告终一下Newtonsoft.Json这个库的用法吧。
Newtonsoft.Json是.NET平台中一个很是流行且高性能的Json序列化和反序列化开源库,提供了许多用于序列化和反序列化Json的类及方法,除此以外它还提供了用Linq操做Json、将Json转换成XML的功能,相较于传统的用于序列化和反序列化的DataContractJsonSerializer类更好用,更高效。本文只介绍其中比较重要的JsonConvert类和几个经常使用的类型,更多内容你们能够访问Json.NET的主页进行了解。
在介绍使用方法前,先来讲说Newtonsoft.Json.dll的引入。从VS2013开始,ASP.NET的项目均自带了Newtonsoft.Json.dll,直接使用便可,而其余版本或者其余项目若是没有的话,能够进入其官方主页,找到Github托管的地址进入并下载。下面演示引用该动态连接库的流程,
① 点击菜单栏的项目->添加引用,或者在解决方案资源管理器中找到项目,右键引用属性,点击添加引用,而后就会打开该窗口。
该窗口打开后,若是是第一次添加Newtonsoft.Json.dll,请找到右下方的浏览按钮,并选择Newtonsoft.Json.dll;若之前添加过该库,能够点击左侧的浏览菜单,并点击最近,右侧会显示之前添加过的库的信息,勾上须要的库,最后点击右下方的肯定便可。
添加了动态连接库还没完,要想使用其中的类和方法,须要引入相应的命名空间(若是你不想每定义一个对象就写一长串命名空间的话),本文主要用到的命名空间是Newtonsoft.Json以及Newtonsoft.Json.Linq。
完成这两步以后就能够舒舒服服的开始解析咱们伟大的Json了。
Now,先来介绍本库比较重要的类,JsonConvert。JsonConvert是个很是实用的工具类,它提供了用于反序列化的DeserializeObject方法和DeserializeObject<T>方法,以及用于序列化的SerializeObject方法。因为这些方法都是静态方法,所以无需建立JsonConvert对象,直接使用妥妥的(事实上JsonConvert是个静态类,根本没法被实例化)。
在最开始演示的例子中,咱们使用了JsonConvert.DeserializeObject方法,这是最原始的Json反序列化方法,它接收string类型的参数并返回object类型的对象。那么你也许会问到,它返回的对象到底是什么类型???想要真正得到该对象的信息,就必须将其强制转换成具体的派生类。那么,咱们可使用GetType方法来一探究竟。
1 private void btnDeserialize_Click(object sender, EventArgs e) 2 { 3 //JsonDeserialize(textBox1.Text); 4 var secretObject = JsonConvert.DeserializeObject(textBox1.Text); 5 MessageBox.Show(secretObject.GetType().ToString()); 6 }
咱们先在一个按钮的点击事件对应的方法中解析输入的Json序列,因为咱们还不肯定JsonConvert.DeserializeObject方法返回什么对象,所以咱们先用隐式类型对象去引用该方法返回的结果,而后调用其GetType方法,获取该对象所属类型的元数据,最后将对象的完整类型名显示在对话框当中。执行这段代码,你将会看到以下结果:
也许这时候在你的内心又有一万个草泥马呼啸而过,“What?还没搞清楚JsonConvert类的用法,怎么又跑出了一个奇怪的类?”
可是不要紧,接下来我会对你们的谜团进行逐一的解释。
在Newtonsoft.Json.Linq命名空间中,定义了一些用于表示不一样形式的Json字符串的类,它们包括JObject、JProperty、JToken、JArray、JValue、JContainer等等。
那么在这些类当中,JToken充当着标杆的角色,它是一个抽象类,并实现了IJEnumerable<T>接口,而IJEnumerable<T>接口继承自IEnumerable<T>接口,这意味着全部继承JToken的类均可以使用foreach语句进行遍历,而接下来要介绍的这些类,全都派生自JToken。另外,JToken还定义了First、Last、Parent、Root、Item、Previous、Next等遍历常常会用到的属性,所以在使用这些类的时候,遍历、查找具体类等操做就显得很是的简单。说了这么多,是时候说说JObject了,这是个JToken的派生类,同时也是实现了JContainer接口、ICollection<T>、IKeyValuePair<T>,这意味着JObject能够存储多个继承JToken类型的对象,同时也能够遍历它们,还能够用键去获取相应的值,也许你还不清楚这些有什么用,那么咱们先来了解一下Json的基本结构:
通常来讲,被大括号括起来的内容能够当作是一个总体的(一个对象的),而被中括号括起来的内容,能够当作是一组内容(一个数组),在一个复杂的Json结构中,大括号里面的某个键的值能够在嵌一个大括号,表明该属性值为另外一个Json对象,同理一个大括号中的某个键的值能够嵌一个中括号,表明该属性值是一个数组,如
1 //对于这种Json来讲,能够当作是一个对象,里面包含了name、doing、handleContent和dateTime属性。 2 { 3 "name":".NET Coder", 4 "doing":"Deserialization", 5 "handleContent":"Json", 6 "dateTime":"2017-09-11 12:12:12" 7 } 8 9 //对于这种Json来讲,能够当作是一个数组对象,里面包含了两个对象。 10 [ 11 { 12 "name":".NET Coder", 13 "doing":"Deserialization", 14 "handleContent":"Json", 15 "dateTime":"2017-09-11 12:12:12" 16 }, 17 { 18 "name":"Java Coder", 19 "doing":"Deserialization", 20 "handleContent":"Json", 21 "dateTime":"2017-09-22 21:21:21" 22 } 23 ]
在上一个演示的例子中,咱们用GetType方法去判断完成反序列化以后,实际返回的类型是什么;那么接下来咱们就将上面这两种不一样结构的Json序列放入程序中解析,看看获得的对象是否是都是JObject。
成功获得了JObject类型的对象。
此次咱们居然获得了JArray?!
这下咱们也许就明白了,JObject用于表示一个完整的Json对象结构,而JArray用于表示一个Json数组,其中会包括一个或多个Json对象。
在得到JObject对象以后,咱们能够用属性名(键)去得到相应的属性值,也可使用Next、Previous等属性去逐个访问JObject的属性。
而得到JArray对象以后,咱们可使用for或者foreach语句去遍历该对象内的元素,而后根据某个属性名或Next、Previous等属性去访问当前元素某个属性的属性值。
那么JProperty和JValue类型又有什么做用呢?
咱们能够来调试一下程序,并观察JObject对象内的结构,
如图咱们在使用foreach循环遍历JObject内部的属性,能够看到,First属性指向了JObject的第一个属性,而它的类型是JProperty,由此咱们能够知道,JProperty类用于描述一个Json对象中的某一个属性,在其内部包含了一个KeyValuePair<string, JToken>的成员变量,用于存储该Json属性的键值对,而这个值,其实就是JValue类型的变量。
那么到这里为止,咱们已经掌握了DeserializeObject方法的用法了,可是很明显,这种方法解析Json数据实在太麻烦了,咱们须要把全部Json对象的属性一个个获取到,而后才能获得其中的值,有没有更简介的办法呢?
有!有!有!重要的事情说三遍。
下面该轮到DeserializeObject<T>方法出场了,这是个泛型方法,指定T的类型并传入Json字符串做为参数后,该方法会完成反序列化并返回一个T类型的对象。那么咱们该指定什么类型给该方法呢?这个类型须要根据Json数据的层次关系自行设计,若是你们是第一次使用,不知道该如何设计,那么咱们来让VS帮咱们设计,下面给你们介绍一个神奇的功能,VS的自动建类功能:
仍是这段Json序列,假设咱们须要将其反序列化,如今殊不知道怎么设计类,那么咱们能够先复制这段Json序列,
1 { 2 "name":".NET Coder", 3 "doing":"Serialization", 4 "handleContent":"Json", 5 "dateTime":"2017-09-11 12:12:12" 6 }
打开VS,在菜单栏找到编辑->选择性粘贴,点击将Json粘贴为类,就会出现下面的结果,
本来空白的地方,忽然出现了一个类的定义(若是Json有多个层级,VS会帮咱们生成多个类),这个类清晰的反映了这段Json的结构,此时咱们只须要将这个自动生成的类,用到方法里就行了:
咱们改写了JsonDeserialize方法,并使用了由VS生成的类,能够看到咱们不须要再根据属性名去得到属性值了,也不用再担忧忘记有什么属性或者属性名写错,只须要在对象后面加上一点,该对象的全部属性都会出如今选择列表中。是否是忽然以为眼前一亮,一身轻松了呢?但其实这个作法隐藏了一点点小问题,由于VS是根据Json原来的属性名来定义属性的,这就要求Json的属性名遵循C#的标识符定义规则,而后事实上,在Json中可能会出现不符合C#标识符定义规范的属性名,这并无错,以下面这段Json:
1 { 2 "name":"money", 3 "price":"66.6", 4 "int":"64", 5 "a-b":"c-d", 6 "@#$":"sdfsdf" 7 }
能够看到,虽然VS会用_去代替不合法的字符串并按照规范生成属性名,可是这样子的属性名已经失去了它的意义,咱们没法得知这个属性表达了什么含义,为了解决这个问题,咱们可使用C#的注解来给该类的属性制定数据协定。
DataContract和DataMember注解来自于命名空间System.Runtime.Serialization,这个命名空间所在的动态连接库项目默认是没有引入的,须要手动引入,咱们只须要按照上面引入Newtonsoft.Json.dll的方式引入就好,可是须要注意的是,System.Runtime.Serialization所在的动态连接库在VS程序集中是提供了的,咱们只须要在添加引用界面,点击程序集,并在右侧的列表找到System.Runtime.Serialization并勾选,而后点击右下角的肯定按钮便可。
完成这些步骤以后咱们就能够改造Json的实体类,为其添加数据协定。什么是数据协定呢,即数据的发送方和接收方不须要使用相同的类型,只须要遵循数据协定便可。为了使该类可以添加数据协定,须要在类名上方添加[DataContract]注解,说明该类的属性使用了数据协定,而且容许将该类进行序列化,若是不加这个注解,就不能使用[DataMember]注解了。添加[DataContract]注解后,咱们就能够在对应的属性上添加[DataMember]注解,该注解有4个参数:
参数名 | 做用 |
Name | 标识该属性对应的Json属性名 |
Order | 标识该属性序列化和反序列化的顺序 |
IsRequired | 标识该属性在读取或反序列化时是否必须存在,类型为bool |
EmitDefaultValue | 标识在序列化的过程当中是否使用该属性的默认值来序列化 |
上图咱们使用了Name参数来设置每一个属性对应的Json属性名,从而加强了该类在程序中的可读性,同时又不会对序列化和反序列化的过程产生麻烦。下面再改写一下JsonDeserialize方法吧:
测试成功!!!
另外再补充一个小知识,当咱们须要反序列化不少简单的Json时,为了不建立过多的实体类,咱们可使用dynamic动态类型来代替自定义的实体类,在这里咱们改写上述例子,使用dynamic类型来实现相应功能:
在这个例子中,我没有定义新的类,而是使用了动态类型,这种类型的对象有一个特色:运行时检查。在使用这种对象的时候咱们要注意,不管是使用它的属性,仍是调用它的方法,编译器都不会对其进行编译时检查,也就是说咱们必须保证属性名或者方法名没有写错,不然会出现表达式为空或者引起异常的现象。
调用不存在的属性返回Null。
调用不存在的方法引起异常。
在没有错误的状况下,成功运行!
到这里反序列化的介绍算是告一段落了,接下来咱们讲讲使用JsonConvert.SerializeObject方法完成序列化操做。
前面那么多操做的目的是为了解析Json字符串,让咱们能方便的得到其中的信息,可是若是咱们不是信息的接收方而是发送方呢,咱们要怎么构造Json字符串?显然不可能用字符串拼接的方法,效率低下,难以排错。所以今天咱们就要用一行代码来完成这件事情。
没错,就只有一行代码!!!
看吧,是否是一行代码就让一个活生生的对象瞬间变成了一串Json了呢!!!
② 使用DataContractJsonSerializer类
其实本人并不想讲解这个类,第一使用过程很是麻烦,第二效率低下,实际工做中也不会用它,可是从学习的角度咱们仍是要了解一下这个类的存在,而且这个类序列化和反序列化Json的过程和XML的序列化和反序列化很类似,所以仍是有必要让你们了解一下。
在此很少说其余废话,直接上代码:
DataContractJsonSerializer序列化
1 public void OldJsonSerialize() 2 { 3 DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HandleObject)); 4 //建立实体类 5 HandleObject jObject = new HandleObject() { 6 Name = ".NET Coder", 7 Doing = "Serialization", 8 HandleContent = "Json", 9 DateTime = DateTime.Now.ToString("yyyy-MM-ss") 10 }; 11 //建立内存流,用于存储序列化后获得的内容 12 MemoryStream ms = new MemoryStream(); 13 //将对象序列化后存入内存流 14 serializer.WriteObject(ms, jObject); 15 //将内存流的内容转换成字符串并输出到文本框 16 textBox1.Text = Encoding.UTF8.GetString(ms.ToArray()); 17 }
DataContractJsonSerializer反序列化
1 public void OldJsonDeserialize() 2 { 3 DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HandleObject)); 4 //将Json字符串转换成字节数组 5 byte[] content = Encoding.UTF8.GetBytes(textBox1.Text); 6 //用字节数组初始化一个内存流对象 7 MemoryStream ms = new MemoryStream(content); 8 //完成反序列化操做 9 HandleObject handleObject = serializer.ReadObject(ms) as HandleObject; 10 //将解析获得的数据显示到listBox中 11 listBoxResult.Items.Add("Name : " + handleObject.Name ?? "null"); 12 listBoxResult.Items.Add("Doing : " + handleObject.Doing ?? "null"); 13 listBoxResult.Items.Add("HandleContent : " + handleObject.HandleContent ?? "null"); 14 listBoxResult.Items.Add("DateTime : " + (handleObject.DateTime.ToString() ?? "null")); 15 }
DataContractJsonSerializer类和JsonConvert类最大的不一样就在于,DataContractJsonSerializer类将操做流的细节暴露了出来,须要由用户自行完成,相比JsonConvert直接使用字符串要麻烦很多。