Xml序列化是将对象中的字段和值序列化为xml流,保存为Xml文件的格式。反序列化是将类从Xml流中读出。
主要用到的类为:XmlSerializer,它的主要方法有Serialize和Deserialize,用来序列化和反序列化Xml。 XmlSerializer构造函数中的参数Type type,用来表示要进行序列化的类的类型,可以通过typeof(类名)来获取。Serialize有两个参数Stream stream和Object o,分别表示序列化的流和要序列化的对象。Deserialize有一个参数Stream stream表示要反序列化的流。
可以用属性来控制Xml的序列化,XmlAttribute属性表示对象成员序列化为Xml属性,
XmlElement 属性表示要将成员序列化为Xml元素,XmlArray属性应用于数组成员,数组的成员将成为Xml数组的成员。XmlRoot属性表示要序列化为Xml 的根元素。在这些属性中都可以进一步制定元素的名称,是否为空等。XmlRoot属性中还可以制定命名空间。
[XmlArray(ElementName = "Items")]
[XmlElement(IsNullable = false)]
[XmlRoot(ElementName="PurchaseOrder",Namespace = “http://www.w3.org”)]
[XmlAttribute]
XML序列化这个东西挺搞人的,当你的Class里有Hashtable这样的东西的时候,在序列化的时候,在
XmlSerializer serialize = new XmlSerializer(typeof(myClass));
这一句上会出来一个“反射类型时出错”的错误。其实就是那个Hashtable在搞鬼,直接把上面一句换成
XmlSerializer serialize = new XmlSerializer(typeof(Hashtable));
罪魁祸首就出来了。这次是:“不支持类型 System.Collections.Hashtable,因为它实现 IDictionary”。原来XML序列化时对集合类还有一些比较特殊的要求。
那先来看一个可以正常序列化的集合类――Arraylist:
serialize = new XmlSerializer(typeof(ArrayList));
在序列化过程中临时生成的文件中可以发现如下的语句:
WriteStartElement(@"ArrayOfAnyType", @"");
for (int ia = 0; ia < a.Count; ia++) {
Write1_Object(@"anyType", @"", ((System.Object)a[ia]), true, false);
}
WriteEndElement();
对于集合类,在XML序列化过程中都是要如上所述来写XML的,显然的,对于实现了IDictionary接口的集合来讲,上面那个for循环是走不通的。因为对于IDictionary来讲Item属性(也就是C#里的[]这个东西,也叫索引器,名字反正有点乱啦)是这样定义的:
[C#]
object this[
object key
] {get; set;} object this[
object key
] {get; set;}
上面是从结果上看,猜想最终序列化的时候,集合类是要以上面那样的循环方式进行序列化的,而实现了IDictionary接口的类对于那样的方式是不灵的,所以在生成临时文件的时候就不让它生成了,反正生成的DLL也是无法运行的,直接一个异常出来了。
其实在SDK上可以查到,关于XML序列化有这么一段:
XmlSerializer可以以不同方式处理实现 IEnumerable 或 ICollection 的类(条件是这些类满足某些要求)。实现 IEnumerable 的类必须实现带单个参数的公共 Add 方法。Add 方法的参数必须与从 GetEnumerator 方法返回的 IEnumerator.Current 属性所返回的类型一致(多态)。除实现 IEnumerable 外还实现 ICollection 的类(如 CollectionBase)必须有一个取整数的公共 Item 索引属性(在 C# 中为索引器),并且它必须有一个整数类型的公共 Count 属性。传递给 Add 方法的参数必须与从 Item 属性返回的类型相同或与该类型的某个基的类型相同。对于实现 ICollection 的类,要序列化的值将从索引 Item 属性检索,而不是通过调用 GetEnumerator 来检索。另外请注意,除返回另一个集合类(实现 ICollection 的集合类)的公共字段之外,将不序列化公共字段和属性。
无法满足上述条件的都会抛出相应的异常,而实现了IDictionary接口的类显然是无法满足的,.net在实现的时候,一开始就先判断是不是实现了IDictionary接口,发现实现了这个接口的直接就出来了,下面什么Add方法、Item属性呀都不管了。
还有一点,就是
l 类(Class这一级)――包括集合类与非集合,
l 非集合类 需要序列化的属性里的访问方法(不用序列化的属性例外),
l 在集合类里,上面提到过的Add方法,Item属性、Count属性、Current属性的访问方法等,
如果有过SecurityAttribute声明的也是无法序列化的。不过现在写代码那个SecurityAttribute用得是甚少――这个方面的东西除了照例子依样画葫芦过一下在实践中根本是没有涉足。
要序列化Hashtable其实就只是少一个整数类型的Item属性而已,在这上面是没有什么办法了。想到SortedList这个东西很多方面跟Hashtable差不多,不过它还能依序取得集合中的元素,只是用的不是整数类型的Item属性,而是用GetByIndex()方法。所以就用它来偷梁换柱一下了。



































































































































Item是自定义的一个类。没什么具体的意义。
这样偷一下,上面的这个MyCollection类就是可以被序列化的了,然后把SortedList其他属性包一下,就基本可以当成一个SortedList使用了,说它是Hashtable也差不多吧――外表基本看不出来。
不过局限性还是有喽。它的Add方法的参数,与Item属性的类型必须是强类型的,不能用Objcet。用Object类型,临时文件是可以生成,serialize = new XmlSerializer(typeof(Myclass)); 这一句是可以通过没异常了。但真正序列化的时候,除非是一些基本的数据类型,否则它不知道如何去把那个类型写成相应的String,写XML文件就出错了。
附加一些冗于的资料:
1
/**//// <summary>
2
/// 序列化类。
3
/// </summary>
4
public class Serializer
5
{
6
//防止被实例化。
7
private Serializer()
8
{
9
10
}
11
/**//// <summary>
12
/// 静态构造函数仅在设置CanBinarySerialize值中使用一次。
13
/// </summary>
14
static Serializer()
15
{
16
SecurityPermission sp = new SecurityPermission(SecurityPermissionFlag.SerializationFormatter);
17
try
18
{
19
sp.Demand();
20
CanBinarySerialize = true;
21
}
22
catch (SecurityException)
23
{
24
CanBinarySerialize = false;
25
}
26
}
27
/**//// <summary>
28
/// 获取二进制序列化是否被使用。
29
/// </summary>
30
public static readonly bool CanBinarySerialize;
31
/**//// <summary>
32
/// 将对象转化成二进制的数组。
33
/// </summary>
34
/// <param name="objectToConvert">用于转化的对象。</param>
35
/// <returns>返回转化后的数组,如果CanBinarySerialize为false则返回null。</returns>
36
public static byte[] ConvertToBytes(object objectToConvert)
37
{
38
byte[] byteArray = null;
39
40
if (CanBinarySerialize)
41
{
42
BinaryFormatter binaryFormatter = new BinaryFormatter();
43
using (MemoryStream ms = new MemoryStream())
44
{
45
46
binaryFormatter.Serialize(ms, objectToConvert);
47
//设置是内存存储位置为0。
48
ms.Position = 0;
49
//读取数组。
50
byteArray = new Byte[ms.Length];
51
ms.Read(byteArray, 0, byteArray.Length);
52
ms.Close();
53
}
54
}
55
return byteArray;
56
}
57
/**//// <summary>
58
/// 将对象以二进制形式存储到硬盘中。
59
/// </summary>
60
/// <param name="objectToSave">用于保存的对象。</param>
61
/// <param name="path">文件路径。</param>
62
/// <returns>如果存储成功则返回true,否则返回false。</returns>
63
public static bool SaveAsBinary(object objectToSave, string path)
64
{
65
if (objectToSave != null && CanBinarySerialize)
66
{
67
byte[] ba = ConvertToBytes(objectToSave);
68
if (ba != null)
69
{
70
using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
71
{
72
using (BinaryWriter bw = new BinaryWriter(fs))
73
{
74
bw.Write(ba);
75
return true;
76
}
77
}
78
}
79
}
80
return false;
81
}
82
/**//// <summary>
83
///将对象转化为XML格式文件,该对象必须用[Serialize]标记,否则将抛出错误。
84
/// </summary>
85
/// <param name="objectToConvert">用于序列化的标记。</param>
86
/// <returns>返回XML文本,如果对象为空则返回null。</returns>
87
public static string ConvertToString(object objectToConvert)
88
{
89
string xml = null;
90
91
if (objectToConvert != null)
92
{
93
//获取当前序列化对象。
94
Type t = objectToConvert.GetType();
95
96
XmlSerializer ser = new XmlSerializer(t);
97
//将序列化的结果保存到XML中。
98
using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
99
{
100
ser.Serialize(writer, objectToConvert);
101
xml = writer.ToString();
102
writer.Close();
103
}
104
}
105
106
return xml;
107
}
108
/**//// <summary>
109
/// 将对象序列化后以XML格式存储于文件中。
110
/// </summary>
111
/// <param name="objectToConvert">用于序列化的对象。</param>
112
/// <param name="path">用于存储的文件路径。</param>
113
public static void SaveAsXML(object objectToConvert, string path)
114
{
115
if (objectToConvert != null)
116
{
117
Type t = objectToConvert.GetType();
118
119
XmlSerializer ser = new XmlSerializer(t);
120
121
using (StreamWriter writer = new StreamWriter(path))
122
{
123
ser.Serialize(writer, objectToConvert);
124
writer.Close();
125
}
126
}
127
128
}
129
/**//// <summary>
130
/// 将一个二进制的数组转化为对象,必须通过类型转化自己想得到的相应对象。如果数组为空则返回空。
131
/// </summary>
132
/// <param name="byteArray">用于转化的二进制数组。</param>
133
/// <returns>返回转化后的对象实例,如果数组为空,则返回空对象。</returns>
134
public static object ConvertToObject(byte[] byteArray)
135
{
136
object convertedObject = null;
137
if (CanBinarySerialize && byteArray != null && byteArray.Length > 0)
138
{
139
BinaryFormatter binaryFormatter = new BinaryFormatter();
140
using (MemoryStream ms = new MemoryStream())
141
{
142
ms.Write(byteArray, 0, byteArray.Length);
143
144
ms.Position = 0;
145
146
if (byteArray.Length > 4)
147
convertedObject = binaryFormatter.Deserialize(ms);
148
149
ms.Close();
150
}
151
}
152
return convertedObject;
153
}
154
/**//// <summary>
155
/// 将文件的数据转化为对象。
156
/// </summary>
157
/// <param name="path">文件路径。</param>
158
/// <param name="objectType">希望得到的对象。</param>
159
/// <returns>返回反序列化的对象。</returns>
160
public static object ConvertFileToObject(string path, Type objectType)
161
{
162
object convertedObject = null;
163
164
if (path != null && path.Length > 0)
165
{
166
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
167
{
168
XmlSerializer ser = new XmlSerializer(objectType);
169
convertedObject = ser.Deserialize(fs);
170
fs.Close();
171
}
172
}
173
return convertedObject;
174
}
175
/**//// <summary>
176
/// 将XML格式的字符串反序列化为实例对象。
177
/// </summary>
178
/// <param name="xml">Xml格式字符串。</param>
179
/// <param name="objectType">反序列化后所期望的对象。</param>
180
/// <returns>反序列化后所期望的对象,如果字符串为空,则返回空对象。</returns>
181
public static object ConvertToObject(string xml, Type objectType)
182
{
183
object convertedObject = null;
184
185
if (!WebHelper.IsNullOrEmpty(xml))
186
{
187
using (StringReader reader = new StringReader(xml))
188
{
189
XmlSerializer ser = new XmlSerializer(objectType);
190
convertedObject = ser.Deserialize(reader);
191
reader.Close();
192
}
193
}
194
return convertedObject;
195
}
196
/**//// <summary>
197
/// 将XML节点反序列化为实例对象。
198
/// </summary>
199
/// <param name="xml">用于反序列化的XML节点。</param>
200
/// <param name="objectType">反序列化后所期望的对象。</param>
201
/// <returns>反序列化后所期望的对象,如果字符串为空,则返回空对象。</returns>
202
public static object ConvertToObject(XmlNode node, Type objectType)
203
{
204
object convertedObject = null;
205
206
if (node != null)
207
{
208
using (StringReader reader = new StringReader(node.OuterXml))
209
{
210
211
XmlSerializer ser = new XmlSerializer(objectType);
212
213
convertedObject = ser.Deserialize(reader);
214
215
reader.Close();
216
}
217
}
218
return convertedObject;
219
}
220
/**//// <summary>
221
/// 加载一个二进制文件并将其转化为实例对象。
222
/// </summary>
223
/// <param name="path">文件路径。</param>
224
/// <returns>返回转化后的实例对象。</returns>
225
public static object LoadBinaryFile(string path)
226
{
227
if (!File.Exists(path))
228
return null;
229
230
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
231
{
232
BinaryReader br = new BinaryReader(fs);
233
byte[] ba = new byte[fs.Length];
234
br.Read(ba, 0, (int)fs.Length);
235
return ConvertToObject(ba);
236
}
237
}
238
/**//// <summary>
239
/// 将字符串序列化<see cref="System.Collections.Specialized.NameValueCollection"/>类型,第一个为键,第二位与其相对应的值。
240
/// </summary>
241
/// <param name="keys">NameValueCollection中的keys键。</param>
242
/// <param name="values">NameValueCollection中的keys键对应的value。</param>
243
/// <returns>基于key-value格式的NameValeCollection集合对象。</returns>
244
/// <example>
245
/// string keys = "key1:S:0:3:key2:S:3:2:";
246
/// string values = "12345";
247
/// 则这个所得的NameValueCollection结果中包含两个键(key1和key2),他们所对应的值分别为(123和45)。
248
/// 其中:"key1:S:0:3"的key1表示键,S表示字符类型,0表示values起始的位置,3表示values结束的位置。
249
/// </example>
250
public static NameValueCollection ConvertToNameValueCollection(string keys, string values)
251
{
252
NameValueCollection nvc = new NameValueCollection();
253
254
if (keys != null && values != null && keys.Length > 0 && values.Length > 0)
255
{
256
char[] splitter = new char[1]
{ ':' };
257
string[] keyNames = keys.Split(splitter);
258
259
for (int i = 0; i < (keyNames.Length / 4); i++)
260
{
261
int start = int.Parse(keyNames[(i * 4) + 2], CultureInfo.InvariantCulture);
262
int len = int.Parse(keyNames[(i * 4) + 3], CultureInfo.InvariantCulture);
263
string key = keyNames[i * 4];
264
265
//Future version will support more complex types
266
if (((keyNames[(i * 4) + 1] == "S") && (start >= 0)) && (len > 0) && (values.Length >= (start + len)))
267
{
268
nvc[key] = values.Substring(start, len);
269
}
270
}
271
}
272
273
return nvc;
274
}
275
/**//// <summary>
276
/// 基于NameValueCollection对象序列化成keys和values的字符串。
277
/// </summary>
278
/// <param name="nvc">用于序列化的NameValueCollection对象。</param>
279
/// <param name="keys">ref格式的参数keys将转化为NameValueCollection中的key。</param>
280
/// <param name="values">ref格式的参数values将转化为NameValueCollection中的value。</param>
281
public static void ConvertFromNameValueCollection(NameValueCollection nvc, ref string keys, ref string values)
282
{
283
if (nvc == null || nvc.Count == 0)
284
return;
285
286
StringBuilder sbKey = new StringBuilder();
287
StringBuilder sbValue = new StringBuilder();
288
289
int index = 0;
290
foreach (string key in nvc.AllKeys)
291
{
292
if (key.IndexOf(':') != -1)
293
throw new ArgumentException("ExtendedAttributes Key can not contain the character /":/"");
294
295
string v = nvc[key];
296
if (!WebHelper.IsNullOrEmpty(v))
297
{
298
sbKey.AppendFormat("{0}:S:{1}:{2}:", key, index, v.Length);
299
sbValue.Append(v);
300
index += v.Length;
301
}
302
}
303
keys = sbKey.ToString();
304
values = sbValue.ToString();
305
}
306
}
在SiteSettings.cs中可以发现有些属性加有[XmlIgnore]标记,这是在序列化和反序列化时对该属性都不起作用,如SettingsID等在数据库中都已经存在字段名。从代码中我们可以看到,将一个NameValueCollection集合序列化和反序列化,其实键和值对存储在一个结构中,这个结构就是:


2

3

4

5



6

7

8



9

10

11


12

13

14

15



16

17

18



19

20

21

22

23



24

25

26

27


28

29

30

31


32

33

34

35

36

37



38

39

40

41



42

43

44



45

46

47

48

49

50

51

52

53

54

55

56

57


58

59

60

61

62

63

64



65

66



67

68

69



70

71



72

73



74

75

76

77

78

79

80

81

82


83

84

85

86

87

88



89

90

91

92



93

94

95

96

97

98

99



100

101

102

103

104

105

106

107

108


109

110

111

112

113

114



115

116



117

118

119

120

121

122



123

124

125

126

127

128

129


130

131

132

133

134

135



136

137

138



139

140

141



142

143

144

145

146

147

148

149

150

151

152

153

154


155

156

157

158

159

160

161



162

163

164

165



166

167



168

169

170

171

172

173

174

175


176

177

178

179

180

181

182



183

184

185

186



187

188



189

190

191

192

193

194

195

196


197

198

199

200

201

202

203



204

205

206

207



208

209



210

211

212

213

214

215

216

217

218

219

220


221

222

223

224

225

226



227

228

229

230

231



232

233

234

235

236

237

238


239

240

241

242

243

244

245

246

247

248

249

250

251



252

253

254

255



256



257

258

259

260



261

262

263

264

265

266

267



268

269

270

271

272

273

274

275


276

277

278

279

280

281

282



283

284

285

286

287

288

289

290

291



292

293

294

295

296

297



298

299

300

301

302

303

304

305

306

在SiteSettings.cs中可以发现有些属性加有[XmlIgnore]标记,这是在序列化和反序列化时对该属性都不起作用,如SettingsID等在数据库中都已经存在字段名。从代码中我们可以看到,将一个NameValueCollection集合序列化和反序列化,其实键和值对存储在一个结构中,这个结构就是:
1
public struct SerializerData
2
{
3
/**//// <summary>
4
/// 序列化NameValueCollection集合时用于保存Keys的字符串。
5
/// </summary>
6
public string Keys;
7
/**//// <summary>
8
/// 序列化NameValueCollection集合时用于保存Values的字符串。
9
/// </summary>
10
public string Values;
11
}

2



3


4

5

6

7


8

9

10

11
