本期来讲解在AJAX中使用json格式数据。不过我们先不说json的事,先来做个案例,然后由这个案例我们再来讨论为啥要用json数据,以及怎么用。
一、案例
非常经典,也是非常简单的AJAX案例,省市联动。就是在网上常见的,选择一个省份,然后城市所在的<select>标签中再动态加载进来所选省份包含的城市选项。
HTML是极其简单的。为了让案例更简单,省份的<select>标签中的选项都写死了,其中value代表省份的主键id。城市所对应的<select>标签也是如此,不在赘述。
<body> 省份:<select id="province"> <option value="1">河北</option> <option value="2">河南</option> <option value="3">山东</option> <option value="4">山西</option> </select> 城市:<select id="city"></select> </body>
后台代码也是非常简单的,仍然使用一般处理程序,如下。
其中City是城市实体类,3个字段。
需要简单说明的是这个一般处理程序,它的思路是:
1.通过QueryString获取到get请求传递过来的proId(省份id)。
2.根据proId从数据库中获取到包含的城市(IList<City>)。
3.遍历这个IList<City>城市集合,然后拼写成类似于"<option value='0100001'>成都</option><option value='0100002'>绵阳</option>......."这样的字符串。因为这样的字符串可以直接放到<select>标签对的内部,形成该下拉选项框的选项。
public class City { public int Id { get; set; }//城市主键 public string Name { get; set; }//城市名字 public int proId { get; set; }//省份的id }
public class citysHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; if (context.Request.HttpMethod.ToUpper() == "GET") { string proId = context.Request.QueryString["proId"]; Citys citys = new Citys(); IList<City> cList = citys.GetCitysByProId(proId); StringBuilder opts = new StringBuilder(); foreach (City c in cList) { opts.Append("<option value='" + c.Id + "'>"); opts.Append(c.Name); opts.Append("</option>"); } context.Response.Write(opts.ToString()); } } public bool IsReusable { get { return false; } } }
接下来是客户端代码。当用户选择任意一个省份后,触发该省份下拉框的onchange事件,在该事件中使用AJAX方式,发出get请求,访问服务端citysHandler.ashx。代码如下:
代码没有太多需要解释的,由于服务端直接返回的就是拼接好html标签的字符串,所以直接作为innerHTML放到<select id="city"><select>里去就可以了,倒是非常简单。
<script> window.onload = function () { var selProv = document.getElementById("province"); selProv.onchange = function () { if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } var proId = this.value; var url = "citysHandler.ashx" + "?proId=" + proId; xhr.open("get", url, true); xhr.setRequestHeader("If-Modified-Since", "0"); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var opts = xhr.responseText; var selCity = document.getElementById("city"); selCity.innerHTML = opts; } } xhr.send(null); } } </script>
二、弊端
功能虽然实现了,但是弊端还是挺大的。
1.服务端返回的字符串不光包含需要展示给用户看的数据,比如城市名字,城市的id,更重要的是这些数据被<option>标签包住了。换句话说服务端返回的数据不干净,或者说不纯粹,加载了太多html标签。这么做的弊端在于把这个后端的数据接口限制死了,由于<option>只能放在<select>标签中作为选项用,那么如果以后客户希望在一个<table>中展示所有数据,那么这个后台的服务就不可重用了。
2.夹杂了很多html标签后,要传输的数据量必定加大,那么势必降低程序的性能,至少客户端的响应速度会变慢,这对于性能控制狂来说是不可接受的。
三、改进--json
json只是一种数据格式,它是从JavaScript的字面量表示法演变过来的,也是JavaScript语言中的一种对象描述方式。它的语法特别简单:{“key1” : "val1" , “key2” : "val2" , ....... }。它与字面量表示法唯一的区别就是json格式要求所有的key和value都要用双引号引住,单引号不可以,不引更不可以。
好了,先看服务端的代码改造吧。只看一般处理程序的PR方法就OK了。
这里使用了System.Web.Script.Serialization.JavaScriptSerializer对一个集合进行序列化,序列化的结果当然就是一个JSON格式的字符串。
public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; if (context.Request.HttpMethod.ToUpper() == "GET") { string proId = context.Request.QueryString["proId"]; Citys citys = new Citys(); IList<City> cList = citys.GetCitysByProId(proId); System.Web.Script.Serialization.JavaScriptSerializer jss = new System.Web.Script.Serialization.JavaScriptSerializer(); string jsonCitys = jss.Serialize(cList); context.Response.Write(jsonCitys); } }
★ 需要注意的是JSON对象与JSON格式的字符串是有本质不同的。
打个比方:
int i = 12345;
string str = "12345";看上去都是存的12345,但是有本质不同,变量i存的是一个整数,能做算术运算的整数;而变量str里存的数据看上去也是“12345”,但这是个字符串,不能做算术运算。
如果你还不理解,再来看个例子。
int[ ] arr = {22, 12, 34, 45};很显然这是个整形数组
“ {22, 12, 34, 45}”,而这个是字符串,只不过跟上边那个数组“长得”很像罢了。
所以这里经过.NET提供的JavaScriptSerializer序列化过后的数据,是个字符串,长得样子大概就是 “[{"Id":1,"Name":"成都","proId":5},{"Id":2,"Name":"绵阳","proId":5},{"Id":3,"Name":"广元","proId":5}]” 这个样子。不过看好了,两头有“”,这个是个正经八百的字符串,所以,这样的数据传递到前端后,我们需要使用JavaScript的方式,把这个字符串再转换成JSON对象。
接下来改造前台JavaScript代码
只需要看回调函数就可以了,其他地方不需要改。
xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var opts = xhr.responseText; var selCity = document.getElementById("city"); //selCity.innerHTML = opts; var jsonCitys = JSON.parse(opts); for (var i = 0; i < jsonCitys.length; i++) { var opt = document.createElement("option"); opt.setAttribute("value", jsonCitys[i].Id); opt.innerHTML = jsonCitys[i].Name; selCity.appendChild(opt); } } } xhr.send(null);
关键点是这条语句:var jsonCitys = JSON.parse(opts); JSON.parse()是JavaScript提供的1个API,它的作用是把一个JSON格式的字符串,转化成一个JSON对象。当然,如果传递过来的字符串是一个由多个JSON格式对象组合成的数组样式的字符串,就比如我们这里就是如此:“[{"Id":1,"Name":"成都","proId":5},{"Id":2,"Name":"绵阳","proId":5},{"Id":3,"Name":"广元","proId":5}]” 。我们从后台得到的数据,引号内部首先是一对 [ ] ,这个方括号显然就是js中数组的符号了。
那么这种情况下,JSON.parse()会把这个字符串解析成由多个JSON对象组成的一个JavaScript数组。然后我们就能用for循环遍历它了。
接下来的DOM操作,就不必细说了。
再回头看这个例子,经过改造后,后台传递来的数据是“纯粹的”,不带有任何表示层的东西,这样带来的好处:1.要传输的数据量小,轻量化了。2.更利于表示层按各种要求展示,表示层想怎么玩就怎么玩。爽!
四、为什么是JSON?
因为json有以下几个显而易见的好处:
- 简单,结构清晰
-
省空间,基本没什么浪费的字符
-
标准,主流的都用它,也都支持它
-
语言无关,任何语言都能轻松搞它
其实在json兴起之前,用的更多的是XML格式的数据,相比较来说,json更轻量级,解析时更简单。
by the way,如果你的后台传递过来的是XML格式数据,那么通过异步对象XMLHttpRequest去获取响应报文时,就需要使用另一个api,代码大致如下:
var xmlResp = xhr.responseXML;
至于javascript如何解析XML数据,其实就跟DOM操作差不多,用的也还是那几个API,大家试试就会了。