XmlAttribute與實體的轉換和匹配方案(附源碼)


一、前言

可擴展標記語言 (XML) 是具有結構性的標記語言,可以用來標記數據、定義數據類型,是一種允許用戶對自己的標記語言進行定義的源語言。XML是用來存儲數據的,重在數據本身。本文中的代碼是幾個月前整理的,最近幾個月的時間很少寫隨筆,除了工作以外,主要還是忙於整理自己的框架。這篇隨筆主要是針對於XML的特性Attribute與實體之間的匹配與轉換,其中的內容包括反射、XML以及LinqToXml,代碼的內容也是想到什么就寫什么,純屬練習下手感,僅供參考。下一篇隨筆將以另外的形式來轉換Xml為對象實體,當然,也是以反射為主,和本隨筆中的思路差不多,主要是XML的格式和解決方案不同而已。對於如何將對象轉換成Xml的話,主要還是看情況,僅僅轉換簡單的對象的話,直接反射就可以生成。對於復雜的對象的話,可以采用dynamic,樹結構和遞歸算法的方案來定制XML。

 

二、類圖設計

該處的主要思路為:通過反射將與類名(類特性名稱或者類名)的節點匹配,然后匹配屬性(屬性特性名稱或者屬性名稱)值。反之,則遍歷實體對象的屬性,設置相應的XML。本來還想細化一下匹配與轉換操作的,但是該類的EA文件不知道放在哪里了,只有設計的截圖還在,XO。相關類圖設計如下:

 

 

 PropertyAttribute主要設置屬性的特性名稱,用於轉換過程設置屬性的別名,同時匹配過程中匹配XML的特性與屬性的名稱。

 ClassAttribute主要設置類的特性名稱,用於轉換過程設置類的別名,同時匹配過程中匹配XML的節點與類的名稱。

 StringExtension主要是字符串的擴展方法。

 FuncDictionary主要轉換字符串為特定類型的值。

 XmlAttributeUtility主要轉換實體對象為XML以及匹配XML為實體對象,其中還包括一些其他基本操作。

 

 

 

三、具體實現

先看下FuncDictionary,該類主要通過異步委托將字符串轉換成相應的簡單類型,所有實現圍繞該類進行相關操作。FuncDictionary基本涵蓋了C#中的所有簡單類型,可以將字符串轉換成這些簡單類型。

主要方法為:public  object DynamicInvoke(Type type,  string arg),通過傳入的類型和字符串值,可以轉換成相應的Object值。屬性Dictionary中涵蓋了所有簡單類型轉換的委托操作。代碼如下:

 

 1      public  class FuncDictionary
 2     {
 3          public  static IDictionary<Type, Delegate> Dictionary
 4         {
 5              get;
 6              private  set;
 7         }
 8 
 9          static FuncDictionary()
10         {
11              if (FuncDictionary.Dictionary ==  null)
12             {
13                 FuncDictionary.Dictionary = CreateDictionary();
14             }
15         }
16 
17          public  object DynamicInvoke(Type type,  string arg)
18         {
19              if (type ==  null)
20             {
21                  return  null;
22             }
23 
24              if (FuncDictionary.Dictionary ==  null)
25             {
26                 FuncDictionary.Dictionary = CreateDictionary();
27             }
28 
29              if (!FuncDictionary.Dictionary.ContainsKey(type))
30             {
31                  return  null;
32             }
33 
34             Delegate action = FuncDictionary.Dictionary[type];
35 
36              return action.DynamicInvoke( new  object[] { arg });
37         }
38 
39          public  static IDictionary<Type, Delegate> CreateDictionary()
40         {
41              var dictionary =  new Dictionary<Type, Delegate>();
42 
43             dictionary.Add( typeof( string),  new Func< stringstring>(p => p));
44             dictionary.Add( typeof( decimal),  new Func< stringdecimal>(p => p.AsDecimal()));
45             dictionary.Add( typeof(DateTime),  new Func< string, DateTime>(p => p.AsDateTime()));
46             dictionary.Add( typeof( float),  new Func< stringfloat>(p => p.AsFloat()));
47             dictionary.Add( typeof( double),  new Func< stringdouble>(p => p.AsDouble()));
48             dictionary.Add( typeof( int),  new Func< stringint>(p => p.AsInt()));
49             dictionary.Add( typeof( byte),  new Func< stringbyte>(p => p.AsByte()));
50             dictionary.Add( typeof( sbyte),  new Func< stringsbyte>(p => p.AsSbyte()));
51             dictionary.Add( typeof( short),  new Func< stringshort>(p => p.AsShort()));
52             dictionary.Add( typeof( ushort),  new Func< stringushort>(p => p.AsUshort()));
53             dictionary.Add( typeof( uint),  new Func< stringuint>(p => p.AsUint()));
54             dictionary.Add( typeof( long),  new Func< stringlong>(p => p.AsLong()));
55             dictionary.Add( typeof( ulong),  new Func< stringulong>(p => p.AsUlong()));
56             dictionary.Add( typeof( char),  new Func< stringchar>(p => p.AsChar()));
57             dictionary.Add( typeof( bool),  new Func< stringbool>(p => p.AsBool()));
58             dictionary.Add( typeof(Color),  new Func< string, Color>(p => p.AsColor()));
59 
60              return dictionary;
61         }
62     }

 

 再看下XmlAttributeUtility類,該類主要包括轉換和匹配操作。匹配主要為兩種方案(主要邏輯為以下代碼的72-183行):

1、通過XmlReader順序讀取來設置實體的值,主要方法為public static IList<T> Parse<T>(XmlReader reader) where T : new():

2、通過遍歷XmlNodeList中的節點,依次遍歷節點中的XmlAttribute設置實體的屬性的值,主要方法為:public static IList<T> Parse<T>(XmlNodeList nodeList) where T : new()

  1          public  static IList<T> Parse<T>( string inputUri,  string parentXPath)  where T :  new()
  2         {
  3              if (!File.Exists(inputUri) ||  string.IsNullOrWhiteSpace(parentXPath))
  4             {
  5                  return  new List<T>();
  6             }
  7 
  8             XmlDocument document =  new XmlDocument();
  9             document.Load(inputUri);
 10 
 11              return Parse<T>(document, parentXPath);
 12         }
 13 
 14          public  static IList<T> Parse<T>(XmlDocument document,  string parentXPath)  where T :  new()
 15         {
 16              if (document ==  null ||  string.IsNullOrWhiteSpace(parentXPath))
 17             {
 18                  return  new List<T>();
 19             }
 20 
 21             XmlNode parentNode = document.DocumentElement.SelectSingleNode(parentXPath);
 22 
 23              if (parentNode ==  null)
 24             {
 25                  return  new List<T>();
 26             }
 27 
 28              return Parse<T>(parentNode);
 29         }
 30 
 31          public  static IList<T> Parse<T>(XmlNode parentNode)  where T :  new()
 32         {
 33              if (parentNode ==  null || !parentNode.HasChildNodes)
 34             {
 35                  return  new List<T>();
 36             }
 37 
 38             XmlNodeList nodeList = parentNode.ChildNodes;
 39 
 40              return Parse<T>(nodeList);
 41         }
 42 
 43          public  static IList<T> Parse<T>(XmlNodeList nodeList)  where T :  new()
 44         {
 45              if (nodeList ==  null || nodeList.Count ==  0)
 46             {
 47                  return  new List<T>();
 48             }
 49 
 50             List<T> entities =  new List<T>();
 51             AddEntities<T>(nodeList, entities);
 52 
 53              return entities;
 54         }
 55 
 56          public  static IList<T> Parse<T>( string inputUri)  where T :  new()
 57         {
 58              if (!File.Exists(inputUri))
 59             {
 60                  return  new List<T>();
 61             }
 62 
 63             XmlReaderSettings settings =  new XmlReaderSettings();
 64             settings.IgnoreComments =  true;
 65             settings.IgnoreWhitespace =  true;
 66 
 67             XmlReader reader = XmlReader.Create(inputUri, settings);
 68 
 69              return Parse<T>(reader);
 70         }
 71 
 72          public  static IList<T> Parse<T>(XmlReader reader)  where T :  new()
 73         {
 74              if (reader ==  null)
 75             {
 76                  return  new List<T>();
 77             }
 78 
 79             reader.ReadStartElement();
 80              string className = GetClassName<T>();     
 81             List<PropertyInfo> properties =  typeof(T).GetProperties().ToList();
 82             List<T> entities =  new List<T>();
 83             T entity =  new T();
 84 
 85              while (!reader.EOF)
 86             {
 87                  if (! string.Equals(reader.Name, className) || !reader.IsStartElement())
 88                 {
 89                     reader.Read();
 90                      continue;
 91                 }
 92 
 93                 entity =  new T();
 94 
 95                  if (!reader.HasAttributes)
 96                 {
 97                     entities.Add(entity);
 98                     reader.Read();
 99                      continue;
100                 }
101 
102                 SetPropertyValue<T>(reader, properties, entity);
103                 entities.Add(entity);
104                 reader.Read();
105             }
106 
107             reader.Close();
108 
109              return entities;
110         }
152 
153          private  static  void AddEntities<T>(XmlNodeList nodeList,
154             List<T> entities)  where T :  new()
155         {
156              string className = GetClassName<T>();
157             List<PropertyInfo> properties =  typeof(T).GetProperties().ToList();
158             T entity =  new T();
159 
160              foreach (XmlNode xmlNode  in nodeList)
161             {
162                 XmlElement element = xmlNode  as XmlElement;
163                  if (element ==  null || ! string.Equals(className, element.Name))
164                 {
165                      continue;
166                 }
167 
168                 entity =  new T();
169                  if (!element.HasAttributes)
170                 {
171                     entities.Add(entity);
172                      continue;
173                 }
174 
175                 XmlAttributeCollection attributes = element.Attributes;
176                  foreach (XmlAttribute attribute  in attributes)
177                 {
178                     SetPropertyValue<T>(properties, entity, attribute.Name, attribute.Value);
179                 }
180 
181                 entities.Add(entity);
182             }
183         }
184 
185          private  static  void SetPropertyValue<T>(XmlReader reader, 
186             List<PropertyInfo> properties, T entity)  where T :  new()
187         {
188              while (reader.MoveToNextAttribute())
189             {
190                 SetPropertyValue<T>(properties, entity, reader.Name, reader.Value);
191             }
192         }
193 
194          private  static  void SetPropertyValue<T>(List<PropertyInfo> properties,
195             T entity,  string name,  string value)  where T :  new()
196         {
197              foreach ( var property  in properties)
198             {
199                  if (!property.CanWrite)
200                 {
201                      continue;
202                 }
203 
204                  string propertyName = GetPropertyName(property);
205 
206                  if ( string.Equals(name, propertyName))
207                 {
208                     FuncDictionary action =  new FuncDictionary();
209                      object invokeResult = action.DynamicInvoke(property.PropertyType, value);
210 
211                     property.SetValue(entity, invokeResult,  null);
212                 }
213             }
214         }

 

XmlAttributeUtility的轉換操作相對來說比較簡單,采用反射+LinqToXml轉換操作就很簡單了,主要實現方法為 public  static XElement Convert<T>(T entity)  where T :  new(),其他方法都是以該方法作為基礎來進行操作。為什么用LinqToXml?主要原因是LinqToXml比Xml更方便,很多細節方面不需要考慮太多。代碼如下:

 

 1          public  static  string ConvertToString<T>(List<T> entities)  where T :  new()
 2         {
 3             List<XElement> elements = Convert<T>(entities);
 4              if (elements ==  null || elements.Count ==  0)
 5             {
 6                  return  string.Empty;
 7             }
 8 
 9             StringBuilder builder =  new StringBuilder();
10             elements.ForEach(p => builder.AppendLine(p.ToString()));
11 
12              return builder.ToString();
13         }
14 
15          public  static List<XElement> Convert<T>(List<T> entities)  where T :  new()
16         {
17              if (entities ==  null || entities.Count ==  0)
18             {
19                  return  new List<XElement>();
20             }
21 
22             List<XElement> elements =  new List<XElement>();
23             XElement element;
24 
25              foreach ( var entity  in entities)
26             {
27                 element = Convert<T>(entity);
28                  if (element ==  null)
29                 {
30                      continue;
31                 }
32 
33                 elements.Add(element);
34             }
35 
36              return elements;
37         }
38 
39          public  static  string ConvertToString<T>(T entity)  where T :  new()
40         {
41             XElement element = Convert<T>(entity);
42              return element ==  null ?  string.Empty : element.ToString();
43         }
44 
45          public  static XElement Convert<T>(T entity)  where T :  new()
46         {
47              if (entity ==  null)
48             {
49                  return  null;
50             }
51 
52              string className = GetClassName<T>();
53             XElement element =  new XElement(className);
54 
55             List<PropertyInfo> properties =  typeof(T).GetProperties().ToList();
56              string propertyName =  string.Empty;
57              object propertyValue =  null;
58 
59              foreach (PropertyInfo property  in properties)
60             {
61                  if (property.CanRead)
62                 {
63                     propertyName = GetPropertyName(property);
64                     propertyValue = property.GetValue(entity,  null);
65                      if (property.PropertyType.Name ==  " Color ")
66                     {
67                         propertyValue = ColorTranslator.ToHtml((Color)propertyValue);
68                     }
69                     element.SetAttributeValue(propertyName, propertyValue);
70                 }
71             }
72 
73              return element;
74         }

 

該類中還包括其他的一些操作,此處不再概述,詳細參見源碼。

 

四、單元測試

 FuncDictionary的單元測試必須涵蓋所有類型的測試才能將代碼覆蓋率達到100%,此處只針對DateTime做正常測試、異常測試和空值測試(當然,對於其他類型的方法,可能還需要做腳本測試,SQL注入測試等,這三種類型的測試是最基本的測試),主要測試代碼如下:

 1         [TestMethod()]
 2          public  void DynamicInvokeTest()
 3         {
 4             FuncDictionary target =  new FuncDictionary();
 5             Type type =  typeof(DateTime);
 6              string arg =  new DateTime( 201195181818).ToString();
 7 
 8             DateTime result = (DateTime)target.DynamicInvoke(type, arg);
 9             Assert.AreEqual(result.Year,  2011);
10             Assert.AreEqual(result.Month,  9);
11             Assert.AreEqual(result.Day,  5);
12             Assert.AreEqual(result.Hour,  18);
13             Assert.AreEqual(result.Minute,  18);
14             Assert.AreEqual(result.Second,  18);
15         }
16 
17         [TestMethod()]
18          public  void DynamicInvokeWithNullOrEmptyArgsTest()
19         {
20             FuncDictionary target =  new FuncDictionary();
21 
22              object result = target.DynamicInvoke( typeof(DateTime),  null);
23             Assert.AreEqual((DateTime)result,DateTime.MinValue);
24 
25             result = target.DynamicInvoke( typeof(DateTime),  string.Empty);
26             Assert.AreEqual((DateTime)result, DateTime.MinValue);
27 
28             result = target.DynamicInvoke( nullstring.Empty);
29             Assert.AreEqual(result,  null);
30         }
31 
32         [TestMethod()]
33          public  void DynamicInvokeWithInvalidArgsTest()
34         {
35             FuncDictionary target =  new FuncDictionary();
36 
37              object result = target.DynamicInvoke( typeof(DateTime),  " w007 ");
38             Assert.AreEqual((DateTime)result, DateTime.MinValue);
39         }

 

其他代碼的單元測試詳細見源代碼,也僅僅只做了些基本的測試,寫測試比寫代碼費哥的時間,

五、總結

以上的代碼僅僅是當時想着怎么實現就怎么寫的,完全是隨意而寫。僅供參考,實戰沒有多大意義,純粹練習下靈感和手感,增強對技術的敏感性而已,純屬娛樂。對於

<Lexer LexerName="Name0" FontColor="#EE2BA9" CreatedTime="2011-10-25T21:16:18.7866084+08:00" Count="0" Exist="true" LineCommentPrefix="LineCommentPrefix0" StreamCommentPrefix="StreamCommentPrefix0" StreamCommentSuffix="StreamCommentSuffix0" />此類格式的Xml轉換和匹配,以上的代碼完全能夠滿足該需求。下一篇將講述另外一種格式的匹配,不過也是通過反射和XmlReader來進行匹配的。
今天也是2011年最后一天,明天就是2012年了,在此恭祝大家2012元旦快樂,新一年,日子順心多平安;新一年,祝福多多又暖暖;新一年,願望件件都圓滿;新一年,幸福快樂早實現;新一年,元旦祝福圍身邊;瑪雅終結之年到了,所以,為了拯救人類,一定要快樂哦!

 

源碼下載:XmlAttribute轉換和匹配源代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM