一、前言
上一篇隨筆主要將實體轉換成相應的Xml或者Xml對象,未考慮到屬性的Attribute特性,以後有時間再整理一下。本文中的Xml匹配涉及到類的特性和屬性的特性,並且對該類的匹配進行了相應的優化,盡量將反射引起的性能問題降低最低(其實,對於對象數量不是很多的Xml匹配,性能是可以忽略不計的)。
二、類圖設計
主要思路為:通過反射將與類名的節點匹配,然後匹配屬性(屬性特性名稱或者屬性名稱)值,設計圖如下所示:

類圖中各個類的作用如下:
PropertyAttribute、ClassAttribute、StringExtension、FuncDictionary的作用詳見XmlAttribute與實體的轉換和匹配方案(附源碼)。
AttributeUtility主要用於獲取類名和屬性名稱。
ReflectionUtility主要用於獲取類型的屬性對象與屬性對應的特性名稱的字典,為匹配進行優化處理。
XmlParser主要用於匹配Xml為實體對象。
三、具體實現
3.1 ReflectionUtility
該類主要用於獲取某個類型的特性名稱和相應的屬性對象的字典。返回的類型為Dictionary<string, PropertyInfo>,其中Key為屬性的特性名稱,而非屬性名稱,Vaule為對應的屬性對象。如此設計,主要是方便後續的操作。
主要方法為:
public static Dictionary<string, PropertyInfo> GetPropertyMapperDictionary<T>() where T : new(),
public static Dictionary<string, PropertyInfo> GetPropertyMapperDictionary(Type type) ,代碼如下:
public class ReflectionUtility
{
public static Dictionary<string, PropertyInfo> GetPropertyMapperDictionary<T>() where T : new()
{
return GetPropertyMapperDictionary(typeof(T));
}
public static Dictionary<string, PropertyInfo> GetPropertyMapperDictionary(Type type)
{
if (type == null)
{
return null;
}
List<PropertyInfo> properties = type.GetProperties().ToList();
var nameMapperDic = new Dictionary<string, PropertyInfo>();
string propertyAttributeName = null;
foreach (PropertyInfo property in properties)
{
propertyAttributeName = AttributeUtility.GetPropertyName(property);
nameMapperDic.Add(propertyAttributeName, property);
}
return nameMapperDic;
}
public static IList<string> GetPropertyNames<T>() where T : new()
{
List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
var propertyNames = new List<string>();
foreach (PropertyInfo property in properties)
{
propertyNames.Add(AttributeUtility.GetPropertyName(property));
}
return propertyNames;
}
}
3.2 AttributeUtility
該類主要用於獲取某個類型的類的特性名稱和屬性的特性名稱。如果類的特性不存在,則返回類名。如果屬性的特性名稱不存在,則返回屬性的名稱。
主要方法為:
public static string GetClassName<T>() where T : new(),
public static string GetClassName(Type type) ,
public static string GetPropertyName(PropertyInfo property) ,代碼如下:
public class AttributeUtility
{
public static string GetClassName<T>() where T : new()
{
return GetClassName(typeof(T));
}
public static string GetClassName(Type type)
{
if (type == null)
{
return string.Empty;
}
string className = type.Name;
ClassAttribute[] attributes = type.GetCustomAttributes(
typeof(ClassAttribute), false) as ClassAttribute[];
if (attributes != null && attributes.Length > 0)
{
if (!string.IsNullOrWhiteSpace(attributes[0].Name))
{
className = attributes[0].Name;
}
}
return className;
}
public static string GetPropertyName(PropertyInfo property)
{
if (property == null)
{
return string.Empty;
}
string propertyName = property.Name;
PropertyAttribute[] attributes = property.GetCustomAttributes(typeof(PropertyAttribute),
false) as PropertyAttribute[];
if (attributes != null && attributes.Length > 0)
{
if (!string.IsNullOrWhiteSpace(attributes[0].Name))
{
propertyName = attributes[0].Name;
}
}
return propertyName;
}
}
3.2 XmlParser
該類主要通過兩種不同的方式匹配Xml為實體對象集合,一種直接通過XmlReader進行只進讀取匹配,另外一種通過XElement進行相應的匹配。
主要方法為:
public static IList<T> Parse<T>(string xmlContent) where T : new(),
public static IList<T> Parse<T>(XDocument document) where T : new() ,
public static IList<T> Parse<T>(IEnumerable<XElement> elements) where T : new(),
public static IList<T> Parse<T>(XmlReader xmlReader) where T : new() ,代碼如下:
public class XmlParser
{
public static IList<T> Parse<T>(string xmlContent) where T : new()
{
try
{
if (string.IsNullOrWhiteSpace(xmlContent))
{
return new List<T>();
}
using (StringReader reader = new StringReader(xmlContent))
{
return Parse<T>(XmlReader.Create(reader));
}
}
catch
{
return new List<T>();
}
}
public static IList<T> Parse<T>(XDocument document) where T : new()
{
if (document == null)
{
return new List<T>();
}
string className = AttributeUtility.GetClassName<T>();
IEnumerable<XElement> elements = document.Root.Elements(className);
return Parse<T>(elements);
}
public static IList<T> Parse<T>(IEnumerable<XElement> elements) where T : new()
{
if (elements == null || elements.Count() == 0)
{
return new List<T>();
}
try
{
var propertyDic = ReflectionUtility.GetPropertyMapperDictionary<T>();
List<T> entities = new List<T>();
IEnumerable<XElement> innerElements = null;
T entity = new T();
foreach (XElement element in elements)
{
entity = new T();
entities.Add(entity);
innerElements = element.Elements();
foreach (XElement innerElement in innerElements)
{
SetPropertyValue<T>(propertyDic, entity, innerElement.Name.ToString(), innerElement.Value);
}
}
return entities;
}
catch
{
return new List<T>();
}
}
public static IList<T> Parse<T>(XmlReader xmlReader) where T : new()
{
try
{
if (xmlReader == null)
{
return new List<T>();
}
return ParseXmlReader<T>(xmlReader);
}
catch
{
return new List<T>();
}
}
private static IList<T> ParseXmlReader<T>(XmlReader xmlReader) where T : new()
{
List<PropertyInfo> properties = new List<PropertyInfo>(typeof(T).GetProperties());
var propertyDic = ReflectionUtility.GetPropertyMapperDictionary<T>();
string className = AttributeUtility.GetClassName<T>();
IList<T> entities = new List<T>();
T entity = new T();
string lastElementName = null;
while (xmlReader.Read())
{
switch (xmlReader.NodeType)
{
case XmlNodeType.Element:
if (string.Equals(xmlReader.Name, className))
{
entity = new T();
entities.Add(entity);
}
lastElementName = xmlReader.Name;
break;
case XmlNodeType.Text:
SetPropertyValue<T>(propertyDic, entity, lastElementName, xmlReader.Value);
break;
default:
break;
}
}
return entities;
}
private static void SetPropertyValue<T>(Dictionary<string, PropertyInfo> propertyDic, T entity, string lastElementName, string value) where T : new()
{
if (!string.IsNullOrWhiteSpace(lastElementName) && propertyDic.ContainsKey(lastElementName))
{
PropertyInfo currentProperty = propertyDic[lastElementName];
if (currentProperty != null && currentProperty.CanWrite)
{
object invokeResult = new FuncDictionary().DynamicInvoke(currentProperty.PropertyType, value);
currentProperty.SetValue(entity, invokeResult, null);
}
}
}
}
以上兩種不同的方式匹配Xml,都是通過var propertyDic = ReflectionUtility.GetPropertyMapperDictionary<T>();來對反射所引起的性能進行了相關的優化。對於多個實體的匹配,只需要執行一次映射獲取到屬性特性的名稱與對應屬性的字典,其後的匹配以該字典來進行操作。
四、總結
與上一篇隨筆將對象集合轉換為Xml恰恰相反,對於如下類似格式的Xml:
<MapperInfoItem> <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>
本文中以上的代碼完全能夠對其進行相應的匹配。只不過上一篇隨筆中未對類的特性名稱和屬性的特性名稱進行考慮罷了,以後有時間再貼上改進後的代碼。
作者:JasenKin
出處:http://www.cnblogs.com/jasenkin/