前言

最近由于项目需求,需要读写操作XML文件,并且存储的XML文件格式会随着导入的数据不同而随时改变(当然导入的数据还是有一定约束的),这样我们要预先定义好XML文件的格式就不太现实了,如何实现不管导入的数据如何变化,我都能正确的把数据解析出来,这就是要实现的动态的XML文件读写操作!如果大家有更好的方式欢迎交流!

具体实现

本文所实现的读写XML文件是使用序列话的方式,具体博文请参考:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html,当然如果只是序列化操作XML文件的话,只需要看这篇博文,也就不需要这篇文章了!

了解了如何序列化操作XML文件了话,你就会知道,要想写入和读取XML文件,我们就需要定义好一个数据类型(这一点很重要),但是问题就出现了,如果我们在编程时就定义好一个数据类型了,当导入的数据改变了,这个数据类型就不适合了,接下来就来解决这个问题

实现一:根据需求创建动态数据类型

废话先不多说,先上代码,代码说话,往往比语言来的直接也更容易懂

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Diagnostics; namespace XMLDemo
{
public class ReflectClass
{
//保存动态生成并编译的类的type对象
Type theType = null;
//保存动态生成类的实例
object theClass = null;
public Assembly DoOperation()
{
//未初始化
if (theType == null)
{
//初始化
return GenerateCode();
}
return null;
}
private Assembly GenerateCode()
{
//文件名
string fileName = "XmlClass";
////从编译好的dll文件load一个Assembly
//Assembly a = Assembly.LoadFrom(fileName + ".dll"); //从编译好的dll文件load一个Assembly
byte[] filedata = File.ReadAllBytes(fileName + ".dll");
Assembly a = Assembly.Load(filedata); //此方法在使用完dll文件后会自动释放资源
return a;
}
public Assembly DoOperation(List<ExcelData> list)
{
//未初始化
if (theType == null)
{
//初始化
return GenerateCode(list);
}
return null;
}
private Assembly GenerateCode(List<ExcelData> list)
{
//文件名
string fileName = "XmlClass";
//打开文件,如果不存在,则创建
Stream s = File.Open(fileName + ".cs", FileMode.Create);
//创建一个StreamWriter来写入数据
StreamWriter wrtr = new StreamWriter(s); #region 写入动态创建类的源代码
wrtr.WriteLine("// 动态创建的XmlClass类文件"); //类名,此类是为序列化读取XML文件创建的类文件
string className = "XmlClass";
wrtr.WriteLine("using System;");
wrtr.WriteLine("using System.Xml.Serialization;");
wrtr.WriteLine("public class {0}", className);
wrtr.WriteLine("{");
var ch = (from num in list select num.Mark).Distinct().ToList();
foreach (var n in ch)
{
wrtr.WriteLine("\tpublic " + n + " " + n);
wrtr.WriteLine("\t{");
wrtr.WriteLine("\t\tget;set;");
wrtr.WriteLine("\t}");
}
wrtr.WriteLine("}");
foreach (var n in ch)
{
wrtr.WriteLine("public class {0}", n);
wrtr.WriteLine("{");
var nlist = from num in list where (num.Mark == n) select num;
foreach (var m in nlist)
{
wrtr.WriteLine("\tpublic string " + m.Name);
wrtr.WriteLine("\t{");
wrtr.WriteLine("\t\tget;set;");
wrtr.WriteLine("\t}");
}
wrtr.WriteLine("}");
}
//关闭StreamWriter和文件
wrtr.Close();
s.Close();
#endregion //启动进程编译源文件
//指定参数
ProcessStartInfo psi = new ProcessStartInfo();
File.Delete(fileName + ".dll"); //重新导入数据时删除旧的DLL文件
//启动cmd.exe
psi.FileName = "cmd.exe";
//cmd.exe的参数,/c-close,完成后关闭;后为参数,指定cmd.exe使用csc来编译刚才生成的源文件
string compileString = "/c C:\\WINDOWS\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe /optimize+ /target:library {0}.cs";
psi.Arguments = String.Format(compileString, fileName);
//运行时的风格-最小化
psi.WindowStyle = ProcessWindowStyle.Minimized;
//启动进程
Process proc = Process.Start(psi);
//指定当前在此进程退出前等待
proc.WaitForExit();
//从编译好的dll文件load一个Assembly
byte[] filedata = File.ReadAllBytes(fileName + ".dll");
Assembly a = Assembly.Load(filedata); //此方法在使用完dll文件后会自动释放资源
//Assembly a = Assembly.LoadFrom(fileName + ".dll"); //这样使用dll文件会一直被占用,不会释放,造成重新生成前无法自动删除
//删除源文件
//File.Delete(fileName + ".cs");
return a;
}
}
}

以上 ReflectClass类帮助我们实现了通过导入的数据来创建一个供我们使用的编译好的类文件,下面主要讲如果通过这个使用这个编译好的dll文件来写入我们的XML文件

写入动态创建类的源代码那一块真是费了我不少脑细胞,感觉跟写模板类似的!(大家如果根据自己需求改写的话,应该就会体会到)

实现二:通过反射得到动态创建的数据类型并序列化写入XML文件

还是先上代码,XMLHelper帮助类可以去上面的连接去下载,也可以在我提供的Demo里下载,我只根据我的需要添加了一个方法,下面我的一个普通帮助类CommonHelper

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO; namespace XMLDemo
{
public class CommonHelper
{
/// <summary>
/// 写文件
/// </summary>
/// <param name="filePath">文件名</param>
/// <param name="Context">写的内容</param>
/// <returns>是否成功</returns>
public static bool FileWriter(string filePath, string Context)
{
try
{
//打开文件,如果不存在,则创建
Stream s = File.Open(filePath, FileMode.Create);
//创建一个StreamWriter来写入数据
StreamWriter wrtr = new StreamWriter(s);
//写入动态创建类的源代码
wrtr.WriteLine(Context);
wrtr.Close();
s.Close();
return true;
}
catch (Exception)
{ return false;
}
}
/// <summary>
/// 读文件
/// </summary>
/// <param name="filePath">文件名</param>
/// <returns>返回读的内容</returns>
public static string FileRead(string filePath)
{
try
{
//打开文件,如果不存在,则创建
Stream s1 = File.Open(filePath, FileMode.Open);
//创建一个StreamWriter来写入数据
StreamReader reader = new StreamReader(s1);
//写入动态创建类的源代码
string xml = reader.ReadToEnd();
reader.Close();
s1.Close();
return xml;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 根据数据创建类,并得到动态创建类的集合
/// </summary>
/// <param name="list">导入的数据集合</param>
/// <returns>返回类的集合</returns>
public static List<object> GetXmlClassInstances(List<ExcelData> list)
{
List<object> o = new List<object>();
ReflectClass t = new ReflectClass();
var assemblys = t.DoOperation(list);
Type[] types = assemblys.GetExportedTypes();
foreach (Type type in types)
{
o.Add(assemblys.CreateInstance(type.Name));
}
return o;
}
/// <summary>
/// 得到动态创建类的集合
/// </summary>
/// <returns></returns>
public static List<object> GetXmlClassInstances()
{
List<object> o = new List<object>();
ReflectClass t = new ReflectClass();
var assemblys = t.DoOperation();
Type[] types = assemblys.GetExportedTypes();
foreach (Type type in types)
{
o.Add(assemblys.CreateInstance(type.Name));
}
return o;
}
}
}

主要是一个GetXmlClassInstances(List<ExcelData> list):根据数据创建类,并得到动态创建类的集合

一个是GetXmlClassInstances():得到已经创建好的动态类的集合

下面是一个数据实体类,也就是我们所规范的数据格式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace XMLDemo
{
/// <summary>
/// 数据实体类
/// </summary>
public class ExcelData
{
/// <summary>
/// 根节点
/// </summary>
public string Mark { get; set; }
/// <summary>
/// 子节点
/// </summary>
public string Name { get; set; } /// <summary>
/// 子节点对应值
/// </summary>
public string Value { get; set; }
}
}

准备工作就绪后就可以进行XML文件的写入了,依然是废话不多说,上代码:

       private void btnWrite_Click(object sender, EventArgs e)
{
List<object> XmlClassInstances = CommonHelper.GetXmlClassInstances(listData);
if (InsertXmlClass(XmlClassInstances, listData) == false)
{
MessageBox.Show("数据导入失败");
return;
}
}
private bool InsertXmlClass(List<object> o, List<ExcelData> list)
{ for (int i = ; i < o.Count; i++)
{
//给类的属性赋值
var nlist = from num in list where (num.Mark == o[i].GetType().Name) select num;
foreach (var model in nlist)
{
PropertyInfo propinfo = o[i].GetType().GetProperty(model.Name);
propinfo.SetValue(o[i], model.Value, null);
}
}
var XmlClass = o[]; //默认第一个类为主体类,主体类的属性为其他类
var tt = XmlClass.GetType();
for (int i = ; i < o.Count; i++)
{
//给主体类的属性赋值
PropertyInfo propinfo = tt.GetProperty(o[i].GetType().Name);
propinfo.SetValue(XmlClass, o[i], null);
}
//序列化主体类,得到序列化字符串
string xml = XmlHelper.XmlSerialize(XmlClass, Encoding.UTF8);
string FilePath = "QMSPlan.XML";
if (CommonHelper.FileWriter(FilePath, xml) == false)
{
return false;
}
return true;
}

上面的代码可能有点绕,大家可以单步调试去理解,当时写的时候崩溃了几次,最后才调试好,不知道有什么更好的写法!

实现三:序列化读出XML文件

感觉用语言都表达不出来,还是直接上代码吧

       private void btnRead_Click(object sender, EventArgs e)
{
List<ExcelData> listRead = new List<ExcelData>();
var XmlClass = GetXmlClass();
if (XmlClass == null)
{
MessageBox.Show("数据读取失败");
return;
}
//获取所有主体类的属性(即所有的子类集合)
PropertyInfo[] ps = XmlClass.GetType().GetProperties();
foreach (var n in ps)
{
//得到子类
var m = n.GetValue(XmlClass, null);
//获取子类的所有属性
PropertyInfo[] x = m.GetType().GetProperties();
foreach (var y in x)
{
ExcelData model = new ExcelData();
model.Mark = m.GetType().Name;
model.Name = y.Name;
model.Value = y.GetValue(m, null).ToString();
listRead.Add(model);
}
}
}
private object GetXmlClass()
{
List<object> o = CommonHelper.GetXmlClassInstances();
var XmlClass = o[]; //默认第一个类为主体类 string FilePath = "QMSPlan.XML";
string xml = CommonHelper.FileRead(FilePath);
if (xml == null)
{
return null;
}
XmlClass = XmlHelper.XmlDeserialize(XmlClass.GetType(), xml, Encoding.UTF8);
return XmlClass;
}

上面大量运用了反射的特性,就是因为实现动态读取数据类型而造成的!

结束语

可能大家看完觉得完全没必要这样做,并且大量这样的操作会影响性能,不过这样做实实在在解决了我项目中的难点,实现了根据导入数据的不同,可以实现正确的XML的读写操作,

总结一下:以上总共有三个知识点的应用

1.动态创建数据类型并在程序中使用

2.序列话读写XML文件

3.利用反射特性实现

最后附上demo下载地址:http://files.cnblogs.com/beimeng/XML.demo.rar

实现动态的XML文件读写操作(依然带干货)的更多相关文章

  1. 实现动态的XML文件读写操作

    实现动态的XML文件读写操作(依然带干货) 前言 最近由于项目需求,需要读写操作XML文件,并且存储的XML文件格式会随着导入的数据不同而随时改变(当然导入的数据还是有一定约束的),这样我们要预先定义 ...

  2. ios 简单的plist文件读写操作(Document和NSUserDefaults)

    最近遇到ios上文件读写操作的有关知识,记录下来,以便以后查阅,同时分享与大家. 一,简单介绍一下常用的plist文件. 全名是:Property List,属性列表文件,它是一种用来存储串行化后的对 ...

  3. QXmlStreamReader/QXmlStreamWriter实现Qt下xml文件读写

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QXmlStreamReader/QXmlStreamWriter实现Qt下xml文件读写   ...

  4. c语言文件读写操作总结

    C语言文件读写操作总结 C语言文件操作 一.标准文件的读写 1.文件的打开 fopen() 文件的打开操作表示将给用户指定的文件在内存分配一个FILE结构区,并将该结构的指针返回给用户程序,以后用户程 ...

  5. [转]Android - 文件读写操作 总结

     转自:http://blog.csdn.net/ztp800201/article/details/7322110 Android - 文件读写操作 总结 分类: Android2012-03-05 ...

  6. PHP文件读写操作之文件写入代码

    在PHP网站开发中,存储数据通常有两种方式,一种以文本文件方式存储,比如txt文件,一种是以数据库方式存储,比如Mysql,相对于数据库存储,文件存储并没有什么优势,但是文件读写操作在基本的PHP开发 ...

  7. 更新java对xml文件的操作

    //更新java在xml文件中操作的内容 public static void upda(Document doc) throws Exception{ //创建一个TransformerFactor ...

  8. Java 字节流实现文件读写操作(InputStream-OutputStream)

    Java 字节流实现文件读写操作(InputStream-OutputStream) 备注:字节流比字符流底层,但是效率底下. 字符流地址:http://pengyan5945.iteye.com/b ...

  9. Java 字符流实现文件读写操作(FileReader-FileWriter)

    Java 字符流实现文件读写操作(FileReader-FileWriter) 备注:字符流效率高,但是没有字节流底层 字节流地址:http://pengyan5945.iteye.com/blog/ ...

随机推荐

  1. java:transient是什么,有什么作用

    参考博客:Java transient关键字 “Java的serialization提供了一种持久化对象实例的机制.当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机 ...

  2. CF Gym101933K King's Colors

    题目分析 题目要求在树上涂上恰好\(K\)种颜色的方案数. 设\(f(k)\)表示恰好涂上\(k\)种颜色的方案数(答案即为\(f(K)\)). 设\(g(k)\)表示至多涂上\(k\)种颜色的方案数 ...

  3. eclipse去掉xml验证的方法

    eclipse Multiple annotations found at this line错误,eclipse开发过程中,一些XML配置文件会报错,但是这些其实不是错,飘红的原因是因为eclips ...

  4. ADF中VO的删除操作初探

    在ADF的VO中,真实提交更改是在commit 方法执行之后,如以下增加操作 EntityDefImpl departmentEODef = DepartmentEOImpl. getDefiniti ...

  5. UVa 10817 - Headmaster's Headache(状压DP)

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  6. PHP时间戳和日期相互转换(转载)

    在php中我们要把时间戳转换日期可以直接使用date函数来实现,如果要把日期转换成时间戳可以使用strtotime()函数实现,下面我来给大家举例说明. 1.php中时间转换函数 strtotime ...

  7. 怎么解决深入学习PHP的瓶颈?

    PHP给学习者的感觉是:初学的时候很容易,但是学了2-3年,就深刻感觉遇到了瓶颈,很难深入,放弃又可惜.所谓“鸡肋,食之无味弃之可惜”的感觉很是贴切. 经常会有这种感觉:不学,看似也不后退:学了,好像 ...

  8. ASP.NET Web API编程——文件上传

    首先分别介绍正确的做法和错误的做法,然后分析他们的不同和错误之处,以便读者在实现此功能时可避开误区 1正确的做法 public class AvaterController : BaseApiCont ...

  9. Linux sed命令用法

    概述 sed命令是一个面向字符流的非交互式编辑器,不允许用户与它进行交互操作.sed是以行为单位处理文本内容的.在shell中,可以批量修改文本内容. 用法 sed [选项] [动作] 选项与参数:- ...

  10. JavaScript脚本加载相关知识

    <script>标签的位置 HTML4规范允许<script>可以放在<head>或<body>中. 但是,放在<head>中会导致性能问题 ...