[.net 物件導向程式設計進階] (11) 序列化(Serialization)(三) 通過介面 IXmlSerializable 實現XML序列化 及 通用XML類
本節導讀:本節主要介紹通過序列化介面IXmlSerializable實現XML序列化和反序列化,整理了XML基礎操作及序列化的通用類(包括XML及節點的基礎讀寫操作,XML到DataSet\DataTable互轉換操作,XML序列化及反序列化通用方法等)。
讀前必備:
A.類和類的例項 [.net 物件導向程式設計基礎] (9) 類和類的例項
B.類的成員 [.net 物件導向程式設計基礎] (10) 類的成員(欄位、屬性、方法)
C.陣列與集合 [.net 物件導向程式設計基礎] (17) 陣列與集合
D.泛型 [.net 物件導向程式設計基礎] (18) 泛型
E.LINQ使用 [.net 物件導向程式設計基礎] (20) LINQ使用
1. 通過序列化介面IXmlSerializable實現XML序列化
名稱空間:System.Xml.Serialization
介面:IXmlSerializable
方法:
XmlSchema GetSchema();
void ReadXml(XmlReader reader);
void WriteXml(XmlWriter writer);
下面通過示例介紹關於序列化介面的使用,還是通過部落格園文章為例:
定義文章實體類及文章呼叫方法:
public class MyBlogs { /// <summary> /// 獲取我的部落格園中文章 /// </summary> /// <returns></returns> public static List<MyArticle> GetMyArticle(int count) { var document = XDocument.Load( "http://wcf.open.cnblogs.com/blog/u/yubinfeng/posts/1/" + count ); List<MyArticle> myArticleList = new List<MyArticle>(); var elements = document.Root.Elements(); //在進行這個工作之前,我們先獲取我部落格中的文章列表 var result = elements.Where(m => m.Name.LocalName == "entry").Select(myArticle => new MyArticle { id = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "id").Value), title = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "title").Value, published = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "published").Value), updated = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "updated").Value), diggs = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "diggs").Value), views = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "views").Value), comments = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "comments").Value), summary = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "summary").Value, link = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "link").Attribute("href").Value, author = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "author").Elements().SingleOrDefault(x => x.Name.LocalName == "name").Value }).OrderByDescending(m => m.published); myArticleList.AddRange(result); return myArticleList; } } /// <summary /// 我的部落格文章實體類 /// </summary> public class MyArticle { /// <summary> /// 文章編號 /// </summary> public int id { get; set; } /// <summary> /// 文章標題 /// </summary> public string title { get; set; } /// <summary> /// 文章摘要 /// </summary> public string summary { get; set; } /// <summary> /// 釋出時間 /// </summary> public DateTime published { get; set; } /// <summary> /// 最後更新時間 /// </summary> public DateTime updated { get; set; } /// <summary> /// URL地址 /// </summary> public string link { get; set; } /// <summary> /// 推薦數 /// </summary> public int diggs { get; set; } /// <summary> /// 瀏覽量 /// </summary> public int views { get; set; } /// <summary> /// 評論數 /// </summary> public int comments { get; set; } /// <summary> /// 作者 /// </summary> public string author { get; set; } }
XML通過介面序列化和反序列化的方法
/// <summary> /// 序列化XML 物件=》XML文字 /// 下面註釋項,可根據需要使用 /// </summary> /// <param name="Obj"></param> /// <returns></returns> public static string ObjectToXmlSerializer(Object Obj) { string XmlString = ""; XmlWriterSettings settings = new XmlWriterSettings(); //settings.OmitXmlDeclaration = true;//去除xml宣告 settings.Encoding = Encoding.Default;//使用預設編碼 //settings.IndentChars = "--"; //使用指定字元縮排 settings.Indent = true; //換行縮排 using (System.IO.MemoryStream mem = new MemoryStream()) { using (XmlWriter writer = XmlWriter.Create(mem, settings)) { //去除預設名稱空間xmlns:xsd和xmlns:xsi XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); XmlSerializer formatter = new XmlSerializer(Obj.GetType()); formatter.Serialize(writer, Obj, ns); } XmlString = Encoding.Default.GetString(mem.ToArray()); } return XmlString; } /// <summary> /// 反序列化 XML文字=》物件 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="str"></param> /// <returns></returns> public static T ObjectToXmlDESerializer<T>(string str) where T : class { object obj; using (System.IO.MemoryStream mem = new MemoryStream(Encoding.Default.GetBytes(str))) { using (XmlReader reader = XmlReader.Create(mem)) { XmlSerializer formatter = new XmlSerializer(typeof(T)); obj = formatter.Deserialize(reader); } } return obj as T; }
呼叫如下:
//名稱空間:System.Xml.Serialization //介面:IXmlSerializable //方法:XmlSchema GetSchema();void ReadXml(XmlReader reader);void WriteXml(XmlWriter writer); //(1)獲取我部落格園中最後10篇文章 (實際上這個過程也是反序列化) List<MyArticle> myArticleList = MyBlogs.GetMyArticle(10); //序列化並輸出XML文字 string xmlString = ObjectToXmlSerializer(myArticleList); Console.Write(xmlString); //序列化XML文字,遍歷輸出物件 List<MyArticle> myNewArticleList = ObjectToXmlDESerializer<List<MyArticle>>(xmlString); //遍歷輸出反序化的新物件myNewArticleList myNewArticleList.ForEach(m => Console.WriteLine("文章編號:" + m.id + "\n文章標題:" + m.title) );
執行結果如下:
2. XML類
我們在序列化XML的時候,經常要操作XML,下面我列出XML類的一些成員,供參考查閱
XML操作的名稱空間:System.Xml;
名稱 |
描述 |
XmlAttribute |
表示一個特性物件的集合,這些物件控制XmlSerializer如何序列化和反序列化物件 |
XmlArrayAttribute |
指定XmlSerializer應將特定的類成員序列化為XML元素陣列 |
XmlArrayItemAttribute |
指定XmlSerializer可以放置在序列化陣列中的派生型別 |
XmlArrayItemAttributes |
表示XmlArrayItemAttribute物件的集合 |
XmlAttributeAttribute |
指定XmlSerializer應將類成員作為XML特性序列化 |
XmlChoiceIdentifierAttribute |
指定可以通過使用列舉來進一步消除成員的歧義 |
XmlElementAttribute |
在XmlSerializer序列化或反序列化包含物件時,指示公共欄位或屬性表示XML元素 |
XmlElementAttributes |
表示XmlElementAttribute的集合,XmlSerializer將其用於它重寫序列化類的預設方式 |
XmlEnumAttribute |
控制XmlSerializer如何序列化列舉成員 |
XmlIgnoreAttribute |
指示XmlSerializer方法不序列化公共欄位或公共讀/寫屬性值 |
XmlIncludeAttribute |
允許XmlSerializer在它序列化或反序列化物件時識別型別 |
XmlRootAttribute |
控制視為XML根元素的屬性目標的XML序列化 |
XmlTextAttribute |
當序列化或反序列化時,想XmlSerializer指示應將此成員作為XML文字處理 |
XmlTypeAttribute |
控制當屬性目標由XmlSerializer序列化時生成的XML結構 |
XmlAnyAttributeAttribute |
指定成員(返回XmlAttribute物件的陣列的欄位)可以包含XML屬性 |
XmlAnyElementAttribute |
指定成員可以包含物件,該物件表示在序列化或反序列化的物件中沒有相應成員的所有XML元素 |
XmlAnyElementAttributes |
表示XmlAnyElementAttribute物件的集合 |
XmlAttributeEventArgs |
為UnKnowAttribute提供資料 |
XmlAttributeOverrides |
允許你在使用XmlSerializer序列化或反序列化時重寫屬性、欄位和類特性 |
XmlElementEventArgs |
為UnknownElement事件提供資料 |
XmlNamespaceDeclarationsAttribute |
指定目標屬性、引數、返回值或類成員包含與XML文件中所用名稱空間關聯的字首 |
XmlNodeEventArgs |
為UnknownNode時間提供資料 |
XmlSerializer |
將物件序列化到XML文件中和從XML文件中反序列化物件,XmlSerializer使你得以控制如何將物件編碼到XML中 |
XmlSerializerNamespaces |
包含XmlSerializer用於在XML文件例項中生成限定名的XML名稱空間和字首 |
XmlTypeMapping |
包含從一種型別到另一種型別的對映 |
更多更詳細的說明,請看官方API:http://msdn.microsoft.com/zh-cn/library/System.Xml.Serialization(v=vs.110).aspx
3. XML封裝類
最後整理一個XML的封裝類提供給小夥伴們,裡面包含了XML節點到DataSet,DataTable及XML序列化和反序列化,XML寫入檔案或讀取XML檔案及節點等。
檔名:XmlHelper.cs
程式碼如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.IO; 7 using System.Data; 8 using System.Xml; 9 using System.Xml.Serialization; 10 using System.Xml.Linq; 11 12 namespace Common 13 { 14 /// <summary> 15 /// Xml通用類 16 /// Author:YuBinfeng 17 /// Data:2015/07/10 18 /// </summary> 19 public class XmlHelper 20 { 21 22 #region XML的基礎讀寫 23 24 #region 將XML文字寫入到指定路徑的檔案 25 /// <summary> 26 /// 將XML文字寫入到指定路徑的檔案 27 /// </summary> 28 /// <param name="XMLString"></param> 29 /// <param name="filePath"></param> 30 /// <returns></returns> 31 public static bool WriteXmlString(string XMLString,string filePath) 32 { 33 try 34 { 35 File.WriteAllText(filePath, XMLString,Encoding.Default); 36 return true; 37 } 38 catch 39 { 40 return false; 41 } 42 } 43 #endregion 44 45 #region 從指定的路徑讀取XML文字 46 /// <summary> 47 /// 將XML文字寫入到指定路徑的檔案 48 /// </summary> 49 /// <param name="XMLString"></param> 50 /// <param name="filePath"></param> 51 /// <returns></returns> 52 public static string ReadXmlString( string filePath) 53 { 54 try 55 { 56 XmlDocument xmlDoc = new XmlDocument(); 57 xmlDoc.Load(filePath); 58 return xmlDoc.InnerXml; 59 } 60 catch (Exception ex) 61 { 62 return ""; 63 } 64 } 65 #endregion 66 67 #region 讀取XML資源中的指定節點內容 68 /// <summary> 69 /// 讀取XML資源中的指定節點內容 70 /// </summary> 71 /// <param name="source">XML資源</param> 72 /// <param name="xmlType">XML資源型別:檔案,字串</param> 73 /// <param name="nodeName">節點名稱</param> 74 /// <returns>節點內容</returns> 75 public static object GetNodeValue(string source, XmlType xmlType, string nodeName) 76 { 77 XmlDocument xd = new XmlDocument(); 78 if (xmlType == XmlType.File) 79 { 80 xd.Load(source); 81 } 82 else 83 { 84 xd.LoadXml(source); 85 } 86 XmlElement xe = xd.DocumentElement; 87 XmlNode xn = xe.SelectSingleNode("//" + nodeName); 88 if (xn != null) 89 { 90 return xn.InnerText; 91 } 92 else 93 { 94 return null; 95 } 96 } 97 98 /// <summary> 99 /// 讀取XML資源中的指定節點內容 100 /// </summary> 101 /// <param name="source">XML資源</param> 102 /// <param name="nodeName">節點名稱</param> 103 /// <returns>節點內容</returns> 104 public static object GetNodeValue(string source, string nodeName) 105 { 106 if (source == null || nodeName == null || source == "" || nodeName == "" || source.Length < nodeName.Length * 2) 107 { 108 return null; 109 } 110 else 111 { 112 int start = source.IndexOf("<" + nodeName + ">") + nodeName.Length + 2; 113 int end = source.IndexOf("</" + nodeName + ">"); 114 if (start == -1 || end == -1) 115 { 116 return null; 117 } 118 else if (start >= end) 119 { 120 return null; 121 } 122 else 123 { 124 return source.Substring(start, end - start); 125 } 126 } 127 } 128 #endregion 129 130 #region 更新XML檔案中的指定節點內容 131 /// <summary> 132 /// 更新XML檔案中的指定節點內容 133 /// </summary> 134 /// <param name="filePath">檔案路徑</param> 135 /// <param name="nodeName">節點名稱</param> 136 /// <param name="nodeValue">更新內容</param> 137 /// <returns>更新是否成功</returns> 138 public static bool UpdateNode(string filePath, string nodeName, string nodeValue) 139 { 140 bool flag = false; 141 142 XmlDocument xd = new XmlDocument(); 143 xd.Load(filePath); 144 XmlElement xe = xd.DocumentElement; 145 XmlNode xn = xe.SelectSingleNode("//" + nodeName); 146 if (xn != null) 147 { 148 xn.InnerText = nodeValue; 149 flag = true; 150 } 151 else 152 { 153 flag = false; 154 } 155 return flag; 156 } 157 #endregion 158 159 #region 操作xml檔案中指定節點的資料 160 /// <summary> 161 /// 獲得xml檔案中指定節點的節點資料 162 /// </summary> 163 /// <param name="TableName"></param> 164 /// <returns></returns> 165 public static string GetNodeInfoByNodeName(string path, string nodeName) 166 { 167 string XmlString = ""; 168 XmlDocument xml = new XmlDocument(); 169 xml.Load(path); 170 System.Xml.XmlElement root = xml.DocumentElement; 171 System.Xml.XmlNode node = root.SelectSingleNode("//" + nodeName); 172 if (node != null) 173 { 174 XmlString = node.InnerText; 175 } 176 return XmlString; 177 } 178 #endregion 179 #endregion 180 181 #region 共有屬性 182 /// <summary> 183 /// XML資源型別 184 /// </summary> 185 public enum XmlType 186 { 187 File, 188 String 189 }; 190 #endregion 191 192 #region XML與DataSet DataTable相關 193 194 #region 讀取XML資源到DataSet中 195 /// <summary> 196 /// 讀取XML資源到DataSet中 197 /// </summary> 198 /// <param name="source">XML資源,檔案為路徑,否則為XML字串</param> 199 /// <param name="xmlType">XML資源型別</param> 200 /// <returns>DataSet</returns> 201 public static DataSet GetDataSet(string source, XmlType xmlType) 202 { 203 DataSet ds = new DataSet(); 204 if (xmlType == XmlType.File) 205 { 206 ds.ReadXml(source); 207 } 208 else 209 { 210 XmlDocument xd = new XmlDocument(); 211 xd.LoadXml(source); 212 XmlNodeReader xnr = new XmlNodeReader(xd); 213 ds.ReadXml(xnr); 214 } 215 return ds; 216 } 217 218 #endregion 219 220 #region 獲取一個字串xml文件中的ds 221 /// <summary> 222 /// 獲取一個字串xml文件中的ds 223 /// </summary> 224 /// <param name="xml_string">含有xml資訊的字串</param> 225 public static void get_XmlValue_ds(string xml_string, ref DataSet ds) 226 { 227 System.Xml.XmlDocument xd = new XmlDocument(); 228 xd.LoadXml(xml_string); 229 XmlNodeReader xnr = new XmlNodeReader(xd); 230 ds.ReadXml(xnr); 231 xnr.Close(); 232 int a = ds.Tables.Count; 233 } 234 #endregion 235 236 #region 讀取XML資源到DataTable中 237 /// <summary> 238 /// 讀取XML資源到DataTable中 239 /// </summary> 240 /// <param name="source">XML資源,檔案為路徑,否則為XML字串</param> 241 /// <param name="xmlType">XML資源型別:檔案,字串</param> 242 /// <param name="tableName">表名稱</param> 243 /// <returns>DataTable</returns> 244 public static DataTable GetTable(string source, XmlType xmlType, string tableName) 245 { 246 DataSet ds = new DataSet(); 247 if (xmlType == XmlType.File) 248 { 249 ds.ReadXml(source); 250 } 251 else 252 { 253 XmlDocument xd = new XmlDocument(); 254 xd.LoadXml(source); 255 XmlNodeReader xnr = new XmlNodeReader(xd); 256 ds.ReadXml(xnr); 257 } 258 return ds.Tables[tableName]; 259 } 260 #endregion 261 262 #region 讀取XML資源中指定的DataTable的指定行指定列的值 263 /// <summary> 264 /// 讀取XML資源中指定的DataTable的指定行指定列的值 265 /// </summary> 266 /// <param name="source">XML資源</param> 267 /// <param name="xmlType">XML資源型別:檔案,字串</param> 268 /// <param name="tableName">表名</param> 269 /// <param name="rowIndex">行號</param> 270 /// <param name="colName">列名</param> 271 /// <returns>值,不存在時返回Null</returns> 272 public static object GetTableCell(string source, XmlType xmlType, string tableName, int rowIndex, string colName) 273 { 274 DataSet ds = new DataSet(); 275 if (xmlType == XmlType.File) 276 { 277 ds.ReadXml(source); 278 } 279 else 280 { 281 XmlDocument xd = new XmlDocument(); 282 xd.LoadXml(source); 283 XmlNodeReader xnr = new XmlNodeReader(xd); 284 ds.ReadXml(xnr); 285 } 286 return ds.Tables[tableName].Rows[rowIndex][colName]; 287 } 288 #endregion 289 290 #region 讀取XML資源中指定的DataTable的指定行指定列的值 291 /// <summary> 292 /// 讀取XML資源中指定的DataTable的指定行指定列的值 293 /// </summary> 294 /// <param name="source">XML資源</param> 295 /// <param name="xmlType">XML資源型別:檔案,字串</param> 296 /// <param name="tableName">表名</param> 297 /// <param name="rowIndex">行號</param> 298 /// <param name="colIndex">列號</param> 299 /// <returns>值,不存在時返回Null</returns> 300 public static object GetTableCell(string source, XmlType xmlType, string tableName, int rowIndex, int colIndex) 301 { 302 DataSet ds = new DataSet(); 303 if (xmlType == XmlType.File) 304 { 305 ds.ReadXml(source); 306 } 307 else 308 { 309 XmlDocument xd = new XmlDocument(); 310 xd.LoadXml(source); 311 XmlNodeReader xnr = new XmlNodeReader(xd); 312 ds.ReadXml(xnr); 313 } 314 return ds.Tables[tableName].Rows[rowIndex][colIndex]; 315 } 316 #endregion 317 318 #region 將DataTable寫入XML檔案中 319 /// <summary> 320 /// 將DataTable寫入XML檔案中 321 /// </summary> 322 /// <param name="dt">含有資料的DataTable</param> 323 /// <param name="filePath">檔案路徑</param> 324 public static void SaveTableToFile(DataTable dt, string filePath) 325 { 326 DataSet ds = new DataSet("Config"); 327 ds.Tables.Add(dt.Copy()); 328 ds.WriteXml(filePath); 329 } 330 #endregion 331 332 #region 將DataTable以指定的根結點名稱寫入檔案 333 /// <summary> 334 /// 將DataTable以指定的根結點名稱寫入檔案 335 /// </summary> 336 /// <param name="dt">含有資料的DataTable</param> 337 /// <param name="rootName">根結點名稱</param> 338 /// <param name="filePath">檔案路徑</param> 339 public static void SaveTableToFile(DataTable dt, string rootName, string filePath) 340 { 341 DataSet ds = new DataSet(rootName); 342 ds.Tables.Add(dt.Copy()); 343 ds.WriteXml(filePath); 344 } 345 #endregion 346 347 #region 使用DataSet方式更新XML檔案節點 348 /// <summary> 349 /// 使用DataSet方式更新XML檔案節點 350 /// </summary> 351 /// <param name="filePath">XML檔案路徑</param> 352 /// <param name="tableName">表名稱</param> 353 /// <param name="rowIndex">行號</param> 354 /// <param name="colName">列名</param> 355 /// <param name="content">更新值</param> 356 /// <returns>更新是否成功</returns> 357 public static bool UpdateTableCell(string filePath, string tableName, int rowIndex, string colName, string content) 358 { 359 bool flag = false; 360 DataSet ds = new DataSet(); 361 ds.ReadXml(filePath); 362 DataTable dt = ds.Tables[tableName]; 363 364 if (dt.Rows[rowIndex][colName] != null) 365 { 366 dt.Rows[rowIndex][colName] = content; 367 ds.WriteXml(filePath); 368 flag = true; 369 } 370 else 371 { 372 flag = false; 373 } 374 return flag; 375 } 376 #endregion 377 378 #region 使用DataSet方式更新XML檔案節點 379 /// <summary> 380 /// 使用DataSet方式更新XML檔案節點 381 /// </summary> 382 /// <param name="filePath">XML檔案路徑</param> 383 /// <param name="tableName">表名稱</param> 384 /// <param name="rowIndex">行號</param> 385 /// <param name="colIndex">列號</param> 386 /// <param name="content">更新值</param> 387 /// <returns>更新是否成功</returns> 388 public static bool UpdateTableCell(string filePath, string tableName, int rowIndex, int colIndex, string content) 389 { 390 bool flag = false; 391 392 DataSet ds = new DataSet(); 393 ds.ReadXml(filePath); 394 DataTable dt = ds.Tables[tableName]; 395 396 if (dt.Rows[rowIndex][colIndex] != null) 397 { 398 dt.Rows[rowIndex][colIndex] = content; 399 ds.WriteXml(filePath); 400 flag = true; 401 } 402 else 403 { 404 flag = false; 405 } 406 return flag; 407 } 408 #endregion 409 410 #endregion 411 412 #region XML序列化與反序列化相關 413 414 /// <summary> 415 /// 序列化XML 物件=》XML文字 416 /// 下面註釋項,可根據需要使用 417 /// </summary> 418 /// <param name="Obj"></param> 419 /// <returns></returns> 420 public static string ObjectToXmlSerializer(Object Obj) 421 { 422 string XmlString = ""; 423 XmlWriterSettings settings = new XmlWriterSettings(); 424 //settings.OmitXmlDeclaration = true;//去除xml宣告 425 settings.Encoding = Encoding.Default;//使用預設編碼 426 //settings.IndentChars = "--"; //使用指定字元縮排 427 settings.Indent = true; //換行縮排 428 429 using (System.IO.MemoryStream mem = new MemoryStream()) 430 { 431 using (XmlWriter writer = XmlWriter.Create(mem, settings)) 432 { 433 //去除預設名稱空間xmlns:xsd和xmlns:xsi 434 XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 435 ns.Add("", ""); 436 437 XmlSerializer formatter = new XmlSerializer(Obj.GetType()); 438 formatter.Serialize(writer, Obj, ns); 439 } 440 XmlString = Encoding.Default.GetString(mem.ToArray()); 441 } 442 return XmlString; 443 } 444 445 /// <summary> 446 /// 反序列化 XML文字=》物件 447 /// </summary> 448 /// <typeparam name="T"></typeparam> 449 /// <param name="str"></param> 450 /// <returns></returns> 451 public static T ObjectToXmlDESerializer<T>(string str) where T : class 452 { 453 object obj; 454 using (System.IO.MemoryStream mem = new MemoryStream(Encoding.Default.GetBytes(str))) 455 { 456 using (XmlReader reader = XmlReader.Create(mem)) 457 { 458 XmlSerializer formatter = new XmlSerializer(typeof(T)); 459 obj = formatter.Deserialize(reader); 460 } 461 } 462 return obj as T; 463 } 464 465 #endregion 466 467 } 468 }
4. 本節要點:
A.本節主要介紹了通過序列化介面IXmlSerializable實現XML序列化
B.XML本身的操作簡單介紹
C.最後提供了一個序列化及XML操作的通用類
==============================================================================================
<如果對你有幫助,記得點一下推薦哦,如有有不明白或錯誤之處,請多交流>
<對本系列文章閱讀有困難的朋友,請先看《.net 物件導向程式設計基礎》>
<轉載宣告:技術需要共享精神,歡迎轉載本部落格中的文章,但請註明版權及URL>
==============================================================================================