上一篇随笔主要将实体转换成相应的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>
本文中以上的代码完全能够对其进行相应的匹配。只不过上一篇随笔中未对类的特性名称和属性的特性名称进行考虑罢了,以后有时间再贴上改进后的代码。