昨天同学问我有关如何快速读取多层级xml文件的问题,于是我就使用省、市、县模拟了一个三级联动的例子,客户端使用jQuery实现异步加载服务器返回的json数据,服务器端则使用XPath表达式查询数据。如下是实现过程:


第一步:准备xml文件,并放置在网站根目录下,名为Area.xml

View Code
 1 <?xml version="1.0" encoding="utf-8" ?>
2 <area>
3 <province id="1" name="北京">
4 <city id="1" name="北京">
5 <county id="1" name="东城区" />
6 <county id="2" name="西城区" />
7 </city>
8 </province>
9 <province id="2" name="河北省">
10 <city id="1" name="石家庄市">
11 <county id="1" name="正定县" />
12 <county id="2" name="灵寿县" />
13 </city>
14 <city id="2" name="邯郸市">
15 <county id="1" name="邯郸县" />
16 <county id="2" name="永年县" />
17 </city>
18 </province>
19 <province id="3" name="海南省">
20 <city id="1" name="海口市">
21 <county id="1" name="龙华区" />
22 <county id="2" name="秀英区" />
23 <county id="3" name="美兰区" />
24 </city>
25 <city id="2" name="三亚市">
26 <county id="1" name="天涯镇" />
27 <county id="2" name="凤凰镇" />
28 </city>
29 </province>
30 </area>

第二步:创建与xml文件中定义的元素对应的实体类。

<province/>对应province类

View Code
 1 public class Province
2 {
3 private string id;
4 /// <summary>
5 /// 编号
6 /// </summary>
7 public string Id
8 {
9 get { return id; }
10 set { id = value; }
11 }
12 private string name;
13 /// <summary>
14 /// 名称
15 /// </summary>
16 public string Name
17 {
18 get { return name; }
19 set { name = value; }
20 }
21 }

<city/>对应City类:

View Code
 1 public class City
2 {
3 private string id;
4 /// <summary>
5 /// 编号
6 /// </summary>
7 public string Id
8 {
9 get { return id; }
10 set { id = value; }
11 }
12 private string name;
13 /// <summary>
14 /// 名称
15 /// </summary>
16 public string Name
17 {
18 get { return name; }
19 set { name = value; }
20 }
21 }

<county/>对应county类:

View Code
 1 public class County
2 {
3 private string id;
4 /// <summary>
5 /// 编号
6 /// </summary>
7 public string Id
8 {
9 get { return id; }
10 set { id = value; }
11 }
12 private string name;
13 /// <summary>
14 /// 名称
15 /// </summary>
16 public string Name
17 {
18 get { return name; }
19 set { name = value; }
20 }
21 }

第三步:编写服务器端处理程序类:Handler.cs

View Code
  1 /// <summary>
2 /// 处理程序
3 /// </summary>
4 public class Handler : IHttpHandler
5 {
6
7 private static XDocument doc;
8 private string filePath = HttpContext.Current.Server.MapPath("~/Area.xml");
9 //javascript序列化类
10 private static JavaScriptSerializer jss = new JavaScriptSerializer();
11
12
13 public void ProcessRequest(HttpContext context)
14 {
15
16 context.Response.ContentType = "text/plain";
17 string result = "failure";//默认返回结果为失败
18 HttpRequest req = context.Request;
19
20 string province = req["province"];//获取用户选择的省的编号
21 string city = req["city"];//获取用户选择的市的编号
22 string county = req["county"];//获取用户选择的县的编号
23 string type = req["type"];//获取用户需要获取的省市县列表的类型
24
25 InitDoc();
26
27 if (type.HasValue())
28 {
29 switch (type.ToLower())
30 {
31 case "province"://如果用户需要获取省级列表
32 result = jss.Serialize(GetProvinceList());
33 break;
34 case "city"://如果用户需要获取的是市级列表
35 result = jss.Serialize(GetCityListByProvince(province));
36 break;
37 case "county"://如果用户需要获取的是县级列表
38 result = jss.Serialize(GetCountyListByCity(province, city));
39 break;
40 default:
41 break;
42 }
43 }
44 //将结果以文本的格式返回给客户端
45 context.Response.Write(result);
46 }
47 /// <summary>
48 /// 初始化文档对象
49 /// </summary>
50 private void InitDoc()
51 {
52 if (doc == null)
53 {
54 doc = XDocument.Load(filePath);
55 }
56 }
57 /// <summary>
58 /// 初始化省级列表
59 /// </summary>
60 private List<Province> GetProvinceList()
61 {
62 List<Province> list = new List<Province>();
63 if (doc != null)
64 {
65 XElement root = doc.Root;
66 foreach (var prov in root.XPathSelectElements("province"))
67 {
68 list.Add(new Province()
69 {
70 Id = prov.Attribute("id").Value,
71 Name = prov.Attribute("name").Value
72 });
73 }
74 }
75 return list;
76 }
77
78 /// <summary>
79 /// 根据省级编号获取市级编号
80 /// </summary>
81 /// <param name="provId">省级编号</param>
82 private List<City> GetCityListByProvince(string provId)
83 {
84 List<City> list = new List<City>();
85 if (doc != null)
86 {
87 XElement root = doc.Root;
88 //xpath表达式:/area/province[@id='1']/city
89 string queryPath = "/area/province[@id='" + provId + "']/city";
90 foreach (var city in root.XPathSelectElements(queryPath))
91 {
92 list.Add(new City()
93 {
94 Id = city.Attribute("id").Value,
95 Name = city.Attribute("name").Value
96 });
97 }
98 }
99 return list;
100 }
101 /// <summary>
102 /// 根据省级编号和市级编号获取县级编号
103 /// </summary>
104 /// <param name="provId">省级编号</param>
105 /// <param name="cityId">市级编号</param>
106 private List<County> GetCountyListByCity(string provId, string cityId)
107 {
108 List<County> list = new List<County>();
109 if (doc != null)
110 {
111 XElement root = doc.Root;
112 string queryPath = "/area/province[@id='" + provId + "']/city[@id='" + cityId + "']/county";
113 foreach (var county in root.XPathSelectElements(queryPath))
114 {
115 list.Add(new County()
116 {
117 Id = county.Attribute("id").Value,
118 Name = county.Attribute("name").Value
119 });
120 }
121 }
122 return list;
123 }
124
125 public bool IsReusable
126 {
127 get
128 {
129 return false;
130 }
131 }
132 }

     在这里,查询xml我采用的是System.Xml.XPath命名空间下的XPathSelectElements(string xpath)方法和XPathSelectElement(string xpath)方法,在根据省级编号获取市级编号的方法里面,我使用了xpath表达式(假设传入的省级编号为1):/area/province[@id='1']/city,这个表达式以“/”开头,表示使用绝对路径,因为area为根节点所以从area开始,接着它下面有province元素,当我想获取area下所有province元素中id属性值为1的province元素时,我可以使用/area/province[@id='1'],即在province后面加上[@id='1']这个条件,这时我就获取到了area下id属性为1的province元素了。接着我要获取该province元素下所有的city,那么只需在后面加上/city即可,所以最终的xpath表达式为:/area/province[@id='1']/city。

     还有,因为此查询的xml是在当前网站的根目录,如果是在其它地方,那么在查询的时候要加上namespace

     将从xml文件中读取到的值组装成对应的实体对象只后,我使用了System.Web.Script.Serialization命名空间下的JavaScriptSerializer类中的Serialize方法将得到的实体对象序列化成json数据返回给客户端。

第四步:编写html和js。

View Code
 1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <title>省市县三级联动下拉列表</title>
4 <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
5 <script type="text/javascript">
6 $(function () {
7 $.post("/Handler.ashx", { "type": "province" }, function (data, status) {
8 if (status == "success") {
9 if (data != "failure") {
10 data = $.parseJSON(data); //解析服务器返回的json数据
11 for (var i = 0; i < data.length; i++) {
12 var value = data[i].Id + ":" + data[i].Name; //设置option选项的值,格式为:"编号:名称"
13 $("#province").append("<option value='" + value + "'>" + data[i].Name + "</option>");
14 }
15 }
16 }
17 }, "text");
18 $("#province").change(function () {
19 var selectValue = $(this).val(); //获取选择的省级option的值
20 var provId = selectValue.split(':')[0]; //取出编号
21 var provTxt = selectValue.split(':')[1]; //取出名称
22 $("#txtProvince").html(provTxt); //显示选择的省的名称
23 $("#city").html("<option>==请选择市==</option>"); //当省级改变时将市级清空
24 $("#county").html("<option>==请选择县==</option>"); //当省级改变时将县级清空
25 $.post("/Handler.ashx", { "province": provId, "type": "city" }, function (data, status) {
26 if (status == "success") {
27 if (data != "failure") {
28 data = $.parseJSON(data);
29 for (var i = 0; i < data.length; i++) {
30 var value = data[i].Id + ":" + data[i].Name;
31 $("#city").append("<option value='" + value + "'>" + data[i].Name + "</option>");
32 }
33 }
34 }
35 }, "text");
36 });
37 $("#city").change(function () {
38 var provId = $("#province").val().split(':')[0];
39 var selectValue = $(this).val(); //同上
40 var cityId = selectValue.split(':')[0]; //同上
41 var cityTxt = selectValue.split(':')[1]; //同上
42 $("#txtCity").html(cityTxt); //显示选择的市的名称
43 $("#county").html("<option>==请选择县==</option>"); //同上
44 $.post("/Handler.ashx", { "province": provId, "city": cityId, "type": "county" }, function (data, status) {
45 if (status == "success") {
46 if (data != "failure") {
47 data = $.parseJSON(data);
48 for (var i = 0; i < data.length; i++) {
49 var value = data[i].Id + ":" + data[i].Name;
50 $("#county").append("<option value='" + value + "'>" + data[i].Name + "</option>");
51 }
52 }
53 }
54 }, "text");
55 });
56 $("#county").change(function () {
57 $("#txtCounty").html($(this).val().split(':')[1]); //显示选择的县的名称
58 });
59 });
60 </script>
61 </head>
62 <body>
63 <!---->
64 <select id="province" name="province">
65 </select>
66 <!---->
67 <select id="city" name="city">
68 </select>
69 <!---->
70 <select id="county" name="county">
71 </select>
72 <br />
73 <span id="txtProvince" style="color: #ff0000;"></span>- <span id="txtCity" style="color: #ff0000;"></span>- <span id="txtCounty" style="color: #ff0000;"></span>
74 </body>
75 </html>

关于使用jQuery与服务器通信,我使用的是$.post方法,该方法的具体使用可以参考jQuery官方文档,这里我想说的是,遍历后通过对象.属性访问时,这个属性的名字是区分大小写的,这个名字是服务器端定义的名字,因为服务器序列化的是服务器端的实体对象。

在这个例子中,关键点就是如何使用XPath表达式,如何调用System.Xml.XPath命名空间下的XPathSelectElements(string xpath)方法。

最终结果如下图:

作者: TwitterSu 发表于 2011-08-09 00:39 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"