上一篇隨筆主要將實體轉換成相應的Xml或者Xml對象,未考慮到屬性的Attribute特性,以后有時間再整理一下。本文中的Xml匹配涉及到類的特性和屬性的特性,並且對該類的匹配進行了相應的優化,盡量將反射引起的性能問題降低最低(其實,對於對象數量不是很多的Xml匹配,性能是可以忽略不計的)。
二、類圖設計
主要思路為:通過反射將與類名的節點匹配,然后匹配屬性(屬性特性名稱或者屬性名稱)值,設計圖如下所示:
類圖中各個類的作用如下:
PropertyAttribute、ClassAttribute、StringExtension、FuncDictionary的作用詳見XmlAttribute與實體的轉換和匹配方案(附源碼)。
三、具體實現
3.1 ReflectionUtility
該類主要用於獲取某個類型的特性名稱和相應的屬性對象的字典。返回的類型為Dictionary<string, PropertyInfo>,其中Key為屬性的特性名稱,而非屬性名稱,Vaule為對應的屬性對象。如此設計,主要是方便后續的操作。
2 {
3 public static Dictionary< string, PropertyInfo> GetPropertyMapperDictionary<T>() where T : new()
4 {
5 return GetPropertyMapperDictionary( typeof(T));
6 }
7
8 public static Dictionary< string, PropertyInfo> GetPropertyMapperDictionary(Type type)
9 {
10 if (type == null)
11 {
12 return null;
13 }
14
15 List<PropertyInfo> properties = type.GetProperties().ToList();
16 var nameMapperDic = new Dictionary< string, PropertyInfo>();
17 string propertyAttributeName = null;
18
19 foreach (PropertyInfo property in properties)
20 {
21 propertyAttributeName = AttributeUtility.GetPropertyName(property);
22 nameMapperDic.Add(propertyAttributeName, property);
23 }
24
25 return nameMapperDic;
26 }
27
28 public static IList< string> GetPropertyNames<T>() where T : new()
29 {
30 List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
31 var propertyNames = new List< string>();
32
33 foreach (PropertyInfo property in properties)
34 {
35 propertyNames.Add(AttributeUtility.GetPropertyName(property));
36 }
37
38 return propertyNames;
39 }
40 }
3.2 AttributeUtility
該類主要用於獲取某個類型的類的特性名稱和屬性的特性名稱。如果類的特性不存在,則返回類名。如果屬性的特性名稱不存在,則返回屬性的名稱。
2 {
3 public static string GetClassName<T>() where T : new()
4 {
5 return GetClassName( typeof(T));
6 }
7
8 public static string GetClassName(Type type)
9 {
10 if (type == null)
11 {
12 return string.Empty;
13 }
14
15 string className = type.Name;
16 ClassAttribute[] attributes = type.GetCustomAttributes(
17 typeof(ClassAttribute), false) as ClassAttribute[];
18
19 if (attributes != null && attributes.Length > 0)
20 {
21 if (! string.IsNullOrWhiteSpace(attributes[ 0].Name))
22 {
23 className = attributes[ 0].Name;
24 }
25 }
26
27 return className;
28 }
29
30 public static string GetPropertyName(PropertyInfo property)
31 {
32 if (property == null)
33 {
34 return string.Empty;
35 }
36
37 string propertyName = property.Name;
38
39 PropertyAttribute[] attributes = property.GetCustomAttributes( typeof(PropertyAttribute),
40 false) as PropertyAttribute[];
41
42 if (attributes != null && attributes.Length > 0)
43 {
44 if (! string.IsNullOrWhiteSpace(attributes[ 0].Name))
45 {
46 propertyName = attributes[ 0].Name;
47 }
48 }
49
50 return propertyName;
51 }
52 }
3.2 XmlParser
該類主要通過兩種不同的方式匹配Xml為實體對象集合,一種直接通過XmlReader進行只進讀取匹配,另外一種通過XElement進行相應的匹配。
2 {
3 public static IList<T> Parse<T>( string xmlContent) where T : new()
4 {
5 try
6 {
7 if ( string.IsNullOrWhiteSpace(xmlContent))
8 {
9 return new List<T>();
10 }
11
12 using (StringReader reader = new StringReader(xmlContent))
13 {
14 return Parse<T>(XmlReader.Create(reader));
15 }
16 }
17 catch
18 {
19 return new List<T>();
20 }
21 }
22
23 public static IList<T> Parse<T>(XDocument document) where T : new()
24 {
25 if (document == null)
26 {
27 return new List<T>();
28 }
29
30 string className = AttributeUtility.GetClassName<T>();
31 IEnumerable<XElement> elements = document.Root.Elements(className);
32
33 return Parse<T>(elements);
34 }
35
36 public static IList<T> Parse<T>(IEnumerable<XElement> elements) where T : new()
37 {
38 if (elements == null || elements.Count() == 0)
39 {
40 return new List<T>();
41 }
42
43 try
44 {
45 var propertyDic = ReflectionUtility.GetPropertyMapperDictionary<T>();
46 List<T> entities = new List<T>();
47 IEnumerable<XElement> innerElements = null;
48 T entity = new T();
49
50 foreach (XElement element in elements)
51 {
52 entity = new T();
53 entities.Add(entity);
54 innerElements = element.Elements();
55
56 foreach (XElement innerElement in innerElements)
57 {
58 SetPropertyValue<T>(propertyDic, entity, innerElement.Name.ToString(), innerElement.Value);
59 }
60 }
61
62 return entities;
63 }
64 catch
65 {
66 return new List<T>();
67 }
68 }
69
70 public static IList<T> Parse<T>(XmlReader xmlReader) where T : new()
71 {
72 try
73 {
74 if (xmlReader == null)
75 {
76 return new List<T>();
77 }
78
79 return ParseXmlReader<T>(xmlReader);
80 }
81 catch
82 {
83 return new List<T>();
84 }
85 }
86
87 private static IList<T> ParseXmlReader<T>(XmlReader xmlReader) where T : new()
88 {
89 List<PropertyInfo> properties = new List<PropertyInfo>( typeof(T).GetProperties());
90 var propertyDic = ReflectionUtility.GetPropertyMapperDictionary<T>();
91 string className = AttributeUtility.GetClassName<T>();
92 IList<T> entities = new List<T>();
93 T entity = new T();
94 string lastElementName = null;
95
96 while (xmlReader.Read())
97 {
98 switch (xmlReader.NodeType)
99 {
100 case XmlNodeType.Element:
101 if ( string.Equals(xmlReader.Name, className))
102 {
103 entity = new T();
104 entities.Add(entity);
105 }
106 lastElementName = xmlReader.Name;
107 break;
108 case XmlNodeType.Text:
109 SetPropertyValue<T>(propertyDic, entity, lastElementName, xmlReader.Value);
110 break;
111 default:
112 break;
113 }
114 }
115
116 return entities;
117 }
118
119 private static void SetPropertyValue<T>(Dictionary< string, PropertyInfo> propertyDic, T entity, string lastElementName, string value) where T : new()
120 {
121 if (! string.IsNullOrWhiteSpace(lastElementName) && propertyDic.ContainsKey(lastElementName))
122 {
123 PropertyInfo currentProperty = propertyDic[lastElementName];
124 if (currentProperty != null && currentProperty.CanWrite)
125 {
126 object invokeResult = new FuncDictionary().DynamicInvoke(currentProperty.PropertyType, value);
127 currentProperty.SetValue(entity, invokeResult, null);
128 }
129 }
130 }
131 }
以上兩種不同的方式匹配Xml,都是通過var propertyDic = ReflectionUtility.GetPropertyMapperDictionary<T>();來對反射所引起的性能進行了相關的優化。對於多個實體的匹配,只需要執行一次映射獲取到屬性特性的名稱與對應屬性的字典,其后的匹配以該字典來進行操作。
四、總結
與上一篇隨筆將對象集合轉換為Xml恰恰相反,對於如下類似格式的Xml:

<DefaultName>MapperInfoItemIndex0</DefaultName>
<CreatedTime> 2012/ 1/ 6 19: 24: 34</CreatedTime>
<IsActive>True</IsActive>
<DefaultValue> 10</DefaultValue>
<Percent> 27</Percent>
<TargetUrl>www.codeplex.com?Id= 0</TargetUrl>
</MapperInfoItem>
本文中以上的代碼完全能夠對其進行相應的匹配。只不過上一篇隨筆中未對類的特性名稱和屬性的特性名稱進行考慮罷了,以后有時間再貼上改進后的代碼。