上篇示範物件轉 XML,本文著重於 XML 解析為強型別集合,並引入 ClassAttribute/PropertyAttribute 支援類別與屬性別名對應,同時以字典快取屬性資訊優化反射效能。
Dictionary<string, PropertyInfo>,Key 為 XML 屬性名,Value 為屬性反射物件,一次反射多次複用。public static string GetClassName<T>() where T : new() => GetClassName(typeof(T));
public static string GetClassName(Type type)
{
if (type == null) return string.Empty;
string name = type.Name;
var attrs = type.GetCustomAttributes(typeof(ClassAttribute), false) as ClassAttribute[];
if (attrs != null && attrs.Length > 0 && !string.IsNullOrWhiteSpace(attrs[0].Name))
name = attrs[0].Name;
return name;
}
public static string GetPropertyName(PropertyInfo prop)
{
if (prop == null) return string.Empty;
string name = prop.Name;
var attrs = prop.GetCustomAttributes(typeof(PropertyAttribute), false) as PropertyAttribute[];
if (attrs != null && attrs.Length > 0 && !string.IsNullOrWhiteSpace(attrs[0].Name))
name = attrs[0].Name;
return name;
}
public static Dictionary<string, PropertyInfo> GetPropertyMapperDictionary<T>() where T : new()
=> GetPropertyMapperDictionary(typeof(T));
public static Dictionary<string, PropertyInfo> GetPropertyMapperDictionary(Type type)
{
if (type == null) return null;
var dict = new Dictionary<string, PropertyInfo>();
foreach (PropertyInfo prop in type.GetProperties())
dict.Add(AttributeUtility.GetPropertyName(prop), prop);
return dict;
}
public static IList<T> Parse<T>(XmlReader reader) where T : new()
{
try { return ParseXmlReader<T>(reader); } catch { return new List<T>(); }
}
private static IList<T> ParseXmlReader<T>(XmlReader reader) where T : new()
{
var propDict = ReflectionUtility.GetPropertyMapperDictionary<T>();
string className = AttributeUtility.GetClassName<T>();
var entities = new List<T>();
T entity = new T();
string lastElementName = null;
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (string.Equals(reader.Name, className))
{
entity = new T();
entities.Add(entity);
}
lastElementName = reader.Name;
break;
case XmlNodeType.Text:
SetPropertyValue(propDict, entity, lastElementName, reader.Value);
break;
}
}
return entities;
}
public static IList<T> Parse<T>(IEnumerable<XElement> elements) where T : new()
{
if (elements == null || !elements.Any()) return new List<T>();
try
{
var propDict = ReflectionUtility.GetPropertyMapperDictionary<T>();
var entities = new List<T>();
foreach (XElement el in elements)
{
T entity = new T();
entities.Add(entity);
foreach (XElement child in el.Elements())
SetPropertyValue(propDict, entity, child.Name.LocalName, child.Value);
}
return entities;
}
catch { return new List<T>(); }
}
private static void SetPropertyValue<T>(Dictionary<string, PropertyInfo> dict, T entity, string xmlName, string value) where T : new()
{
if (string.IsNullOrWhiteSpace(xmlName) || !dict.ContainsKey(xmlName)) return;
PropertyInfo prop = dict[xmlName];
if (prop != null && prop.CanWrite)
{
object val = new FuncDictionary().DynamicInvoke(prop.PropertyType, value);
prop.SetValue(entity, val, null);
}
}
反射資訊快取:無論解析多少實體,ReflectionUtility.GetPropertyMapperDictionary<T>() 僅呼叫一次,後續皆以字典查找 PropertyInfo,大幅降低反射開銷。對於物件數量不大的情境,效能差異可忽略;大量資料時效果顯著。
<MapperInfoItem>
<DefaultName>MapperInfoItemIndex0</DefaultName>
<CreatedTime>2012/1/6 19:24:34</CreatedTime>
<IsActive>True</IsActive>
...
</MapperInfoItem>
以上兩種解析器均能正確處理此類「子元素對應屬性」的 XML 結構。
作者:JasenKin | 出處:https://www.cnblogs.com/jasenkin/