最近忙于做一个小网站,是电子商务性质的。其中很多地方我用到了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操作类
1
using System;
2
using System.Xml;
3
using System.Data;
4
using System.Collections;
5
6
namespace 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