您的位置:首页 > 编程语言 > C#

XML反射解析

2016-06-03 14:14 429 查看
      和同事对接一个接口,采用xml传输,从root节点到叶节点总共套了13层,导致对应实体类的编写困难,同时在xml操作6层后反序列化的时候总是会丢失部分信息,于是便想有没有什么办法更加简单地解析xml,花了一晚上时间,采用xpath加反射机制弄出了本文的成果,整体分为3块:实体类、配置类和解析器。

        首先是定义一个helper工具类,作为解析器使用,核心思路就是根据传入的实体类和实体类对应的节点配置文件利用反射来获取值,遇到泛型的嵌套就递归获取下一层级的对象的值,代码里注释已经比较完整了,就不重复说了,需要注意的是目前的做法需要将帮助类、实体类、配置类放在同一个程序集内,因为目前使用的反射方式不支持跨程序集,有需要的可以自己修改:

(因为是为了满足遇到的问题写的,所以需要的朋友需要拉去自己改改才能用,例如在实体类中具有多个LIst<T>的情况下就需要修改代码)

/// <summary>
/// XML解析工具
/// </summary>
public class XmlConvertUtility
{
/// <summary>
/// 解析XML接口
/// </summary>
/// <typeparam name="T">返回对象</typeparam>
/// <param name="xml">返回的xml</param>
/// <param name="rootNodeXPath">数据初始节点pxath表达式</param>
/// <param name="config">配置类</param>
/// <returns></returns>
public List<T> ConvertXmlStringToObject<T>(string xml,string rootNodeXPath,object config)where T:new()
{
List<T> resultList = new List<T>();
try
{
if (string.IsNullOrWhiteSpace(xml))
throw new AggregateException("Xml不能为空");
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNodeList nodes = doc.SelectNodes(rootNodeXPath);
resultList = ConvertXmlToObject<T>(nodes, config);
}
catch (Exception ex)
{
throw ex;
}
return resultList;
}
/// <summary>
/// 根据配置文件解析XML
/// </summary>
/// <typeparam name="T">返回对象,单返回对象作为List中的泛型类时需要配置一下两个参数
/// public string ConfigName { get { return "ProductItemXPathConfig"; } }//配置对象名称
/// public string ConfigNodeXPath { get { return "OrderSheet/orderDetailSet"; } }//XPath语句
/// </typeparam>
/// <param name="pNodes">初始节点</param>
/// <param name="xPathObject">Xpath表达式</param>
/// <returns></returns>
public List<T> ConvertXmlToObject<T>(XmlNodeList pNodes, object xPathObject) where T : new()
{
try
{
List<T> resultObjectList = new List<T>();//输出对象列表
T resultObject = new T();//输出对象
Type resultObjectType = resultObject.GetType();//输出对象类型
Type xPathObjectType = xPathObject.GetType();//配置对象类型
foreach (XmlNode node in pNodes)//遍历XML
{
resultObject = new T();
object objectPropertyValue;
foreach (PropertyInfo xPathObjectProperty in xPathObjectType.GetProperties())//遍历配置对象
{
string xPathObjectPropertyName = xPathObjectProperty.Name;//配置类属性名
if (string.IsNullOrWhiteSpace(xPathObjectPropertyName))
continue;
Type objectPropertyType = resultObjectType.GetProperty(xPathObjectPropertyName).PropertyType;//获取输出对象类型

if (objectPropertyType.IsGenericType)//如果是泛型则递归加载下层
{
//返回对象
Type childResultObjectType = objectPropertyType.GetGenericArguments().FirstOrDefault();//返回泛型中的对象
ConstructorInfo childResultObjectConstructor = childResultObjectType.GetConstructor(new Type[0]);//构造器
Object childResultObject = childResultObjectConstructor.Invoke(new Object[0]);//实例化配置对象

//XML,需要返回对象实例化后才能获得下面的值
PropertyInfo childXPthProperty = childResultObjectType.GetProperty("ConfigNodeXPath");//获取Xpath节点
if (childXPthProperty == null)
throw new AggregateException("对象中具有List<T>时需配置ConfigNodeXPath属性");
string childXPathPropertyValue = childXPthProperty.GetValue(childResultObject, null).ToString();
if (string.IsNullOrWhiteSpace(childXPathPropertyValue))
throw new AggregateException("ConfigNodeXPath值不能为空");
XmlNodeList childPNode = node.SelectNodes(childXPathPropertyValue);//获取Xpath值
if (childPNode == null)
continue;
//配置对象
PropertyInfo childConfigObjectProperty = childResultObjectType.GetProperty("ConfigName");//获取Xpath配置节点
if (childConfigObjectProperty == null)
throw new AggregateException("对象中具有List<T>时需配置ConfigName属性");
string childConfigObjectValue = childConfigObjectProperty.GetValue(childResultObject, null).ToString();//获取Xpath配置对象名
if (string.IsNullOrWhiteSpace(childConfigObjectValue))
throw new AggregateException("ConfigName值不能为空");
Type childConfigObjectType = Type.GetType(childResultObjectType.Assembly.GetName().Name
+ ".Config." + childConfigObjectValue, true); //获取配置对象类型
if (childConfigObjectType == null)
throw new Exception("获取配置对象类型失败");
ConstructorInfo childConfigObjectConstructor = childConfigObjectType.GetConstructor(new Type[0]);//构造器
Object childConfigObject = childConfigObjectConstructor.Invoke(new Object[0]);//实例化配置对象

//递归执行
MethodInfo mi = this.GetType().GetMethod("ConvertXmlToObject").MakeGenericMethod(childResultObjectType);//委托执行递归
objectPropertyValue = mi.Invoke(this, new object[] { childPNode, childConfigObject });//递归获取下层List值
}
else
{
string xPathObjectPropertyValue = xPathObjectProperty.GetValue(xPathObject, null).ToString();//获取配置对象属性值
if (string.IsNullOrWhiteSpace(xPathObjectPropertyValue))//判断配置对象属性值是否为空,如果是空则说明不映射
continue;
if (resultObjectType.GetProperty(xPathObjectPropertyName) == null)//判断输出对象是否有对应名称的属性
continue;
XmlNode objectNode = node.SelectSingleNode(xPathObjectPropertyValue);
if (objectNode == null)
continue;
string resultObjectStringValue = objectNode.InnerText;//获取输出对象XML节点值
objectPropertyValue = Convert.ChangeType(resultObjectStringValue, objectPropertyType);//获取输出对象属性值
}
resultObjectType.GetProperty(xPathObjectPropertyName).SetValue(resultObject, objectPropertyValue, null);
}
resultObjectList.Add(resultObject);
}
return resultObjectList;
}
catch (Exception ex)
{
throw ex;
}
}
}


然后定义一个实体类,需要说明的是多级嵌套的子级实体中需要配置ConfigName和ConfigNodeXPath两个属性,用来查找对应的配置类和xml节点:

/// <summary>
/// 用户类
/// </summary>
public class User
{
public string Id{ get; set; }
public string Name{ get; set; }
public List<UserInfo> InfoList { get; set; }
}
public class UserInfo
{
public string ConfigName { get { return UserInfo.name; } }
        public string ConfigNodeXPath { get { return UserInfo.xpath; } }
public string ChildName { get; set; }
}
最后定义配置类,用来配置每个属性对应的节点名称(xpath路径):

public class UserConfig
{
private string id= "id";
private string name= "name";
private List<UserInfo> infoList;

public List<UserInfoConfig> InfoList
{
get { return infoList; }
set { infoList= value; }
}
public string Id
{
get { return id; }
set { id= value; }
}
public string Name
{
get { return name; }
set { name= value; }
}
}

public class UserInfoConfig
{
public static string name = "UserInfoConfig";
public static string xpath = "userDetailSet/UserInfo";

private string childName = "name";

public string ChildName
{
get { return childName ; }
set { childName  = value; }
}
}


最后是使用:

string xmlStr=@"<root>
  <body>
    <user>
      <id>S001</id>
      <name>张三</name>
      <userDetailSet clazz=\"com.UserInfo\">
        <UserInfo>
          <name>李四</name>
        </UserInfo>
        <UserInfo>
          <name>王五</name>
        </UserInfo>
      </userDetailSet>
    </user>
  </body> 
</root>";            XmlConvertUtility xml = new XmlConvertUtility();
User test=xml.ConvertXmlStringToObject<User>(xmlStr, "//body/user", new UserConfig());</span>


发现文中不妥之处欢迎提出宝贵意见,留言评论,共同进步,谢谢

原创内容,欢迎转载,但请注明出处:http://blog.csdn.net/huang461072830
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C# .net xml 反射 反序列