c#使用Attribute模拟JAVA的Annotation实现属性xml的读取
声明
突发奇想的一个处理问题的方法,好不好用不用就仁者见仁智者见智了。
用法
1. 实现自定义Attribute
namespace 秃顶韭菜的命名空间
{
public class XmlMapping : Attribute
{
/// <summary>
/// xml属性地址
/// </summary>
private string xmlAddress = string.Empty;
/// <summary>
/// xml中存储值的类型
/// </summary>
private ParamType xmlType = ParamType.OBJECT;
/// <summary>
/// 指定参数处理函数
/// </summary>
private string xmlFunc = string.Empty;
public string XmlAddress { get => xmlAddress; set => xmlAddress = value; }
public ParamType XmlType { get => xmlType; set => xmlType = value; }
public string XmlFunc { get => xmlFunc; set => xmlFunc = value; }
}
public enum ParamType
{
INT=1,
FLOAT=2,
DOUBLE=3,
STRING=4,
OBJECT=5,
USER=6
}
}
2.标注我们的配置文件
namespace 秃顶韭菜的命名空间
{
public class DemoConfig : ICustomizationConfig
{
[XmlMapping(XmlAddress = "//Id", XmlType =ParamType.INT)]
public int Id { get; set; }
[XmlMapping(XmlAddress = "//Code", XmlType = ParamType.STRING)]
public string Code { get; set; }
[XmlMapping(XmlAddress = "//Version", XmlType = ParamType.STRING)]
public string Version { get; set; }
[XmlMapping(XmlAddress = "//TestObject", XmlType = ParamType.USER, XmlFunc= "DemoUserParam")]
public List<PointF> points { get; set; }
/// <summary>
/// 示例,对于特殊参数使用自定义解析方法
/// </summary>
/// <param name="xmlStr">读取自xml的字符串配置信息</param>
/// <returns>注意这里的return类型必须要和你定义的属性类型一致,因为要填充的!</returns>
public List<PointF> DemoUserParam(string xmlStr)
{
string[] pointStrs = xmlStr.Trim().Split('|');
List<PointF> points = new List<PointF>();
foreach (string pointStr in pointStrs)
{
string[] x_y = pointStr.Replace('(', ' ').Replace(')', ' ').Trim().Split(',');
if (x_y.Length != 2)
{
throw new Exception("娃,看看配置文件是不是整错了?中英文逗阔号啥的。");
}
else
{
PointF point = new PointF(int.Parse(x_y[0]), int.Parse(x_y[1]));
points.Add(point);
}
}
return points;
}
}
}
3.配置文件示例
<?xml version="1.0" encoding="utf-8" ?>
<Config>
<Id>100</Id>
<Code>DFT01</Code>
<UpdateTime>2020-12-31 19:06:00</UpdateTime>
<Version>pigger</Version>
<TestObject>(1,1)|(2,4)|(3,5)</TestObject>
</Config>
4.核心处理函数
读取attribute配置参数,并使用反射执行相关功能。
namespace 秃顶韭菜的命名空间
{
public class ConfigReader
{
/// <summary>
/// 从XML中读取配置
/// </summary>
/// <typeparam name="T">ICustomizationConfig 自定义的配置文件接口,可以不限定</typeparam>
/// <param name="path">配置文件路径</param>
/// <param name="rootName">xml根节点名称</param>
/// <param name="configClass">配置文件对象</param>
/// <returns></returns>
public static bool ReadConfigFromXML<T>(string path, string rootName, ref T configClass)
where T : ICustomizationConfig
{
XmlHelper xmlHelper = new XmlHelper(path, rootName);
XmlDocument xmlDocument = xmlHelper.LoadXmlDocument();
PropertyInfo[] propertyInfos = configClass.GetType().GetProperties();
foreach (PropertyInfo info in propertyInfos)
{
// TODO 我们假设只使用一个标签,不存在多个,所以用first来获取
XmlMapping mapping = info.GetCustomAttributes().First() as XmlMapping;
if (!string.IsNullOrEmpty(mapping.XmlAddress)) // 读取配置文件的地址
{
string value = xmlHelper.GetInnerText(xmlDocument, mapping.XmlAddress);
switch (mapping.XmlType)
{
case ParamType.INT:
int valueInt = int.Parse(value);
info.SetValue(configClass, valueInt);
break;
case ParamType.DOUBLE:
double valueDouble = double.Parse(value);
info.SetValue(configClass, valueDouble);
break;
case ParamType.FLOAT:
float valueFloat = float.Parse(value);
info.SetValue(configClass, valueFloat);
break;
case ParamType.STRING:
info.SetValue(configClass, value);
break;
case ParamType.OBJECT:
info.SetValue(configClass, value);
break;
case ParamType.USER:
if (!string.IsNullOrEmpty(mapping.XmlFunc))
{
MethodInfo methodInfo = configClass.GetType().GetMethod(mapping.XmlFunc);
Object[] param = {value};
object res = methodInfo.Invoke(configClass, param);
info.SetValue(configClass, res);
}
else
{
throw new Exception("自定义处理函数时,XmlFunc指定的函数不能为空!");
}
break;
default:
info.SetValue(configClass, value);
break;
}
}
else
{
throw new Exception("XmlAddress不能为空!");
}
}
return true;
}
}
}
5.使用它
只要在系统启动时调用,将其注入到实例即可,具体什么时候调用构建实例和注入,看应用场景。
DemoConfig config = new DemoConfig();
ConfigReader.ReadConfigFromXML(configPath, rootName, ref config);