最近忙于做一个小网站,是电子商务性质的。其中很多地方我用到了XML文件作为数据存储的方式,目的就是减轻数据库负担。
经过一两个月,在XML数据操作这块也有了一定经验,现在把它写出来共享给大家,同时也欢迎大家参与讨论。
(注:本文虽然质量不高,但转载望注明一下出处,谢谢合作!)
1。如果数据库表的记录是分组的,总的操作频繁但每组的并不频繁,且数据对保密性要求不高。那可以用XML。
如论坛的回帖,因为如果用数据库,好多回帖都添加到一张表里,数据库并发的可能性就大了,但换成XML,每个帖子专门建立一个文件夹,回帖里的内容均用XML文件来保存,这样的话,并发性就得到了很好的控制。
2。数据库量大的,如果用XML保存,在存取上会比较慢。所以如果能分成若干个XML文件保存就比较好。
如回帖,每50个回帖建立一个新的XML文件,而且名字跟页数对应,这样也能简化翻页。更大地减少了并发操作。
3。能用XSD描述数据结构的,就尽量描述。(开始是写的XSLT,后来更正)
这样对查询数据(当使用DataSet查询时)时,尤其是在数据量大的时候,能获得相当大的速度提升(它跳过了DataSet的自动分析XML结构的步骤)。
4。查询XML数据,尽量使用XML空间下提供的类。
这样能对XML进行快速地高效率地读写操作。 (稍后我会把自己写的XML操作类帖出来,供大家参考)
5。使用XML时,要注意结点的值。
有的值是需要过滤掉的,因为查询XML数据要用到XPath,其语法中的关键字是不能出现在查询关键字里的。
6。能用结点,就不要使用属性。尽量构架规范的XML结构。
比如说相册:
<root >
<相册 >
<名称/ >
<创建时间 / >
<相片 >
<图 >
<图名/ >
<地址/ >
<上传时间/ >
</图 >
</相片 >
</相册 >
</root >
在这个结构中,每个图要这样放在相片结点下,而不要直接放在root结点下。这样有助于XSD(原为XSLT,后更正)结构的清晰。程序读取数据也方便。
另,如果属性使用太多,对XPath查询会有影响,一些查询就可能不容易实现。
其实使用XML作为数据存储方式还有好多优点,比如RSS订阅,客户端解析成HTML页面等。
以上仅是个人的一点经验之谈,不足不馁不对之处,望各高手不吝赐教。
后来加的:
这里我看重的是数据的分别保存分别处理的思想。利用XML文件的分开存放使无相关的数据不再像数据库的表那样会互相影响。
大家以考虑下,如果回帖用表来保存,这样,不管你回复哪个帖子,都要在这张表中插入一条新记录,此时的并发显然是比较高。高的原因是,不相关的回帖,即回复不同的帖子,在这种情况下也相互影响、限制了。
如果回帖换成使用XML保存,每个帖子的回复只使用一个XML(当然为了分页方便,可以每N个回复建立一个文件,这样也省了分页查询了)。此时的并发,就被分解了。分解到回复一个帖子的情况。并且,在写XML文件时候,加上写保护,独占此资源,写完释放(而读取回帖不受影响)。这样,除非瞬间有许多人在回复同一帖子,才会形成比较大的并发操作(此时还能产生这样的并发量的网站实在是够大的了)。
网站中的个人消息,操作记录等,我觉得,均比较适合用XML作数据源。而且是很理想的。
下面是我在项目中自己写的XML操作类。
XML操作类
1using System;
2using System.Xml;
3using System.Data;
4using System.Collections;
5
6namespace lib.xml
7{
8 public class XmlControl
9 {
10
11 === 保护成员 ===#region === 保护成员 ===
12
13 protected string _strXmlFile; // XML文件的路径
14 protected XmlDocument _objXmlDoc = new XmlDocument(); // XML文件
15
16 #endregion
17
18
19 === 构造函数 ===#region === 构造函数 ===
20
21 /**//// <summary>
22 ///
23 /// </summary>
24 /// <param name="xmlFilePath">文件路径(服务器的绝对路径)</param>
25 public XmlControl(string xmlFilePath)
26 {
27 try
28 {
29 _objXmlDoc.Load(xmlFilePath);
30 }
31 catch (System.Exception ex)
32 {
33 throw ex;
34 }
35 _strXmlFile = xmlFilePath;
36 }
37
38 #endregion
39
40
41 === 查看是否存在 ===#region === 查看是否存在 ===
42 public bool IsExist(string xpath)
43 {
44 XmlNode xn = this._objXmlDoc.SelectSingleNode(xpath);
45 return xn == null? false: true;
46 }
47 #endregion
48
49
50 === 统计结点数目 ===#region === 统计结点数目 ===
51 /**//// <summary>
52 /// 获取结果个数
53 /// </summary>
54 public int GetCount(string xpath)
55 {
56 XmlNodeList xnl = _objXmlDoc.SelectNodes(xpath);
57 return xnl == null ? 0 : xnl.Count;
58 }
59 #endregion
60
61
62 === 查询数据 ===#region === 查询数据 ===
63
64 -- 根据Xpath查询 --#region -- 根据Xpath查询 --
65 /**//// <summary>
66 /// 查找对应结点的数据
67 /// </summary>
68 /// <param name="xPaht">结点选择</param>
69 public DataView GetData(string xPath)
70 {
71 DataSet ds = GetDataSet(xPath);
72// XmlReader read = new XmlNodeReader(_objXmlDoc.SelectSingleNode(xPath));
73// ds.ReadXml(read);
74 return ds.Tables.Count == 0 ? null : ds.Tables[0].DefaultView; // 防止空结果
75 }
76
77 /**//// <summary>
78 /// 获取某个结点下面的所有子元素对应的hashtable
79 /// </summary>
80 /// <param name="xpath"></param>
81 /// <returns></returns>
82 public Hashtable GetElemet(string xpath)
83 {
84 Hashtable hs = new Hashtable();
85 XmlNode xn = _objXmlDoc.SelectSingleNode(xpath);
86 if(xn != null)
87 {
88 foreach(XmlNode x in xn.ChildNodes)
89 {
90 hs.Add(x.Name, x.InnerText);
91 }
92 }
93
94 return hs;
95 }
96
97 /**//// <summary>
98 /// 获取某个元素的列表
99 /// </summary>
100 /// <param name="xpath"></param>
101 /// <returns></returns>
102 public IList GetElemetList(string xpath)
103 {
104 IList list = new ArrayList();
105 XmlNodeList xnl = _objXmlDoc.SelectNodes(xpath);
106 if(xnl != null)
107 {
108 foreach(XmlNode x in xnl)
109 {
110 list.Add(x.InnerText);
111 }
112 }
113 return list;
114 }
115
116 /**//// <summary>
117 /// 获取单个结点里面内容
118 /// </summary>
119 /// <param name="xpath"></param>
120 /// <returns>无则返回null</returns>
121 public string GetSingleNode(string xpath)
122 {
123 XmlNode xn = _objXmlDoc.SelectSingleNode(xpath);
124 if(xn != null)
125 {
126 return xn.InnerText;
127 }
128 return null;
129 }
130 #endregion
131
132 -- 获取对应DataSet --#region -- 获取对应DataSet --
133 // 只能获取列相等的记录
134 private DataSet GetDataSet(string xPath)
135 {
136 XmlNodeList xnl = _objXmlDoc.SelectNodes(xPath);
137// XmlTextReader reader = new XmlTextReader(
138 XmlDocument xd = new XmlDocument();
139 XmlNode rootNode = xd.CreateNode(XmlNodeType.Element, "root", null);
140
141 DataSet ds = new DataSet();
142 for(int i=0; i<xnl.Count; i++)
143 {
144// read = new XmlNodeReader(xnl.Item(i));
145 rootNode.AppendChild(xd.ImportNode(xnl.Item(i), true));
146// if(i == 0) // 初始化DS
147// {
148// ds.ReadXml(read);
149// }
150// else // 为DS的表添加行
151// {
152// DataSet dss = new DataSet();
153// dss.ReadXml(read);
154// ds.Tables[0].Rows.Add(dss.Tables[0].Rows[0].ItemArray);
155// }
156 }
157 XmlNodeReader reader = new XmlNodeReader(rootNode);
158 ds.ReadXml(reader);
159 return ds;
160 }
161 #endregion
162
163 #endregion
164
165
166 === 更新结点/元素 ===#region === 更新结点/元素 ===
167
168 /**//// <summary>
169 /// 更新一元素
170 /// </summary>
171 /// <param name="elementPath">结点地址</param>
172 /// <param name="content">值</param>
173 public bool UpdateElement(string elementPath, string content)
174 {
175 XmlNode xn = _objXmlDoc.SelectSingleNode(elementPath);
176 if(xn == null)
177 return false;
178 xn.InnerText = content;
179 return true;
180 }
181
182 /**//// <summary>
183 /// 更新某一结点下的指定元素值
184 /// </summary>
185 /// <param name="nodePath">目标结点</param>
186 /// <param name="elements">元素名称</param>
187 /// <param name="cotents">元素新值</param>
188 public bool UpdateElements(string nodePath, string[] elements, string[] contents)
189 {
190 XmlNode objNode = _objXmlDoc.SelectSingleNode(nodePath); // 找到目标结点
191
192 if(objNode == null)
193 return false;
194
195 // 更新目标结点的元素
196 for(int i = 0; i < elements.Length; i++)
197 {
198 objNode.SelectSingleNode(elements[i]).InnerText = contents[i];
199 }
200
201 return true;
202 }
203
204 #endregion
205
206
207 === 删除结点/元素 ===#region === 删除结点/元素 ===
208
209 /**//// <summary>
210 /// 删除一结点(包括子结点)
211 /// </summary>
212 /// <param name="nodePath">目标结点</param>
213 public bool DeleteNode(string nodePath)
214 {
215 try
216 {
217 XmlNode aimNode = _objXmlDoc.SelectSingleNode(nodePath); // 目标结点
218 if(aimNode != null)
219 {
220 aimNode.ParentNode.RemoveChild(aimNode); // 删除
221 }
222 return true;
223 }
224 catch
225 {
226 return false;
227 }
228 }
229
230 /**//// <summary>
231 /// 删除符合条件的所有结点
232 /// </summary>
233 /// <param name="xPath">条件</param>
234 public bool DeleteNodes(string xPath)
235 {
236 XmlNodeList listNode = _objXmlDoc.SelectNodes(xPath);
237 if(listNode == null)
238 return true;
239 try
240 {
241 foreach(XmlNode aimNode in listNode)
242 {
243 aimNode.ParentNode.RemoveChild(aimNode);
244 }
245 return true;
246 }
247 catch
248 {
249 return false;
250 }
251 }
252
253 #endregion
254
255
256 === 插入结点/元素 ===#region === 插入结点/元素 ===
257
258 /**//// <summary>
259 /// 插入一节点和其所有元素
260 /// </summary>
261 /// <param name="MainNode">父节点,目标位置</param>
262 /// <param name="ChildNodes">新的结点</param>
263 /// <param name="Element">新的元素名称</param>
264 /// <param name="Content">元素内容</param>
265 public bool InsertNodeWithElements(string MainNode, string ChildNode, string[] Elements, string[] Contents)
266 {
267 return InsertNodeWithElements(MainNode, ChildNode, Elements, Contents, true);
268 }
269
270 public bool InsertNodeWithElements(string MainNode, string ChildNode, string[] Elements, string[] Contents, bool isLast)
271 {
272 XmlNode objRootNode = _objXmlDoc.SelectSingleNode(MainNode); // 找到父节点,即目标位置
273
274 if(objRootNode != null)
275 {
276 XmlElement objChildNode = _objXmlDoc.CreateElement(ChildNode); // 新建结点
277 if(isLast)
278 {
279 objRootNode.AppendChild(objChildNode); // 添加到父结点末尾
280 }
281 else
282 {
283 objRootNode.PrependChild(objChildNode); // 添加到父结点开头
284 }
285
286 // 添加所有元素及内容到新加结点中
287 for(int i=0; i<Elements.Length; i++)
288 {
289 XmlElement objElement = _objXmlDoc.CreateElement(Elements[i]);
290 objElement.InnerText = Contents[i];
291 objChildNode.AppendChild(objElement);
292 }
293 return true;
294 }
295 return false;
296 }
297
298
299 /**//// <summary>
300 /// 在一位置插入多个元素
301 /// </summary>
302 /// <param name="MainNode">目标位置</param>
303 /// <param name="Elements">元素名</param>
304 /// <param name="Contents">内容</param>
305 public bool InsertElements(string MainNode, string[] Elements, string[] Contents)
306 {
307 XmlNode objNode = _objXmlDoc.SelectSingleNode(MainNode); // 找到目标位置
308
309 if(objNode != null)
310 {
311 // 添加所有元素和对应内容
312 for(int i = 0; i<Elements.Length; i++)
313 {
314 XmlElement objElement = _objXmlDoc.CreateElement(Elements[i]);
315 objElement.InnerText = Contents[i];
316 objNode.AppendChild(objElement);
317 }
318 return true;
319 }
320 return false;
321 }
322#endregion
323
324 === 保存 ===#region === 保存 ===
325
326 public bool Save()
327 {
328 bool re = false;
329 try
330 {
331 _objXmlDoc.Save(_strXmlFile);
332 re = true;
333 }
334 catch (System.Exception ex)
335 {
336 re = false;
337 throw ex;
338 }
339 finally
340 {
341 _objXmlDoc = null;
342 }
343 return re;
344 }
345
346 #endregion
347
348 }
349
350}
351