1、作用:将实体类的属性(字符串、日期、数值、布而、类)生成为xml文档中的结点,将xml文档的结点值或者属性值填充到实体类的属性值中
2、思路:特性、反射、泛型;特性记录xml结点与实体属性的对应关系,反射获取和填充实例中的属性值。 自定义NodeAttribute,两个属性Name和Index,Name用于对应实体类中属性在xml文档中的结点名称,Index反应不同结点在xml文档中的出现顺序。
3、两个类:
XmlNodeSerializer,实体<->XmlNode;因为有时候要操作的xml文档结构比较复杂,NodeAttribute不能实现所有需求,所以把简单基础的结点在里做了,复杂的结点自己实现。
XmlSerializer:从XmlNodeSerializer继承来的,实体<->XmlDocument
4、可设置的部分:
1)TimeFormat:可以设置实例中日期类型的属性值的输出格式;
2)NodeNameFormatter:可以对结点名称格式化,属性与结点的名称对应关系a)如果设置了NodeAttribute,从NodeAttribute中取;b)如果没有NodeAttribute默认属性名;c)如果设置了NodeNameFormatter,再按NodeNameFormatter的规则格式化
3)CreateNodeWhenNull:属性值为null时是否生成结点
4)ContainPropertyWithOutNodeAttr:属性值没有被NodeAttribute标记时,是否生成、解析结点

测试

        public void Test1()
{
XmlSerializer xs = new XmlSerializer();
xs.TimeFormat = "yyyy-MM-dd";
xs.NodeNameFormatter = Format;
xs.CreateNodeWhenNull = true;
xs.ContainPropertyWithOutNodeAttr = true; Student stu = new Student();
stu.Name = "小小";
stu.Age = 10;
stu.BirthDay = new DateTime(2008, 6, 7);
Dog dog = new Dog();
dog.Color = "黑色";
dog.Age = 2;
dog.Weight = 12.34;
//dog.Name //null 测试CreateNodeWhenNull
stu.Dog = dog;
XmlDocument doc = xs.Serialize(stu);
doc.Save("1.xml");
Student stu2 = xs.DeSerialize<Student>(File.ReadAllText("1.xml"));
} private string Format(string attr)
{
if (string.IsNullOrEmpty(attr)) return null;
StringBuilder sb = new StringBuilder();
char[] name = attr.ToCharArray();
for (int i = 0; i < name.Length; i++)
{
if (i.Equals(0))
{
sb.Append(name[i].ToString().ToUpper());
}
else
{
if (name[i] >= 'A' && name[i] <= 'Z')
{
sb.Append("_").Append(name[i].ToString());
}
else
{
sb.Append(name[i].ToString().ToUpper());
}
}
}
return sb.ToString();
} public class Student
{
[Node(Index = 2, Name = "MingCheng")]
public string Name { get; set; }
[Node(Index = 3)]
public int Age { get; set; }
[Node(Index = 1)]
public DateTime? BirthDay { get; set; }
[Node(Name = "GouGou")]
public Dog Dog { get; set; }
} public class Dog
{
[Node]
public string Name { get; set; }
[Node]
public int? Age { get; set; }
[Node]
public double? Weight { get; set; }
public string Color { get; set; }
}

源代码

    /// <summary>
/// xml结点序列化,反序列化
/// TimeFormat:设置日期属性的格式
/// NodeNameFormatter:可以对结点名称格式化
/// 结点名:如果设置了NodeAttr的Name,按设置取,否则默认取属性名
/// CreateNodeWhenNull:设置属性值为null时是否生成结点
/// ContainPropertyWithOutNodeAttr:属性值没有被NodeAttr标记时,是否生成、解析结点
/// </summary>
public class XmlNodeSerializer
{
protected string timeFormat;
protected Func<string, string> nodeFormatter;
protected bool createNodeWhenNull = true;
protected bool containPropertyWithOutNodeAttr = false; /// <summary>
/// 日期格式
/// </summary>
public string TimeFormat { set { timeFormat = value; } } /// <summary>
/// 结点名称格式化器
/// </summary>
public Func<string, string> NodeNameFormatter { set { nodeFormatter = value; } } /// <summary>
/// 空属性值是否生成结点
/// </summary>
public bool CreateNodeWhenNull { set { createNodeWhenNull = value; } } /// <summary>
/// 属性没有被NodeAttr标记时,是否生成结点或解析,默认false
/// </summary>
public bool ContainPropertyWithOutNodeAttr { set { containPropertyWithOutNodeAttr = value; } } //如果node需要添加到doc中,则需要将doc作参数传入
public XmlNode Serialize<T>(T data, XmlDocument doc)
{
Type type = typeof(T);
string nodeName = string.Empty; //结点名
NodeAttribute na = type.GetCustomAttribute<NodeAttribute>();
if (na == null || na.Name.IsNullOrEmpty())
{
nodeName = type.Name;//如果没有设置,使用类名
}
else
{
nodeName = na.Name; //取设置的结点名
}
if (nodeFormatter != null) nodeName = nodeFormatter(nodeName);
return Serialize(data, typeof(T), nodeName, doc);
} public XmlNode Serialize<T>(T data)
{
return Serialize(data, new XmlDocument());
} private XmlNode Serialize(object data, Type type, string parentNodeName, XmlDocument doc)
{
if (data == null || parentNodeName.IsNullOrEmpty() || type == null || doc == null) return null;
XmlNode root = doc.CreateElement(parentNodeName);
Entity ent = new Entity();
//对自定义类、基础数据类型的属性值生成节点
var props = type.GetProperties().Where(p => ent.IsUserDefinedClass(p) || ent.IsNormalBaseType(p));
//有注解的属性
var selfNodeProps = props.Where(p =>
{
var cus = p.GetCustomAttributes(typeof(NodeAttribute), true);
return cus != null && cus.Count() > 0;
}).OrderBy(p =>
{
NodeAttribute na = p.GetCustomAttributes(typeof(NodeAttribute), true)[0] as NodeAttribute;
int i = na.Index;
Console.WriteLine($"{na.Name},{na.Index}");
return i;
}).ToArray();
List<PropertyInfo> pis = new List<PropertyInfo>();
//按注解里的先后顺序添加结点
if (selfNodeProps != null && selfNodeProps.Count() > 0)
{
pis.AddRange(selfNodeProps);
}
if (containPropertyWithOutNodeAttr)
{
//没有注解的属性
var defaultNodeProps = props.Where(p =>
{
var cus = p.GetCustomAttributes(typeof(NodeAttribute), true);
return cus == null || cus.Count() <= 0;
});
//再添加没有注解的结点
if (defaultNodeProps != null && defaultNodeProps.Count() > 0)
{
pis.AddRange(defaultNodeProps);
}
}
foreach (var prop in pis)
{
string nodeName = prop.Name;//结点名,默认没有注解,用属性名
var attrs = prop.GetCustomAttributes(typeof(NodeAttribute), true);
if (attrs != null && attrs.Count() > 0)
{
NodeAttribute attr = attrs[0] as NodeAttribute;
if (attr.Name.IsNotNullOrEmpty())//注解里有结点名
{
nodeName = attr.Name;
}
}
if (nodeFormatter != null)//如果设置了更改规则,按规则修改
{
nodeName = nodeFormatter(nodeName);
}
if (ent.IsNormalBaseType(prop))//基础数据类型,直接生成结点
{
string nodeValue = null;
object v = prop.GetValue(data);
if (prop.PropertyType.ToString().Contains("DateTime"))
{
if (v != null)
{
if (timeFormat.IsNullOrEmpty()) timeFormat = "yyyyMMdd";
nodeValue = Convert.ToDateTime(v).ToString(timeFormat);
}
}
else if (v != null)
{
nodeValue = Convert.ToString(v);
}
if (nodeValue == null && !createNodeWhenNull)
{
continue;//属性值为null,且设置了null值不需要加入结点
}
XmlNode node = doc.CreateElement(nodeName);//创建结点
node.AppendChild(doc.CreateCDataSection(nodeValue));
root.AppendChild(node);//拼接到根结点中
}
else if (ent.IsUserDefinedClass(prop))//自定义类,生成复杂结点
{
object v = prop.GetValue(data);
if (v != null)
{
XmlNode node = Serialize(v, prop.PropertyType, nodeName, doc);
if (node != null)
{
root.AppendChild(node);
}
}
}
}
return root;
} //对基础数据类型属性赋值
private void FillBaseType<T>(XmlNode node, T obj, IEnumerable<PropertyInfo> baseProps)
{
DataType myType = new DataType();
foreach (var prop in baseProps)
{
string name = GetNodeNameFromProp(prop);
XmlNode xn = node.SelectSingleNode(name);
string xnv = string.Empty;
if (xn == null)
{
//如果没有对应结点则从属性中找
var attr = node.Attributes[name];
if (attr != null && attr.Value.IsNotNullOrEmpty())
{
xnv = attr.Value;
}
else
{
continue;
}
}
else if (xn.InnerText.IsNullOrEmpty())
{
continue;
}
else
{
xnv = xn.InnerText;
}
//取属性的数据类型
DbType dbt = myType.PropertyTypeToDbType(prop.PropertyType.ToString());
switch (dbt)
{
case DbType.AnsiString:
prop.SetValue(obj, xnv, null); break;
case DbType.DateTime:
DateTime dt;
if (timeFormat.IsNullOrEmpty())//如果没有设置日期格式,按值长度自动设置
{
if (xnv.Length.Equals(8)) timeFormat = "yyyyMMdd";
if (xnv.Length.Equals(14)) timeFormat = "yyyyMMddHHmmss";
}
if (DateTime.TryParseExact(xnv, timeFormat, null, DateTimeStyles.None, out dt))
{
prop.SetValue(obj, dt, null);
}
break;
case DbType.Decimal:
Decimal dec;
if (Decimal.TryParse(xnv, out dec))
{
prop.SetValue(obj, dec, null);
}
break;
case DbType.Double:
double db;
if (Double.TryParse(xnv, out db))
{
prop.SetValue(obj, db, null);
}
break;
//Int32、Int16 3种类型处理方式一样
case DbType.Int32:
case DbType.Int16:
int i;
if (int.TryParse(xnv, out i))
{
prop.SetValue(obj, i, null);
}
break;
case DbType.UInt64:
long li;
if (long.TryParse(xnv, out li))
{
prop.SetValue(obj, li, null);
}
break;
case DbType.Boolean:
bool b;
if (Boolean.TryParse(xnv, out b))
{
prop.SetValue(obj, b, null);
}
break;
}
}
} //从prop里取出对应的结点名,1、如果有NodeAttribute特性,则取特性里的名称,2、否则默认取属性名,3、如果设置了变化规则,按规则变化
private string GetNodeNameFromProp(PropertyInfo prop)
{
string name = prop.Name;//默认为属性名
var cus = prop.GetCustomAttributes(typeof(NodeAttribute), true);
if (cus != null && cus.Length > 0)
{
string seflName = (cus[0] as NodeAttribute).Name;
if (seflName.IsNotNullOrEmpty()) name = seflName;//注释里的结点名
}
if (nodeFormatter != null)
{
name = nodeFormatter(name);
}
return name;
} /*从node的子结点或属性中解析数据到实体中*/
public T DeSerialize<T>(XmlNode node) where T : class
{
object obj = DeSerialize(typeof(T), node);
return obj as T;
} private object DeSerialize(Type type, XmlNode node)
{
if (node == null || type == null) return null;
object obj = Activator.CreateInstance(type);
var props = type.GetProperties();
if (props == null || props.Count() <= 0) return null;
//如果不需要处理没有被NodeAttr标记的属性,过滤出有NodeAttr的属性
if (!containPropertyWithOutNodeAttr)
{
props = props.Where(p =>
{
var nas = p.GetCustomAttributes(typeof(NodeAttribute), true);
return nas != null && nas.Count() > 0;
}).ToArray();
}
Entity ext = new Entity();
//先处理基础数据类型
var baseProps = props.Where(p => ext.IsNormalBaseType(p));
if (baseProps != null && baseProps.Count() > 0)
{
FillBaseType(node, obj, baseProps);
}
//处理自定义类类型
var classProps = props.Where(p => ext.IsUserDefinedClass(p));
if (classProps != null && classProps.Count() > 0)
{
foreach (var prop in classProps)
{
string name = GetNodeNameFromProp(prop);
XmlNode xn = node.SelectSingleNode(name);
if (xn == null) continue;
object v = DeSerialize(prop.PropertyType, xn);
if (v != null) prop.SetValue(obj, v);
}
}
return obj;
} /*
从对象属性中取出结点名称和结点值
step1、如果有NodeAttribute,按顺序取结点名
step2、如果没有NodeAttribute,取属性名作结点名
step3、如果设置了nodeFormatter,根据nodeFormatter更改结点名
*/
private List<KeyValueEntity> GetAttrValue(object data)
{
List<KeyValueEntity> list = null;
if (data != null)
{
Type type = data.GetType();
var props = new Entity().GetNormalBaseType(type.GetProperties());
list = new List<KeyValueEntity>();
//有注解的属性
var selfNodeProps = props.Where(p =>
{
var cus = p.GetCustomAttributes(typeof(NodeAttribute), true);
return cus != null && cus.Count() > 0;
}).OrderBy(p =>
{
NodeAttribute na = p.GetCustomAttributes(typeof(NodeAttribute), true)[0] as NodeAttribute;
int i = na.Index;
Console.WriteLine($"{na.Name},{na.Index}");
return i;
}).ToArray();
//没有注解的属性
var defaultNodeProps = props.Where(p =>
{
var cus = p.GetCustomAttributes(typeof(NodeAttribute), true);
return cus == null || cus.Count() <= 0;
});
List<PropertyInfo> pis = new List<PropertyInfo>();
//按注解里的先后顺序添加结点
if (selfNodeProps != null && selfNodeProps.Count() > 0)
{
pis.AddRange(selfNodeProps);
}
//再添加没有注解的结点
if (defaultNodeProps != null && defaultNodeProps.Count() > 0)
{
pis.AddRange(defaultNodeProps);
}
foreach (var prop in pis)
{
KeyValueEntity kv = new Entities.KeyValueEntity();
kv.Key = prop.Name;//默认没有注解,用属性名
var attrs = prop.GetCustomAttributes(typeof(NodeAttribute), true);
if (attrs != null && attrs.Count() > 0)
{
NodeAttribute attr = attrs[0] as NodeAttribute;
if (attr.Name.IsNotNullOrEmpty())//注解里有结点名
{
kv.Key = attr.Name;
}
}
if (nodeFormatter != null)//如果设置了更改规则,按规则修改
{
kv.Key = nodeFormatter(kv.Key);
} object v = prop.GetValue(data);
if (prop.PropertyType.ToString().Contains("DateTime"))
{
if (v != null)
{
if (timeFormat.IsNullOrEmpty()) timeFormat = "yyyyMMdd";
kv.Value = Convert.ToDateTime(v).ToString(timeFormat);
}
}
else if (v != null)
{
kv.Value = Convert.ToString(v);
}
list.Add(kv);
}
}
return list;
}
} /// <summary>
/// xml文档序列化,反序列化
/// TimeFormat:设置日期属性的格式
/// NodeNameFormatter:可以对结点名称格式化
/// 结点名:如果设置了NodeAttr的Name,按设置取,否则默认取属性名
/// CreateNodeWhenNull:设置属性值为null时是否生成结点
/// ContainPropertyWithOutNodeAttr:属性值没有被NodeAttr标记时,是否生成、解析结点
/// </summary>
public class XmlSerializer : XmlNodeSerializer
{
public new XmlDocument Serialize<T>(T data)
{
return Serialize(data, "UTF-8");
} public XmlDocument Serialize<T>(T data, string encoding)
{
if (data == null) return null;
XmlDocument doc = new XmlDocument();
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", encoding, null);
doc.AppendChild(dec);
XmlNode node = Serialize(data, doc);
if (node != null)
{
doc.AppendChild(node);
}
return doc;
} public T DeSerialize<T>(XmlDocument doc) where T : class
{
if (doc == null) return null;
Type type = typeof(T);
string nodeName = string.Empty; //结点名
NodeAttribute na = type.GetCustomAttribute<NodeAttribute>();
if (na == null || na.Name.IsNullOrEmpty())
{
nodeName = type.Name;//如果没有设置,使用类名
}
else
{
nodeName = na.Name;//取设置的结点名
}
if (nodeFormatter != null) nodeName = nodeFormatter(nodeName);
XmlNode node = doc.SelectSingleNode(nodeName);
if (node == null) return null;
return DeSerialize<T>(node); //doc.firstchild可能是XmlDeclaration
} public T DeSerialize<T>(string xml) where T : class
{
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return DeSerialize<T>(doc);
}
catch (XmlException ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
} public class NodeAttribute : Attribute
{
/// <summary>
/// 结点名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 结点在文档内的顺序
/// </summary>
public int Index { get; set; } = int.MaxValue;//默认最大值,因为有些node只设置了name没设置index,index就会默认为0就排到第一个去了
}

反射+特性实现 类和XML文档的序列化反序列化的更多相关文章

  1. C# 使用XmlDocument类对XML文档进行操作

    原创地址:http://www.cnblogs.com/jfzhu/archive/2012/11/19/2778098.html 转载请注明出处 W3C制定了XML DOM标准.很多编程语言中多提供 ...

  2. C#XmlHelper帮助类操作Xml文档的通用方法汇总

    前言 该篇文章主要总结的是自己平时工作中使用频率比较高的Xml文档操作的一些常用方法和收集网上写的比较好的一些通用Xml文档操作的方法(主要包括Xml序列化和反序列化,Xml文件读取,Xml文档节点内 ...

  3. 使用dom4j类操作xml文档

    dom4j操作xml数据 1.Document对象相关 ①读取XML文件,获得document对象. SAXReader reader = new SAXReader(); Document docu ...

  4. Java高级特性 第15节 解析XML文档(3) - JDOM和DOM4J技术

    一.JDOM解析 特征: 1.仅使用具体类,而不使用接口. 2.API大量使用了Collections类. Jdom由6个包构成: Element类表示XML文档的元素 org.jdom: 解析xml ...

  5. XPath操作XML文档

    NET框架下的Sytem.Xml.XPath命名空间提供了一系列的类,允许应用XPath数据模式查询和展示XML文档数据. 3.1XPath介绍 主要的目的是在xml1.0和1.1文档节点树种定位节点 ...

  6. 文档对象模型操作xml文档

    简介 :文档对象模型(DOM)是一种用于处理xml文档的API函数集. 2.1文档对象模型概述 按照W3C的定义,DOM是“一种允许程序或脚本动态地访问更新文档内容,结构和样式的.独立于平台和语言的规 ...

  7. 使用dynamic特性处理XML文档

    处理XML文档是我们经常需要进行的一项工作,尤其是在进行网络服务相关编程时,比如更新RSS等.在.NET 3.5中引入了Linq To XML,使得XML文档的读写已经大大简化,而.NET 4.0中最 ...

  8. C#XmlHelper操作Xml文档的帮助类

    using System.Xml; using System.Data; namespace DotNet.Utilities { /// <summary> /// Xml的操作公共类 ...

  9. [XML] C# XmlHelper操作Xml文档的帮助类 (转载)

    点击下载 XmlHelper.rar 主要功能如下所示 /// <summary> /// 类说明:XmlHelper /// 编 码 人:苏飞 /// 联系方式:361983679 // ...

  10. Java高级特性 第14节 解析XML文档(2) - SAX 技术

    一.SAX解析XML文档 SAX的全称是Simple APIs for XML,也即XML简单应用程序接口.与DOM不同,SAX提供的访问模式是一种顺序模式,这是一种快速读写XML数据的方式.当使用S ...

随机推荐

  1. C# 遇见System.Net.Http不兼容的解决方案

    背景 假设我有一个项目A,调用B项目里面的HttpClient.A里面的System.Net.Http引用路径为(版本4.0.0.0) C:\Program Files (x86)\Reference ...

  2. 零基础学习人工智能—Python—Pytorch学习(二)

    前言 数学的学习跟数学的计算是要分开的,现在回头再去看大学的高数和线性代数,如果只是学习的话,其实一门课程3天,也就学完了. 学校的课程之所以上那么久,其实是为了考试,也就是为计算准备的.计算有意义的 ...

  3. Python-目标检测-将xml文件转换成.txt文件

    代码说明:labels文件夹是工程下的一个文件夹,里面存放的是一些xml文件. 然后我们将这些xml文件中的内容取出来,放在路径path1的文件名下.这样也就完成了xml文件到txt文件的转化. 该代 ...

  4. 推荐一款开源一站式SQL审核查询平台!功能强大、安全可靠!

    1.前言 在当今这个数据驱动的时代,数据库作为企业核心信息资产的载体,其重要性不言而喻.随着企业业务规模的不断扩大,数据库的数量和种类也日益增多,这对数据库的管理与运维工作提出了前所未有的挑战.在这样 ...

  5. Ubuntu 设置 SMB 服务

    安装 Samba 包 sudo apt install samba samba-common 创建用于 SMB 共享的文件夹 sudo mkdir /usr/local/volumes # 新建用于共 ...

  6. Windows SSH 免密登陆远程计算机

    上传公钥 如果远程计算机是类 Unix 系统,使用下面这条命令: Get-Content $Env:USERPROFILE\.ssh\id_rsa.pub | ssh USER@HOST " ...

  7. php自定义函数访问其他地方定义的变量

    新捣鼓php,很多地方有.net的思维好难改过来. 在Connection/config.php 自定义了数据库连接字符串 然后在外部页面,自定义了一个function,对数据库进行操作 然后死活都给 ...

  8. 把dataframe 一列转成 array

    把dataframe 一列转成 array

  9. 基于Service Worker实现WebRTC局域网大文件传输能力

    基于Service Worker实现WebRTC局域网大文件传输能力 Service Worker是一种驻留在用户浏览器后台的脚本,能够拦截和处理网络请求,从而实现丰富的离线体验.缓存管理和网络效率优 ...

  10. ARC119F 题解

    前言 ARC119F 好厉害,是没见过的自动机 DP. 正文 [1] 分析 主要分析一下为什么这么写. [2] 状态设计 [3] 自动机状态转移 感觉状态设计中最难的就是如何处理带 \(O\) 的. ...